Skip to content

Commit ed8f946

Browse files
committed
Migrate the TokenStreamCreator to the "all" View Mode
The idea being that the token stream cares about seeing the entire structure of the document, missing nodes and all, because it can try to format around the missing parts. Additionally, the structure of the missing nodes provides a place to hang opening and closing breaks in the formatting stream for now. A lot of the tests were relying on the C++ parser skipping large regions of syntax with unknown syntax nodes - which the stream would render back verbatim. The printing parts have been updated so they do not print missing tokens. Note that if the formatter wishes to heal invalid syntax, it can do so by simply printing missing keyword-like tokens into the stream, and replacing missing identifier-like tokens with code completion placeholders.
1 parent e1be590 commit ed8f946

File tree

1 file changed

+26
-16
lines changed

1 file changed

+26
-16
lines changed

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
6262
self.config = configuration
6363
self.operatorContext = operatorContext
6464
self.maxlinelength = config.lineLength
65-
super.init(viewMode: .sourceAccurate)
65+
super.init(viewMode: .all)
6666
}
6767

6868
func makeStream(from node: Syntax) -> [Token] {
@@ -328,7 +328,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
328328
// Due to visitation order, the matching .open break is added in ParameterClauseSyntax.
329329
after(node.signature.lastToken, tokens: .close)
330330
}
331-
331+
332332
arrangeParameterClause(node.signature.input, forcesBreakBeforeRightParen: node.body != nil)
333333

334334
// Prioritize keeping "<modifiers> init<punctuation>" together.
@@ -525,7 +525,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
525525

526526
// Breaks are only allowed after `else` when there's a comment; otherwise there shouldn't be
527527
// any newlines between `else` and the open brace or a following `if`.
528-
if let tokenAfterElse = elseKeyword.nextToken, tokenAfterElse.leadingTrivia.hasLineComment {
528+
if let tokenAfterElse = elseKeyword.nextToken(viewMode: .all), tokenAfterElse.leadingTrivia.hasLineComment {
529529
after(node.elseKeyword, tokens: .break(.same, size: 1))
530530
} else if let elseBody = node.elseBody, elseBody.is(IfStmtSyntax.self) {
531531
after(node.elseKeyword, tokens: .space)
@@ -793,7 +793,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
793793
// to exist at the EOL with the left paren or on its own line. The contents are always
794794
// indented on the following lines, since parens always create a scope. An open/close break
795795
// pair isn't used here to avoid forcing the closing paren down onto a new line.
796-
if node.leftParen.nextToken?.leadingTrivia.hasLineComment ?? false {
796+
if node.leftParen.nextToken(viewMode: .all)?.leadingTrivia.hasLineComment ?? false {
797797
after(node.leftParen, tokens: .break(.continue, size: 0))
798798
}
799799
} else if elementCount > 1 {
@@ -951,8 +951,8 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
951951
// When this function call is wrapped by a try-expr or await-expr, the group applied when
952952
// visiting that wrapping expression is sufficient. Adding another group here in that case
953953
// can result in unnecessarily breaking after the try/await keyword.
954-
if !(base.firstToken?.previousToken?.parent?.is(TryExprSyntax.self) ?? false
955-
|| base.firstToken?.previousToken?.parent?.is(AwaitExprSyntax.self) ?? false) {
954+
if !(base.firstToken?.previousToken(viewMode: .all)?.parent?.is(TryExprSyntax.self) ?? false
955+
|| base.firstToken?.previousToken(viewMode: .all)?.parent?.is(AwaitExprSyntax.self) ?? false) {
956956
before(base.firstToken, tokens: .open)
957957
after(calledMemberAccessExpr.name.lastToken, tokens: .close)
958958
}
@@ -1304,7 +1304,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
13041304
if let lastElemTok = node.elements.lastToken {
13051305
after(lastElemTok, tokens: .break(breakKindClose, newlines: .soft), .close)
13061306
} else {
1307-
before(tokenToOpenWith.nextToken, tokens: .break(breakKindClose, newlines: .soft), .close)
1307+
before(tokenToOpenWith.nextToken(viewMode: .all), tokens: .break(breakKindClose, newlines: .soft), .close)
13081308
}
13091309

13101310
if isNestedInPostfixIfConfig(node: Syntax(node)) {
@@ -1915,7 +1915,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
19151915
var closesNeeded: Int = 0
19161916
var closeAfterToken: TokenSyntax? = nil
19171917

1918-
if let typeAnnotation = node.typeAnnotation {
1918+
if let typeAnnotation = node.typeAnnotation, !typeAnnotation.type.is(MissingTypeSyntax.self) {
19191919
after(
19201920
typeAnnotation.colon,
19211921
tokens: .break(.open(kind: .continuation), newlines: .elective(ignoresDiscretionary: true)))
@@ -2190,6 +2190,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
21902190
}
21912191

21922192
override func visit(_ node: GenericWhereClauseSyntax) -> SyntaxVisitorContinueKind {
2193+
guard node.whereKeyword != node.lastToken else {
2194+
verbatimToken(Syntax(node))
2195+
return .skipChildren
2196+
}
2197+
21932198
after(node.whereKeyword, tokens: .break(.open))
21942199
after(node.lastToken, tokens: .break(.close, size: 0))
21952200

@@ -2378,6 +2383,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
23782383

23792384
// MARK: - Nodes representing unknown or malformed syntax
23802385

2386+
override func visit(_ node: UnexpectedNodesSyntax) -> SyntaxVisitorContinueKind {
2387+
verbatimToken(Syntax(node))
2388+
return .skipChildren
2389+
}
2390+
23812391
override func visit(_ node: UnknownDeclSyntax) -> SyntaxVisitorContinueKind {
23822392
verbatimToken(Syntax(node))
23832393
return .skipChildren
@@ -2429,7 +2439,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
24292439
appendMultilineStringSegments(at: pendingSegmentIndex)
24302440
} else if !ignoredTokens.contains(token) {
24312441
// Otherwise, it's just a regular token, so add the text as-is.
2432-
appendToken(.syntax(token.text))
2442+
appendToken(.syntax(token.presence == .present ? token.text : ""))
24332443
}
24342444

24352445
appendTrailingTrivia(token)
@@ -2883,7 +2893,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
28832893
}
28842894

28852895
private func extractLeadingTrivia(_ token: TokenSyntax) {
2886-
var isStartOfFile = token.previousToken == nil
2896+
var isStartOfFile = token.previousToken(viewMode: .all) == nil
28872897
let trivia = token.leadingTrivia
28882898

28892899
// If we're at the end of the file, determine at which index to stop checking trivia pieces to
@@ -3260,12 +3270,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
32603270
/// alongside the last token of the given node. Any tokens between `node.lastToken` and the
32613271
/// returned node's `lastToken` are delimiter tokens that shouldn't be preceded by a break.
32623272
private func outermostEnclosingNode(from node: Syntax) -> Syntax? {
3263-
guard let afterToken = node.lastToken?.nextToken, closingDelimiterTokens.contains(afterToken)
3273+
guard let afterToken = node.lastToken?.nextToken(viewMode: .all), closingDelimiterTokens.contains(afterToken)
32643274
else {
32653275
return nil
32663276
}
32673277
var parenthesizedExpr = afterToken.parent
3268-
while let nextToken = parenthesizedExpr?.lastToken?.nextToken,
3278+
while let nextToken = parenthesizedExpr?.lastToken?.nextToken(viewMode: .all),
32693279
closingDelimiterTokens.contains(nextToken),
32703280
let nextExpr = nextToken.parent
32713281
{
@@ -3355,9 +3365,9 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
33553365
// followed by a dot (for example, in an implicit member reference)---removing the spaces in
33563366
// those situations would cause the parser to greedily treat the combined sequence of
33573367
// operator characters as a single operator.
3358-
if case .postfixOperator? = token.previousToken?.tokenKind { return true }
3368+
if case .postfixOperator? = token.previousToken(viewMode: .all)?.tokenKind { return true }
33593369

3360-
switch token.nextToken?.tokenKind {
3370+
switch token.nextToken(viewMode: .all)?.tokenKind {
33613371
case .prefixOperator?, .prefixPeriod?: return true
33623372
default: return false
33633373
}
@@ -3389,7 +3399,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
33893399
// The leading trivia of the next token, after the ignored node, may contain content that
33903400
// belongs with the ignored node. The trivia extraction that is performed for `lastToken` later
33913401
// excludes that content so it needs to be extracted and added to the token stream here.
3392-
if let next = node.lastToken?.nextToken, let trivia = next.leadingTrivia.first {
3402+
if let next = node.lastToken?.nextToken(viewMode: .all), let trivia = next.leadingTrivia.first {
33933403
switch trivia {
33943404
case .lineComment, .blockComment:
33953405
trivia.write(to: &nodeText)
@@ -3581,7 +3591,7 @@ class CommentMovingRewriter: SyntaxRewriter {
35813591
override func visit(_ node: SequenceExprSyntax) -> ExprSyntax {
35823592
for element in node.elements {
35833593
if let binaryOperatorExpr = element.as(BinaryOperatorExprSyntax.self),
3584-
let followingToken = binaryOperatorExpr.operatorToken.nextToken,
3594+
let followingToken = binaryOperatorExpr.operatorToken.nextToken(viewMode: .all),
35853595
followingToken.leadingTrivia.hasLineComment
35863596
{
35873597
// Rewrite the trivia so that the comment is in the operator token's leading trivia.

0 commit comments

Comments
 (0)