Skip to content

Add a function to merge trivia from a node into an existing trivia #1478

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxVisitor.swift"], syntaxVisitorFile),
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["TokenKind.swift"], tokenKindFile),
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Tokens.swift"], tokensFile),
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Trivia.swift"], triviaFile),
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["TriviaPieces.swift"], triviaPiecesFile),

// SwiftSyntaxBuilder
GeneratedFileSpec(swiftSyntaxBuilderGeneratedDir + ["BuildableCollectionNodes.swift"], buildableCollectionNodesFile),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,7 @@ import SwiftSyntaxBuilder
import SyntaxSupport
import Utils

let triviaFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
public enum TriviaPosition {
case leading
case trailing
}
"""
)

let triviaPiecesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
try! EnumDeclSyntax(
"""
/// A contiguous stretch of a single kind of trivia. The constituent part of
Expand Down Expand Up @@ -115,88 +106,11 @@ let triviaFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
}
}

DeclSyntax(
"""
extension TriviaPiece {
/// Returns true if the trivia is `.newlines`, `.carriageReturns` or `.carriageReturnLineFeeds`
public var isNewline: Bool {
switch self {
case .newlines,
.carriageReturns,
.carriageReturnLineFeeds:
return true
default:
return false
}
}
}
"""
)

try! StructDeclSyntax(
try! ExtensionDeclSyntax(
"""
/// A collection of leading or trailing trivia. This is the main data structure
/// for thinking about trivia.
public struct Trivia
extension Trivia
"""
) {
DeclSyntax("public let pieces: [TriviaPiece]")

DeclSyntax(
"""
/// Creates Trivia with the provided underlying pieces.
public init<S: Sequence>(pieces: S) where S.Element == TriviaPiece {
self.pieces = Array(pieces)
}
"""
)

DeclSyntax(
"""
/// Creates Trivia with no pieces.
public static var zero: Trivia {
return Trivia(pieces: [])
}
"""
)

DeclSyntax(
"""
/// Whether the Trivia contains no pieces.
public var isEmpty: Bool {
pieces.isEmpty
}
"""
)

DeclSyntax(
"""
/// Creates a new `Trivia` by appending the provided `TriviaPiece` to the end.
public func appending(_ piece: TriviaPiece) -> Trivia {
var copy = pieces
copy.append(piece)
return Trivia(pieces: copy)
}
"""
)

DeclSyntax(
"""
public var sourceLength: SourceLength {
return pieces.map({ $0.sourceLength }).reduce(.zero, +)
}
"""
)

DeclSyntax(
"""
/// Get the byteSize of this trivia
public var byteSize: Int {
return sourceLength.utf8Length
}
"""
)

for trivia in TRIVIAS {
if trivia.isCollection {
let joined = trivia.characters.map { "\($0)" }.joined()
Expand Down Expand Up @@ -231,99 +145,6 @@ let triviaFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
}
}

DeclSyntax(
#"""
extension Trivia: CustomDebugStringConvertible {
public var debugDescription: String {
if count == 1, let first = first {
return first.debugDescription
}
return "[" + map(\.debugDescription).joined(separator: ", ") + "]"
}
}
"""#
)

DeclSyntax("extension Trivia: Equatable {}")

DeclSyntax(
"""
/// Conformance for Trivia to the Collection protocol.
extension Trivia: Collection {
public var startIndex: Int {
return pieces.startIndex
}

public var endIndex: Int {
return pieces.endIndex
}

public func index(after i: Int) -> Int {
return pieces.index(after: i)
}

public subscript(_ index: Int) -> TriviaPiece {
return pieces[index]
}
}
"""
)

DeclSyntax(
"""
extension Trivia: ExpressibleByArrayLiteral {
/// Creates Trivia from the provided pieces.
public init(arrayLiteral elements: TriviaPiece...) {
self.pieces = elements
}
}
"""
)

DeclSyntax(
"""
extension Trivia: TextOutputStreamable {
/// Prints the provided trivia as they would be written in a source file.
///
/// - Parameter stream: The stream to which to print the trivia.
public func write<Target>(to target: inout Target)
where Target: TextOutputStream {
for piece in pieces {
piece.write(to: &target)
}
}
}
"""
)

DeclSyntax(
"""
extension Trivia: CustomStringConvertible {
public var description: String {
var description = ""
self.write(to: &description)
return description
}
}
"""
)

DeclSyntax(
"""
extension Trivia {
/// Concatenates two collections of `Trivia` into one collection.
public static func +(lhs: Trivia, rhs: Trivia) -> Trivia {
return Trivia(pieces: lhs.pieces + rhs.pieces)
}

/// Concatenates two collections of `Trivia` into the left-hand side.
public static func +=(lhs: inout Trivia, rhs: Trivia) {
lhs = lhs + rhs
}
}
"""
)

DeclSyntax("extension TriviaPiece: Equatable {}")

try! ExtensionDeclSyntax("extension TriviaPiece") {
Expand Down Expand Up @@ -388,26 +209,6 @@ let triviaFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
}
}

DeclSyntax(
"""
extension RawTriviaPiece: TextOutputStreamable {
public func write<Target: TextOutputStream>(to target: inout Target) {
TriviaPiece(raw: self).write(to: &target)
}
}
"""
)

DeclSyntax(
"""
extension RawTriviaPiece: CustomDebugStringConvertible {
public var debugDescription: String {
TriviaPiece(raw: self).debugDescription
}
}
"""
)

try! ExtensionDeclSyntax("extension TriviaPiece") {
try InitializerDeclSyntax("@_spi(RawSyntax) public init(raw: RawTriviaPiece)") {
try SwitchExprSyntax("switch raw") {
Expand Down Expand Up @@ -463,22 +264,4 @@ let triviaFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
}
}
}

DeclSyntax(
"""
extension RawTriviaPiece {
/// Returns true if the trivia is `.newlines`, `.carriageReturns` or `.carriageReturnLineFeeds`
public var isNewline: Bool {
switch self {
case .newlines,
.carriageReturns,
.carriageReturnLineFeeds:
return true
default:
return false
}
}
}
"""
)
}
47 changes: 2 additions & 45 deletions Sources/SwiftParserDiagnostics/DiagnosticExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ extension FixIt.Changes {
/// where it makes sense and refusing to add e.g. a space after punctuation,
/// where it usually doesn't make sense.
static func transferTriviaAtSides<SyntaxType: SyntaxProtocol>(from nodes: [SyntaxType]) -> Self {
let removedTriviaAtSides = Trivia.merged(nodes.first?.leadingTrivia ?? [], nodes.last?.trailingTrivia ?? [])
let removedTriviaAtSides = (nodes.first?.leadingTrivia ?? []).merging(nodes.last?.trailingTrivia ?? [])
if !removedTriviaAtSides.isEmpty, let previousToken = nodes.first?.previousToken(viewMode: .sourceAccurate) {
let mergedTrivia = Trivia.merged(previousToken.trailingTrivia, removedTriviaAtSides)
let mergedTrivia = previousToken.trailingTrivia.merging(removedTriviaAtSides)
if previousToken.tokenKind.isPunctuation, mergedTrivia.allSatisfy({ $0.isSpaceOrTab }) {
// Punctuation is generally not followed by spaces in Swift.
// If this action would only add spaces to the punctuation, drop it.
Expand All @@ -158,49 +158,6 @@ extension FixIt.Changes {
}
}

extension Trivia {
/// Decomposes the trivia into pieces that all have count 1
var decomposed: Trivia {
let pieces = self.flatMap({ (piece: TriviaPiece) -> [TriviaPiece] in
switch piece {
case .spaces(let count):
return Array(repeating: TriviaPiece.spaces(1), count: count)
case .tabs(let count):
return Array(repeating: TriviaPiece.tabs(1), count: count)
case .verticalTabs(let count):
return Array(repeating: TriviaPiece.verticalTabs(1), count: count)
case .formfeeds(let count):
return Array(repeating: TriviaPiece.formfeeds(1), count: count)
case .newlines(let count):
return Array(repeating: TriviaPiece.newlines(1), count: count)
case .backslashes(let count):
return Array(repeating: TriviaPiece.backslashes(1), count: count)
case .pounds(let count):
return Array(repeating: TriviaPiece.pounds(1), count: count)
case .carriageReturns(let count):
return Array(repeating: TriviaPiece.carriageReturns(1), count: count)
case .carriageReturnLineFeeds(let count):
return Array(repeating: TriviaPiece.carriageReturnLineFeeds(1), count: count)
case .lineComment, .blockComment, .docLineComment, .docBlockComment, .unexpectedText, .shebang:
return [piece]
}
})
return Trivia(pieces: pieces)
}

/// Concatenate `lhs` and `rhs`, merging an infix that is shared between both trivia pieces.
static func merged(_ lhs: Trivia, _ rhs: Trivia) -> Self {
let lhs = lhs.decomposed
let rhs = rhs.decomposed
for infixLength in (0...Swift.min(lhs.count, rhs.count)).reversed() {
if lhs.suffix(infixLength) == rhs.suffix(infixLength) {
return lhs + Trivia(pieces: Array(rhs.dropFirst(infixLength)))
}
}
return lhs + rhs
}
}

extension TriviaPiece {
var isSpaceOrTab: Bool {
switch self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//

import SwiftDiagnostics
import SwiftSyntax
@_spi(RawSyntax) import SwiftSyntax

/// A diagnostic that `MultiLineStringLiteralIndentatinDiagnosticsGenerator` is building.
/// As indentation errors are found on more lines, this diagnostic is modified
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftSyntax/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_swift_host_library(SwiftSyntax
BumpPtrAllocator.swift
CommonAncestor.swift
IncrementalParseTransition.swift
Trivia.swift
SourceLength.swift
SourceLocation.swift
SourcePresence.swift
Expand Down Expand Up @@ -46,7 +47,7 @@ add_swift_host_library(SwiftSyntax
generated/SyntaxVisitor.swift
generated/TokenKind.swift
generated/Tokens.swift
generated/Trivia.swift
generated/TriviaPieces.swift

generated/syntaxNodes/SyntaxDeclNodes.swift
generated/syntaxNodes/SyntaxExprNodes.swift
Expand Down
Loading