13
13
import IndexStoreDB
14
14
import LSPLogging
15
15
import LanguageServerProtocol
16
+ import SemanticIndex
16
17
import SwiftSyntax
17
18
18
19
public enum TestStyle {
@@ -41,22 +42,26 @@ fileprivate extension SymbolOccurrence {
41
42
/// Find the innermost range of a document symbol that contains the given position.
42
43
private func findInnermostSymbolRange(
43
44
containing position: Position ,
44
- documentSymbols documentSymbolsResponse: DocumentSymbolResponse
45
+ documentSymbolsResponse: DocumentSymbolResponse
45
46
) -> Range < Position > ? {
46
- guard case . documentSymbols( let documentSymbols) = documentSymbolsResponse else {
47
- // Both `ClangLanguageService` and `SwiftLanguageService` return `documentSymbols` so we don't need to handle the
48
- // .symbolInformation case.
49
- logger. fault (
50
- """
51
- Expected documentSymbols response from language service to resolve test ranges but got \
52
- \( documentSymbolsResponse. forLogging)
53
- """
54
- )
55
- return nil
47
+ switch documentSymbolsResponse {
48
+ case . documentSymbols( let documentSymbols) :
49
+ return findInnermostSymbolRange ( containing: position, documentSymbols: documentSymbols)
50
+ case . symbolInformation( let symbolInformation) :
51
+ return findInnermostSymbolRange ( containing: position, symbolInformation: symbolInformation)
56
52
}
53
+ }
54
+
55
+ private func findInnermostSymbolRange(
56
+ containing position: Position ,
57
+ documentSymbols: [ DocumentSymbol ]
58
+ ) -> Range < Position > ? {
57
59
for documentSymbol in documentSymbols where documentSymbol. range. contains ( position) {
58
60
if let children = documentSymbol. children,
59
- let rangeOfChild = findInnermostSymbolRange ( containing: position, documentSymbols: . documentSymbols( children) )
61
+ let rangeOfChild = findInnermostSymbolRange (
62
+ containing: position,
63
+ documentSymbolsResponse: . documentSymbols( children)
64
+ )
60
65
{
61
66
// If a child contains the position, prefer that because it's more specific.
62
67
return rangeOfChild
@@ -66,6 +71,21 @@ private func findInnermostSymbolRange(
66
71
return nil
67
72
}
68
73
74
+ /// Return the smallest range in `symbolInformation` containing `position`.
75
+ private func findInnermostSymbolRange(
76
+ containing position: Position ,
77
+ symbolInformation symbolInformationArray: [ SymbolInformation ]
78
+ ) -> Range < Position > ? {
79
+ var bestRange : Range < Position > ? = nil
80
+ for symbolInformation in symbolInformationArray where symbolInformation. location. range. contains ( position) {
81
+ let range = symbolInformation. location. range
82
+ if bestRange == nil || ( bestRange!. lowerBound < range. lowerBound && range. upperBound < bestRange!. upperBound) {
83
+ bestRange = range
84
+ }
85
+ }
86
+ return bestRange
87
+ }
88
+
69
89
extension SourceKitLSPServer {
70
90
/// Converts a flat list of test symbol occurrences to a hierarchical `TestItem` array, inferring the hierarchical
71
91
/// structure from `childOf` relations between the symbol occurrences.
@@ -263,9 +283,15 @@ extension SourceKitLSPServer {
263
283
264
284
let syntacticTests = try await languageService. syntacticDocumentTests ( for: req. textDocument. uri, in: workspace)
265
285
266
- if let index = workspace. index ( checkedFor: . inMemoryModifiedFiles( documentManager) ) {
286
+ // We `syntacticDocumentTests` returns `nil`, it indicates that it doesn't support syntactic test discovery.
287
+ // In that case, the semantic index is the only source of tests we have and we thus want to show tests from the
288
+ // semantic index, even if they are out-of-date. The alternative would be showing now tests after an edit to a file.
289
+ let indexCheckLevel : IndexCheckLevel =
290
+ syntacticTests == nil ? . deletedFiles : . inMemoryModifiedFiles( documentManager)
291
+
292
+ if let index = workspace. index ( checkedFor: indexCheckLevel) {
267
293
var syntacticSwiftTestingTests : [ TestItem ] {
268
- syntacticTests. filter { $0. style == TestStyle . swiftTesting }
294
+ syntacticTests? . filter { $0. style == TestStyle . swiftTesting } ?? [ ]
269
295
}
270
296
271
297
let testSymbols =
@@ -283,7 +309,7 @@ extension SourceKitLSPServer {
283
309
for: testSymbols,
284
310
resolveLocation: { uri, position in
285
311
if uri == snapshot. uri, let documentSymbols,
286
- let range = findInnermostSymbolRange ( containing: position, documentSymbols : documentSymbols)
312
+ let range = findInnermostSymbolRange ( containing: position, documentSymbolsResponse : documentSymbols)
287
313
{
288
314
return Location ( uri: uri, range: range)
289
315
}
@@ -298,7 +324,7 @@ extension SourceKitLSPServer {
298
324
}
299
325
}
300
326
// We don't have any up-to-date semantic index entries for this file. Syntactically look for tests.
301
- return syntacticTests
327
+ return syntacticTests ?? [ ]
302
328
}
303
329
}
304
330
@@ -437,7 +463,7 @@ extension TestItem {
437
463
}
438
464
439
465
extension SwiftLanguageService {
440
- public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async throws -> [ TestItem ] {
466
+ public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async throws -> [ TestItem ] ? {
441
467
let snapshot = try documentManager. latestSnapshot ( uri)
442
468
let semanticSymbols = workspace. index ( checkedFor: . deletedFiles) ? . symbols ( inFilePath: snapshot. uri. pseudoPath)
443
469
let xctestSymbols = await SyntacticSwiftXCTestScanner . findTestSymbols (
@@ -453,7 +479,7 @@ extension SwiftLanguageService {
453
479
}
454
480
455
481
extension ClangLanguageService {
456
- public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async -> [ TestItem ] {
457
- return [ ]
482
+ public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async -> [ TestItem ] ? {
483
+ return nil
458
484
}
459
485
}
0 commit comments