@@ -18,13 +18,12 @@ import SKLogging
18
18
import SKUtilities
19
19
import SwiftExtensions
20
20
21
- /// A cache of symbol graphs and their associated snapshots opened in sourcekitd. Any opened documents will be
22
- /// closed when the cache is de-initialized.
21
+ /// Caches document snapshots from disk opened in sourcekitd.
23
22
///
24
23
/// Used by `textDocument/doccDocumentation` requests to retrieve symbol graphs for files that are not currently
25
24
/// open in the editor. This allows for retrieving multiple symbol graphs from the same file without having
26
25
/// to re-open and parse the syntax tree every time.
27
- actor SymbolGraphCache : Sendable {
26
+ actor OnDiskDocumentManager : Sendable {
28
27
private weak var sourceKitLSPServer : SourceKitLSPServer ?
29
28
private var openSnapshots : [ DocumentURI : ( snapshot: DocumentSnapshot , patchedCompileCommand: SwiftCompileCommand ? ) ]
30
29
@@ -33,20 +32,14 @@ actor SymbolGraphCache: Sendable {
33
32
self . openSnapshots = [ : ]
34
33
}
35
34
36
- /// Open a unique dummy document in sourcekitd that has the contents of the file on disk for uri, but an arbitrary
37
- /// URI which doesn't exist on disk. Return the symbol graph from sourcekitd.
35
+ /// Retrieve the symbol graph at a particular ``SymbolLocation``.
38
36
///
39
- /// The document will be retained until ``DocCSymbolGraphCache`` is de-initialized. This will avoid parsing the same
40
- /// document multiple times if more than one symbol needs to be looked up.
37
+ /// A unique dummy document will be opened in sourcekitd to fulfill the request.
41
38
///
42
- /// - Parameter symbolLocation: The location of a symbol to find the symbol graph for.
43
- /// - Returns: The symbol graph for this location, if any.
39
+ /// - Parameter symbolLocation: The location of the symbol to fetch the symbol graph for.
44
40
func fetchSymbolGraph( at symbolLocation: SymbolLocation ) async throws -> String ? {
41
+ let ( snapshot, patchedCompileCommand) = try await openDocumentSnapshot ( at: symbolLocation)
45
42
let swiftLanguageService = try await swiftLanguageService ( for: symbolLocation. documentUri)
46
- let ( snapshot, patchedCompileCommand) = try await swiftLanguageService. openSnapshotFromDiskOpenedInSourcekitd (
47
- uri: symbolLocation. documentUri,
48
- fallbackSettingsAfterTimeout: false
49
- )
50
43
return try await swiftLanguageService. cursorInfo (
51
44
snapshot,
52
45
compileCommand: patchedCompileCommand,
@@ -55,6 +48,52 @@ actor SymbolGraphCache: Sendable {
55
48
) . symbolGraph
56
49
}
57
50
51
+ /// Open a unique dummy document in sourcekitd that has the contents of the file on disk for uri, but an arbitrary
52
+ /// URI which doesn't exist on disk.
53
+ ///
54
+ /// The document will be retained until ``closeAllDocuments()`` is called. This will avoid parsing the same
55
+ /// document multiple times if more than one symbol needs to be looked up.
56
+ ///
57
+ /// - Parameter symbolLocation: The location of the document that will be opened.
58
+ func openDocumentSnapshot(
59
+ at symbolLocation: SymbolLocation
60
+ ) async throws -> ( snapshot: DocumentSnapshot , patchedCompileCommand: SwiftCompileCommand ? ) {
61
+ try await openDocumentSnapshot ( for: symbolLocation. documentUri)
62
+ }
63
+
64
+ /// Open a unique dummy document in sourcekitd that has the contents of the file on disk for uri, but an arbitrary
65
+ /// URI which doesn't exist on disk.
66
+ ///
67
+ /// The document will be retained until ``closeAllDocuments()`` is called. This will avoid parsing the same
68
+ /// document multiple times if more than one symbol needs to be looked up.
69
+ ///
70
+ /// - Parameter uri: The ``DocumentURI`` that will be opened.
71
+ func openDocumentSnapshot(
72
+ for uri: DocumentURI
73
+ ) async throws -> ( snapshot: DocumentSnapshot , patchedCompileCommand: SwiftCompileCommand ? ) {
74
+ guard let cachedSnapshot = openSnapshots [ uri] else {
75
+ let languageService = try await swiftLanguageService ( for: uri)
76
+ let snapshot = try await languageService. openSnapshotFromDiskOpenedInSourcekitd (
77
+ uri: uri,
78
+ fallbackSettingsAfterTimeout: false
79
+ )
80
+ openSnapshots [ uri] = snapshot
81
+ return snapshot
82
+ }
83
+ return cachedSnapshot
84
+ }
85
+
86
+ /// Closes all document snapshots that were opened by this ``OnDiskDocumentManager``.
87
+ func closeAllDocuments( ) async {
88
+ for (snapshot, _) in openSnapshots. values {
89
+ await orLog ( " Close helper document \( snapshot. uri. forLogging) " ) {
90
+ let languageService = try await swiftLanguageService ( for: snapshot. uri)
91
+ await languageService. closeSnapshotFromDiskOpenedInSourcekitd ( snapshot: snapshot)
92
+ }
93
+ }
94
+ openSnapshots = [ : ]
95
+ }
96
+
58
97
private func swiftLanguageService( for uri: DocumentURI ) async throws -> SwiftLanguageService {
59
98
guard let sourceKitLSPServer else {
60
99
throw ResponseError . internalError ( " SourceKit-LSP is shutting down " )
@@ -67,24 +106,20 @@ actor SymbolGraphCache: Sendable {
67
106
}
68
107
return swiftLanguageService
69
108
}
109
+ }
70
110
71
- deinit {
72
- guard let sourceKitLSPServer else {
73
- return
74
- }
75
-
76
- let documentsToClose = openSnapshots. values
77
- Task {
78
- for (snapshot, _) in documentsToClose {
79
- guard let workspace = await sourceKitLSPServer. workspaceForDocument ( uri: snapshot. uri) ,
80
- let languageService = await sourceKitLSPServer. languageService ( for: snapshot. uri, . swift, in: workspace) ,
81
- let swiftLanguageService = languageService as? SwiftLanguageService
82
- else {
83
- logger. log ( " Unable to find SwiftLanguageService to close helper document \( snapshot. uri. forLogging) " )
84
- return
85
- }
86
- await swiftLanguageService. closeSnapshotFromDiskOpenedInSourcekitd ( snapshot: snapshot)
87
- }
111
+ extension SourceKitLSPServer {
112
+ nonisolated ( nonsending) func withOnDiskDocumentManager< T> (
113
+ _ body: ( OnDiskDocumentManager ) async throws -> T
114
+ ) async rethrows -> T {
115
+ let onDiskDocumentManager = OnDiskDocumentManager ( sourceKitLSPServer: self )
116
+ do {
117
+ let result = try await body ( onDiskDocumentManager)
118
+ await onDiskDocumentManager. closeAllDocuments ( )
119
+ return result
120
+ } catch {
121
+ await onDiskDocumentManager. closeAllDocuments ( )
122
+ throw error
88
123
}
89
124
}
90
125
}
0 commit comments