@@ -103,22 +103,6 @@ public struct SourceRange: Hashable, Codable, Sendable {
103
103
}
104
104
}
105
105
106
- /// Collects all `PoundSourceLocationSyntax` directives in a file.
107
- fileprivate class SourceLocationCollector : SyntaxVisitor {
108
- private var sourceLocationDirectives : [ PoundSourceLocationSyntax ] = [ ]
109
-
110
- override func visit( _ node: PoundSourceLocationSyntax ) -> SyntaxVisitorContinueKind {
111
- sourceLocationDirectives. append ( node)
112
- return . skipChildren
113
- }
114
-
115
- static func collectSourceLocations( in tree: some SyntaxProtocol ) -> [ PoundSourceLocationSyntax ] {
116
- let collector = SourceLocationCollector ( viewMode: . sourceAccurate)
117
- collector. walk ( tree)
118
- return collector. sourceLocationDirectives
119
- }
120
- }
121
-
122
106
fileprivate struct SourceLocationDirectiveArguments {
123
107
enum Error : Swift . Error , CustomStringConvertible {
124
108
case nonDecimalLineNumber( TokenSyntax )
@@ -169,8 +153,8 @@ public final class SourceLocationConverter {
169
153
/// The information from all `#sourceLocation` directives in the file
170
154
/// necessary to compute presumed locations.
171
155
///
172
- /// - `sourceLine` is the line at which the `#sourceLocation` statement occurs
173
- /// within the current file.
156
+ /// - `sourceLine` is the physical line number of the end of the last token of
157
+ /// `#sourceLocation(...)` directive within the current file.
174
158
/// - `arguments` are the `file` and `line` arguments of the directive or `nil`
175
159
/// if spelled as `#sourceLocation()` to reset the source location directive.
176
160
private var sourceLocationDirectives : [ ( sourceLine: Int , arguments: SourceLocationDirectiveArguments ? ) ] = [ ]
@@ -189,21 +173,7 @@ public final class SourceLocationConverter {
189
173
precondition ( tree. parent == nil , " SourceLocationConverter must be passed the root of the syntax tree " )
190
174
self . fileName = fileName
191
175
self . source = tree. syntaxTextBytes
192
- ( self . lines, endOfFile) = computeLines ( tree: Syntax ( tree) )
193
- precondition ( tree. totalLength. utf8Length == endOfFile. utf8Offset)
194
-
195
- for directive in SourceLocationCollector . collectSourceLocations ( in: tree) {
196
- let location = self . physicalLocation ( for: directive. positionAfterSkippingLeadingTrivia)
197
- if let args = directive. arguments {
198
- if let parsedArgs = try ? SourceLocationDirectiveArguments ( args) {
199
- // Ignore any malformed `#sourceLocation` directives.
200
- sourceLocationDirectives. append ( ( sourceLine: location. line, arguments: parsedArgs) )
201
- }
202
- } else {
203
- // `#sourceLocation()` without any arguments resets the `#sourceLocation` directive.
204
- sourceLocationDirectives. append ( ( sourceLine: location. line, arguments: nil ) )
205
- }
206
- }
176
+ ( self . lines, self . endOfFile, self . sourceLocationDirectives) = computeLines ( tree: Syntax ( tree) )
207
177
}
208
178
209
179
/// Create a new ``SourceLocationConverter`` to convert between ``AbsolutePosition``
@@ -511,21 +481,21 @@ extension SyntaxProtocol {
511
481
/// the end-of-file position.
512
482
fileprivate func computeLines(
513
483
tree: Syntax
514
- ) -> ( [ AbsolutePosition ] , AbsolutePosition ) {
515
- var lines : [ AbsolutePosition ] = [ ]
516
- // First line starts from the beginning.
517
- lines. append ( . startOfFile)
484
+ ) -> (
485
+ lines: [ AbsolutePosition ] ,
486
+ endOfFile: AbsolutePosition ,
487
+ sourceLocationDirectives: [ ( sourceLine: Int , arguments: SourceLocationDirectiveArguments ? ) ]
488
+ ) {
489
+ var lines : [ AbsolutePosition ] = [ . startOfFile]
518
490
var position : AbsolutePosition = . startOfFile
519
- let addLine = { ( lineLength: SourceLength ) in
491
+ var sourceLocationDirectives : [ ( sourceLine: Int , arguments: SourceLocationDirectiveArguments ? ) ] = [ ]
492
+ let lastLineLength = tree. raw. forEachLineLength { lineLength in
520
493
position += lineLength
521
494
lines. append ( position)
495
+ } handleSourceLocationDirective: { lineOffset, args in
496
+ sourceLocationDirectives. append ( ( sourceLine: lines. count + lineOffset, arguments: args) )
522
497
}
523
- var curPrefix : SourceLength = . zero
524
- for token in tree. tokens ( viewMode: . sourceAccurate) {
525
- curPrefix = token. forEachLineLength ( prefix: curPrefix, body: addLine)
526
- }
527
- position += curPrefix
528
- return ( lines, position)
498
+ return ( lines, position + lastLineLength, sourceLocationDirectives)
529
499
}
530
500
531
501
fileprivate func computeLines( _ source: SyntaxText ) -> ( [ AbsolutePosition ] , AbsolutePosition ) {
@@ -636,7 +606,7 @@ fileprivate extension RawTriviaPiece {
636
606
}
637
607
}
638
608
639
- fileprivate extension Array where Element == RawTriviaPiece {
609
+ fileprivate extension RawTriviaPieceBuffer {
640
610
/// Walks and passes to `body` the ``SourceLength`` for every detected line,
641
611
/// with the newline character included.
642
612
/// - Returns: The leftover ``SourceLength`` at the end of the walk.
@@ -652,18 +622,61 @@ fileprivate extension Array where Element == RawTriviaPiece {
652
622
}
653
623
}
654
624
655
- fileprivate extension TokenSyntax {
625
+ fileprivate extension RawSyntax {
656
626
/// Walks and passes to `body` the ``SourceLength`` for every detected line,
657
627
/// with the newline character included.
658
628
/// - Returns: The leftover ``SourceLength`` at the end of the walk.
659
629
func forEachLineLength(
660
630
prefix: SourceLength = . zero,
661
- body: ( SourceLength ) -> ( )
631
+ body: ( SourceLength ) -> ( ) ,
632
+ handleSourceLocationDirective: ( _ lineOffset: Int , _ arguments: SourceLocationDirectiveArguments ? ) -> ( )
662
633
) -> SourceLength {
663
634
var curPrefix = prefix
664
- curPrefix = self . tokenView. leadingRawTriviaPieces. forEachLineLength ( prefix: curPrefix, body: body)
665
- curPrefix = self . tokenView. rawText. forEachLineLength ( prefix: curPrefix, body: body)
666
- curPrefix = self . tokenView. trailingRawTriviaPieces. forEachLineLength ( prefix: curPrefix, body: body)
635
+ switch self . rawData. payload {
636
+ case . parsedToken( let dat) :
637
+ curPrefix = dat. wholeText. forEachLineLength ( prefix: curPrefix, body: body)
638
+ case . materializedToken( let dat) :
639
+ curPrefix = dat. leadingTrivia. forEachLineLength ( prefix: curPrefix, body: body)
640
+ curPrefix = dat. tokenText. forEachLineLength ( prefix: curPrefix, body: body)
641
+ curPrefix = dat. trailingTrivia. forEachLineLength ( prefix: curPrefix, body: body)
642
+ case . layout( let dat) :
643
+ for case let node? in dat. layout where SyntaxTreeViewMode . sourceAccurate. shouldTraverse ( node: node) {
644
+ curPrefix = node. forEachLineLength (
645
+ prefix: curPrefix,
646
+ body: body,
647
+ handleSourceLocationDirective: handleSourceLocationDirective
648
+ )
649
+ }
650
+
651
+ // Handle '#sourceLocation' directive.
652
+ if dat. kind == . poundSourceLocation {
653
+ // Count newlines in the trailing trivia. The client want to get the
654
+ // line of the _end_ of '#sourceLocation()' directive.
655
+ var lineOffset = 0
656
+ if let lastTok = self . lastToken ( viewMode: . sourceAccurate) {
657
+ switch lastTok. raw. rawData. payload {
658
+ case . parsedToken( let dat) :
659
+ _ = dat. trailingTriviaText. forEachLineLength ( body: { _ in lineOffset -= 1 } )
660
+ case . materializedToken( let dat) :
661
+ _ = dat. trailingTrivia. forEachLineLength ( body: { _ in lineOffset -= 1 } )
662
+ case . layout( _) :
663
+ preconditionFailure ( " lastToken(viewMode:) returned non-token " )
664
+ }
665
+ }
666
+
667
+ let directive = Syntax . forRoot ( self , rawNodeArena: self . arenaReference. retained)
668
+ . cast ( PoundSourceLocationSyntax . self)
669
+ if let args = directive. arguments {
670
+ if let parsedArgs = try ? SourceLocationDirectiveArguments ( args) {
671
+ // Ignore any malformed `#sourceLocation` directives.
672
+ handleSourceLocationDirective ( lineOffset, parsedArgs)
673
+ }
674
+ } else {
675
+ // `#sourceLocation()` without any arguments resets the `#sourceLocation` directive.
676
+ handleSourceLocationDirective ( lineOffset, nil )
677
+ }
678
+ }
679
+ }
667
680
return curPrefix
668
681
}
669
682
}
0 commit comments