Skip to content

Commit bfe4a25

Browse files
committed
Extract Indenter to its own file
1 parent abf9298 commit bfe4a25

File tree

2 files changed

+96
-84
lines changed

2 files changed

+96
-84
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//
2+
// Indenter.swift
3+
// swift-syntax
4+
//
5+
// Created by Jed Fox on 9/8/24.
6+
//
7+
8+
#if swift(>=6)
9+
public import SwiftSyntax
10+
#else
11+
import SwiftSyntax
12+
#endif
13+
14+
extension SyntaxProtocol {
15+
/// Indent this node’s lines by the provided amount.
16+
///
17+
/// - Parameter includeFirstLine: Whether the first token of this node should be indented.
18+
/// Pass `true` if you know that this node will be placed at the beginning of a line, even if its
19+
/// current leading trivia does not start with a newline (such as at the very start of a file).
20+
public func indented(by indentation: Trivia, indentFirstLine: Bool = false) -> Syntax {
21+
Indenter(indentation: indentation, indentFirstLine: indentFirstLine)
22+
.rewrite(self)
23+
}
24+
}
25+
26+
private class Indenter: SyntaxRewriter {
27+
private let indentation: Trivia
28+
private var shouldIndent: Bool
29+
30+
init(indentation: Trivia, indentFirstLine: Bool) {
31+
self.indentation = indentation
32+
self.shouldIndent = indentFirstLine
33+
}
34+
35+
private func indentIfNeeded() -> [TriviaPiece] {
36+
if shouldIndent {
37+
shouldIndent = false
38+
return indentation.pieces
39+
} else {
40+
return []
41+
}
42+
}
43+
44+
private func indentAfterNewlines(_ content: String) -> String {
45+
content.split(separator: "\n").joined(separator: "\n" + indentation.description)
46+
}
47+
48+
private func indent(_ trivia: Trivia, skipEmpty: Bool) -> Trivia {
49+
if skipEmpty, trivia.isEmpty { return trivia }
50+
51+
var result: [TriviaPiece] = []
52+
// most times, we won’t have anything to insert so this will
53+
// reserve enough space
54+
result.reserveCapacity(trivia.count)
55+
56+
for piece in trivia.pieces {
57+
result.append(contentsOf: indentIfNeeded())
58+
switch piece {
59+
case .newlines, .carriageReturns, .carriageReturnLineFeeds:
60+
shouldIndent = true
61+
// style decision: don’t indent totally blank lines
62+
result.append(piece)
63+
case .blockComment(let content):
64+
result.append(.blockComment(indentAfterNewlines(content)))
65+
case .docBlockComment(let content):
66+
result.append(.docBlockComment(indentAfterNewlines(content)))
67+
case .unexpectedText(let content):
68+
result.append(.unexpectedText(indentAfterNewlines(content)))
69+
default:
70+
result.append(piece)
71+
}
72+
}
73+
result.append(contentsOf: indentIfNeeded())
74+
return Trivia(pieces: result)
75+
}
76+
77+
override func visit(_ token: TokenSyntax) -> TokenSyntax {
78+
let indentedLeadingTrivia = indent(token.leadingTrivia, skipEmpty: false)
79+
80+
// compute this before indenting the trailing trivia since the
81+
// newline here is before the start of the trailing trivia (since
82+
// it is part of the string’s value)
83+
if case .stringSegment(let content) = token.tokenKind,
84+
let last = content.last,
85+
last.isNewline {
86+
shouldIndent = true
87+
}
88+
89+
return token
90+
.with(\.leadingTrivia, indentedLeadingTrivia)
91+
// source files as parsed can’t have anything requiring indentation
92+
// here, but it’s easy to do `.with(\.trailingTrivia, .newline)` so
93+
// we should still check if there’s something to indent.
94+
.with(\.trailingTrivia, indent(token.trailingTrivia, skipEmpty: true))
95+
}
96+
}

Sources/SwiftBasicFormat/Syntax+Extensions.swift

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -42,87 +42,3 @@ extension TokenSyntax {
4242
return []
4343
}
4444
}
45-
46-
extension SyntaxProtocol {
47-
/// Indent this node’s lines by the provided amount.
48-
///
49-
/// - Parameter includeFirstLine: Whether the first token of this node should be indented.
50-
/// Pass `true` if you know that this node will be placed at the beginning of a line, even if its
51-
/// current leading trivia does not start with a newline (such as at the very start of a file).
52-
public func indented(by indentation: Trivia, indentFirstLine: Bool = false) -> Syntax {
53-
Indenter(indentation: indentation, indentFirstLine: indentFirstLine)
54-
.rewrite(self)
55-
}
56-
}
57-
58-
private class Indenter: SyntaxRewriter {
59-
private let indentation: Trivia
60-
private var shouldIndent: Bool
61-
62-
init(indentation: Trivia, indentFirstLine: Bool) {
63-
self.indentation = indentation
64-
self.shouldIndent = indentFirstLine
65-
}
66-
67-
private func indentIfNeeded() -> [TriviaPiece] {
68-
if shouldIndent {
69-
shouldIndent = false
70-
return indentation.pieces
71-
} else {
72-
return []
73-
}
74-
}
75-
76-
private func indentAfterNewlines(_ content: String) -> String {
77-
content.split(separator: "\n").joined(separator: "\n" + indentation.description)
78-
}
79-
80-
private func indent(_ trivia: Trivia, skipEmpty: Bool) -> Trivia {
81-
if skipEmpty, trivia.isEmpty { return trivia }
82-
83-
var result: [TriviaPiece] = []
84-
// most times, we won’t have anything to insert so this will
85-
// reserve enough space
86-
result.reserveCapacity(trivia.count)
87-
88-
for piece in trivia.pieces {
89-
result.append(contentsOf: indentIfNeeded())
90-
switch piece {
91-
case .newlines, .carriageReturns, .carriageReturnLineFeeds:
92-
shouldIndent = true
93-
// style decision: don’t indent totally blank lines
94-
result.append(piece)
95-
case .blockComment(let content):
96-
result.append(.blockComment(indentAfterNewlines(content)))
97-
case .docBlockComment(let content):
98-
result.append(.docBlockComment(indentAfterNewlines(content)))
99-
case .unexpectedText(let content):
100-
result.append(.unexpectedText(indentAfterNewlines(content)))
101-
default:
102-
result.append(piece)
103-
}
104-
}
105-
result.append(contentsOf: indentIfNeeded())
106-
return Trivia(pieces: result)
107-
}
108-
109-
override func visit(_ token: TokenSyntax) -> TokenSyntax {
110-
let indentedLeadingTrivia = indent(token.leadingTrivia, skipEmpty: false)
111-
112-
// compute this before indenting the trailing trivia since the
113-
// newline here is before the start of the trailing trivia (since
114-
// it is part of the string’s value)
115-
if case .stringSegment(let content) = token.tokenKind,
116-
let last = content.last,
117-
last.isNewline {
118-
shouldIndent = true
119-
}
120-
121-
return token
122-
.with(\.leadingTrivia, indentedLeadingTrivia)
123-
// source files as parsed can’t have anything requiring indentation
124-
// here, but it’s easy to do `.with(\.trailingTrivia, .newline)` so
125-
// we should still check if there’s something to indent.
126-
.with(\.trailingTrivia, indent(token.trailingTrivia, skipEmpty: true))
127-
}
128-
}

0 commit comments

Comments
 (0)