Skip to content

Commit 2223e4c

Browse files
authored
Merge pull request #2272 from divalue/syntax_tree_with_lookahead_struct
Add IncrementalParseResult
2 parents 3fd8f49 + 45e92e8 commit 2223e4c

File tree

5 files changed

+101
-19
lines changed

5 files changed

+101
-19
lines changed

Release Notes/511.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010
- Description: `is`, `as`, and `cast` methods for types not contained in the choice node are marked as deprecated. The deprecated methods will emit a warning, indicating that the cast will always fail.
1111
- Issue: https://github.com/apple/swift-syntax/issues/2092
1212
- Pull Request: https://github.com/apple/swift-syntax/pull/2184
13-
13+
14+
- `IncrementalParseTransition`:
15+
- Description: The initializer `IncrementalParseTransition.init(previousTree:edits:lookaheadRanges:reusedNodeCallback:)` is marked as deprecated. Use `IncrementalParseTransition.init(previousIncrementalParseResult:edits:reusedNodeCallback:)` instead.
16+
- Issue: https://github.com/apple/swift-syntax/issues/2267
17+
- Pull request: https://github.com/apple/swift-syntax/pull/2272
18+
1419
## API-Incompatible Changes
1520

1621
- Effect specifiers:
@@ -23,6 +28,10 @@
2328
- Description: `IntegerLiteralExprSyntax.Radix` no longer conforms to `CaseIterable` since there is no good use case to iterate over all radix kinds.
2429
- Pull request: https://github.com/apple/swift-syntax/pull/2292
2530

31+
- `Parser.parseIncrementally(source:parseTransition:)` and `Parser.parseIncrementally(source:maximumNestingLevel:parseTransition:)`:
32+
- Description: The default versions of `Parser.parseIncrementally` return a `IncrementalParseResult` instead of a tuple. Access to the struct should be compatible with the tuple in almost all cases unless the tuple is stored into a variable and then destructed or passed to a function that expects a tuple.
33+
- Issue: https://github.com/apple/swift-syntax/issues/2267
34+
- Pull request: https://github.com/apple/swift-syntax/pull/2272
2635

2736
## Template
2837

Sources/SwiftParser/IncrementalParseTransition.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,39 @@ public typealias ReusedNodeCallback = (_ node: Syntax) -> ()
4848
/// Keeps track of a previously parsed syntax tree and the source edits that
4949
/// occurred since it was created.
5050
public final class IncrementalParseTransition {
51-
fileprivate let previousTree: SourceFileSyntax
51+
fileprivate let previousIncrementalParseResult: IncrementalParseResult
5252
fileprivate let edits: ConcurrentEdits
53-
fileprivate let lookaheadRanges: LookaheadRanges
5453
fileprivate let reusedNodeCallback: ReusedNodeCallback?
5554

5655
/// - Parameters:
5756
/// - previousTree: The previous tree to do lookups on.
5857
/// - edits: The edits that have occurred since the last parse that resulted
5958
/// in the new source that is about to be parsed.
6059
/// - reusedNodeCallback: Optional closure to accept information about the re-used node. For each node that gets re-used `reusedNodeCallback` is called.
60+
@available(*, deprecated, message: "Use initializer taking `IncrementalParseResult` instead")
6161
public init(
6262
previousTree: SourceFileSyntax,
6363
edits: ConcurrentEdits,
6464
lookaheadRanges: LookaheadRanges,
6565
reusedNodeCallback: ReusedNodeCallback? = nil
6666
) {
67-
self.previousTree = previousTree
67+
self.previousIncrementalParseResult = IncrementalParseResult(tree: previousTree, lookaheadRanges: lookaheadRanges)
68+
self.edits = edits
69+
self.reusedNodeCallback = reusedNodeCallback
70+
}
71+
72+
/// - Parameters:
73+
/// - previousIncrementalParseResult: The previous incremental parse result to do lookups on.
74+
/// - edits: The edits that have occurred since the last parse that resulted
75+
/// in the new source that is about to be parsed.
76+
/// - reusedNodeCallback: Optional closure to accept information about the re-used node. For each node that gets re-used `reusedNodeCallback` is called.
77+
public init(
78+
previousIncrementalParseResult: IncrementalParseResult,
79+
edits: ConcurrentEdits,
80+
reusedNodeCallback: ReusedNodeCallback? = nil
81+
) {
82+
self.previousIncrementalParseResult = previousIncrementalParseResult
6883
self.edits = edits
69-
self.lookaheadRanges = lookaheadRanges
7084
self.reusedNodeCallback = reusedNodeCallback
7185
}
7286
}
@@ -81,7 +95,7 @@ struct IncrementalParseLookup {
8195
/// given ``IncrementalParseTransition``.
8296
public init(transition: IncrementalParseTransition) {
8397
self.transition = transition
84-
self.cursor = .init(root: Syntax(transition.previousTree))
98+
self.cursor = .init(root: Syntax(transition.previousIncrementalParseResult.tree))
8599
}
86100

87101
fileprivate var edits: ConcurrentEdits {
@@ -148,7 +162,7 @@ struct IncrementalParseLookup {
148162
return true
149163
}
150164

151-
guard let nodeAffectRangeLength = transition.lookaheadRanges.lookaheadRanges[node.raw.id] else {
165+
guard let nodeAffectRangeLength = transition.previousIncrementalParseResult.lookaheadRanges.lookaheadRanges[node.raw.id] else {
152166
return false
153167
}
154168

Sources/SwiftParser/ParseSourceFile.swift

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,86 @@ extension Parser {
6262
/// how far the parser looked ahead while parsing a node, which is
6363
/// necessary to construct an ``IncrementalParseTransition`` for a
6464
/// subsequent incremental parse
65+
@available(*, deprecated, message: "Use parseIncrementally with `IncrementalParseResult` return instead")
66+
@_disfavoredOverload
6567
public static func parseIncrementally(
6668
source: String,
6769
parseTransition: IncrementalParseTransition?
6870
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
69-
var parser = Parser(source, parseTransition: parseTransition)
70-
return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges)
71+
let parseResult = parseIncrementally(source: source, parseTransition: parseTransition)
72+
return (parseResult.tree, parseResult.lookaheadRanges)
7173
}
7274

7375
/// Parse the source code in the given buffer as Swift source file with support
7476
/// for incremental parsing.
7577
///
76-
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)``
78+
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)-2gtt2``
79+
@available(*, deprecated, message: "Use parseIncrementally with `IncrementalParseResult` return instead")
80+
@_disfavoredOverload
7781
public static func parseIncrementally(
7882
source: UnsafeBufferPointer<UInt8>,
7983
maximumNestingLevel: Int? = nil,
8084
parseTransition: IncrementalParseTransition?
8185
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
86+
let parseResult = parseIncrementally(source: source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition)
87+
return (parseResult.tree, parseResult.lookaheadRanges)
88+
}
89+
90+
/// Parse the source code in the given string as Swift source file with support
91+
/// for incremental parsing.
92+
///
93+
/// When parsing a source file for the first time, invoke `parseIncrementally`
94+
/// with `parseTransition: nil`. This returns the ``IncrementalParseResult``
95+
/// If an edit is made to the source file, an ``IncrementalParseTransition``
96+
/// can be constructed from the ``IncrementalParseResult``.
97+
/// When invoking `parseIncrementally` again with
98+
/// the post-edit source and that parse transition, the parser will re-use
99+
/// nodes that haven’t changed.
100+
///
101+
/// - Parameters:
102+
/// - source: The source code to parse
103+
/// - parseTransition: If a similar source file has already been parsed, the
104+
/// ``IncrementalParseTransition`` that contains the previous tree as well
105+
/// as the edits that were performed to it.
106+
/// - Returns: The ``IncrementalParseResult``, which is
107+
/// necessary to construct an ``IncrementalParseTransition`` for a
108+
/// subsequent incremental parse
109+
public static func parseIncrementally(
110+
source: String,
111+
parseTransition: IncrementalParseTransition?
112+
) -> IncrementalParseResult {
113+
var parser = Parser(source, parseTransition: parseTransition)
114+
return IncrementalParseResult(tree: SourceFileSyntax.parse(from: &parser), lookaheadRanges: parser.lookaheadRanges)
115+
}
116+
117+
/// Parse the source code in the given buffer as Swift source file with support
118+
/// for incremental parsing.
119+
///
120+
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)-4kn2k``
121+
public static func parseIncrementally(
122+
source: UnsafeBufferPointer<UInt8>,
123+
maximumNestingLevel: Int? = nil,
124+
parseTransition: IncrementalParseTransition?
125+
) -> IncrementalParseResult {
82126
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition)
83-
return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges)
127+
return IncrementalParseResult(tree: SourceFileSyntax.parse(from: &parser), lookaheadRanges: parser.lookaheadRanges)
128+
}
129+
}
130+
131+
/// The result of incrementally parsing a file.
132+
///
133+
/// This contains the parsed syntax tree and additional information on how far the parser looked ahead to parse each node.
134+
/// This information is required to perform an incremental parse of the tree after applying edits to it.
135+
public struct IncrementalParseResult {
136+
/// The syntax tree from parsing source
137+
public let tree: SourceFileSyntax
138+
/// The lookahead ranges for syntax nodes describe
139+
/// how far the parser looked ahead while parsing a node.
140+
public let lookaheadRanges: LookaheadRanges
141+
142+
public init(tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
143+
self.tree = tree
144+
self.lookaheadRanges = lookaheadRanges
84145
}
85146
}
86147

Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,17 @@ public func assertIncrementalParse(
4444
let originalString = String(originalSource)
4545
let editedString = String(editedSource)
4646

47-
let (originalTree, lookaheadRanges) = Parser.parseIncrementally(source: originalString, parseTransition: nil)
47+
let parseResult = Parser.parseIncrementally(source: originalString, parseTransition: nil)
4848

4949
var reusedNodes: [Syntax] = []
5050
let transition = IncrementalParseTransition(
51-
previousTree: originalTree,
51+
previousIncrementalParseResult: parseResult,
5252
edits: concurrentEdits,
53-
lookaheadRanges: lookaheadRanges,
5453
reusedNodeCallback: { reusedNodes.append($0) }
5554
)
5655

5756
let newTree = Parser.parse(source: editedString)
58-
let (incrementallyParsedNewTree, _) = Parser.parseIncrementally(source: editedString, parseTransition: transition)
57+
let incrementallyParsedNewTree = Parser.parseIncrementally(source: editedString, parseTransition: transition).tree
5958

6059
// Round-trip
6160
assertStringsEqualWithDiff(

SwiftParserCLI/Sources/swift-parser-cli/Commands/PerformanceTest.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,14 @@ struct PerformanceTest: ParsableCommand {
4747
/// The initial parse for incremental parsing
4848
for file in files {
4949
file.withUnsafeBytes { buf in
50-
let (tree, lookaheadRanges) = Parser.parseIncrementally(
50+
let parseResult = Parser.parseIncrementally(
5151
source: buf.bindMemory(to: UInt8.self),
5252
parseTransition: nil
5353
)
5454

5555
fileTransition[file] = IncrementalParseTransition(
56-
previousTree: tree,
57-
edits: ConcurrentEdits(fromSequential: []),
58-
lookaheadRanges: lookaheadRanges
56+
previousIncrementalParseResult: parseResult,
57+
edits: ConcurrentEdits(fromSequential: [])
5958
)
6059
}
6160
}

0 commit comments

Comments
 (0)