10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
+ import BuildSystemIntegration
13
14
import Csourcekitd
14
15
import LanguageServerProtocol
15
16
import SKLogging
@@ -121,13 +122,62 @@ extension CursorInfoError: CustomStringConvertible {
121
122
}
122
123
123
124
extension SwiftLanguageService {
125
+ fileprivate func openHelperDocument( _ snapshot: DocumentSnapshot ) async throws {
126
+ let skreq = sourcekitd. dictionary ( [
127
+ keys. request: self . requests. editorOpen,
128
+ keys. name: snapshot. uri. pseudoPath,
129
+ keys. sourceText: snapshot. text,
130
+ keys. syntacticOnly: 1 ,
131
+ ] )
132
+ _ = try await sendSourcekitdRequest ( skreq, fileContents: snapshot. text)
133
+ }
134
+
135
+ fileprivate func closeHelperDocument( _ snapshot: DocumentSnapshot ) async {
136
+ let skreq = sourcekitd. dictionary ( [
137
+ keys. request: requests. editorClose,
138
+ keys. name: snapshot. uri. pseudoPath,
139
+ keys. cancelBuilds: 0 ,
140
+ ] )
141
+ _ = await orLog ( " Close helper document for cursor info " ) {
142
+ try await sendSourcekitdRequest ( skreq, fileContents: snapshot. text)
143
+ }
144
+ }
145
+
146
+ /// Ensures that the snapshot is open in sourcekitd before calling body(_:).
147
+ ///
148
+ /// - Parameters:
149
+ /// - uri: The URI of the document to be opened.
150
+ /// - body: A closure that accepts the DocumentSnapshot as a parameter.
151
+ fileprivate func withSnapshot< Result> (
152
+ uri: DocumentURI ,
153
+ _ body: ( _ snapshot: DocumentSnapshot ) async throws -> Result
154
+ ) async throws -> Result where Result: Sendable {
155
+ let documentManager = try self . documentManager
156
+ let snapshot = try await self . latestSnapshotOrDisk ( for: uri)
157
+
158
+ // If the document is not opened in the DocumentManager then send an open
159
+ // request to sourcekitd.
160
+ guard documentManager. openDocuments. contains ( uri) else {
161
+ try await openHelperDocument ( snapshot)
162
+ defer {
163
+ Task { await closeHelperDocument ( snapshot) }
164
+ }
165
+ return try await body ( snapshot)
166
+ }
167
+ return try await body ( snapshot)
168
+ }
169
+
124
170
/// Provides detailed information about a symbol under the cursor, if any.
125
171
///
126
172
/// Wraps the information returned by sourcekitd's `cursor_info` request, such as symbol name,
127
173
/// USR, and declaration location. This request does minimal processing of the result.
128
174
///
175
+ /// Ensures that the document is actually open in sourcekitd before sending the request. If not,
176
+ /// then the document will be opened for the duration of the request so that sourcekitd does not
177
+ /// have to read from disk.
178
+ ///
129
179
/// - Parameters:
130
- /// - url: Document URI in which to perform the request. Must be an open document.
180
+ /// - url: Document URI in which to perform the request.
131
181
/// - range: The position range within the document to lookup the symbol at.
132
182
/// - includeSymbolGraph: Whether or not to ask sourcekitd for the complete symbol graph.
133
183
/// - fallbackSettingsAfterTimeout: Whether fallback build settings should be used for the cursor info request if no
@@ -139,46 +189,46 @@ extension SwiftLanguageService {
139
189
fallbackSettingsAfterTimeout: Bool ,
140
190
additionalParameters appendAdditionalParameters: ( ( SKDRequestDictionary ) -> Void ) ? = nil
141
191
) async throws -> ( cursorInfo: [ CursorInfo ] , refactorActions: [ SemanticRefactorCommand ] , symbolGraph: String ? ) {
142
- let documentManager = try self . documentManager
143
- let snapshot = try await self . latestSnapshotOrDisk ( for: uri)
144
-
145
- let offsetRange = snapshot. utf8OffsetRange ( of: range)
146
-
147
- let keys = self . keys
148
-
149
- let skreq = sourcekitd. dictionary ( [
150
- keys. request: requests. cursorInfo,
151
- keys. cancelOnSubsequentRequest: 0 ,
152
- keys. offset: offsetRange. lowerBound,
153
- keys. length: offsetRange. upperBound != offsetRange. lowerBound ? offsetRange. count : nil ,
154
- keys. sourceFile: snapshot. uri. sourcekitdSourceFile,
155
- keys. primaryFile: snapshot. uri. primaryFile? . pseudoPath,
156
- keys. retrieveSymbolGraph: includeSymbolGraph ? 1 : 0 ,
157
- keys. compilerArgs: await self . buildSettings ( for: uri, fallbackAfterTimeout: fallbackSettingsAfterTimeout) ?
158
- . compilerArgs as [ SKDRequestValue ] ? ,
159
- ] )
160
-
161
- appendAdditionalParameters ? ( skreq)
162
-
163
- let dict = try await sendSourcekitdRequest ( skreq, fileContents: snapshot. text)
164
-
165
- var cursorInfoResults : [ CursorInfo ] = [ ]
166
- if let cursorInfo = CursorInfo ( dict, documentManager: documentManager, sourcekitd: sourcekitd) {
167
- cursorInfoResults. append ( cursorInfo)
192
+ try await withSnapshot ( uri: uri) { snapshot in
193
+ let documentManager = try self . documentManager
194
+ let offsetRange = snapshot. utf8OffsetRange ( of: range)
195
+
196
+ let keys = self . keys
197
+
198
+ let skreq = sourcekitd. dictionary ( [
199
+ keys. request: requests. cursorInfo,
200
+ keys. cancelOnSubsequentRequest: 0 ,
201
+ keys. offset: offsetRange. lowerBound,
202
+ keys. length: offsetRange. upperBound != offsetRange. lowerBound ? offsetRange. count : nil ,
203
+ keys. sourceFile: snapshot. uri. sourcekitdSourceFile,
204
+ keys. primaryFile: snapshot. uri. primaryFile? . pseudoPath,
205
+ keys. retrieveSymbolGraph: includeSymbolGraph ? 1 : 0 ,
206
+ keys. compilerArgs: await self . compileCommand ( for: uri, fallbackAfterTimeout: fallbackSettingsAfterTimeout) ?
207
+ . compilerArgs as [ SKDRequestValue ] ? ,
208
+ ] )
209
+
210
+ appendAdditionalParameters ? ( skreq)
211
+
212
+ let dict = try await sendSourcekitdRequest ( skreq, fileContents: snapshot. text)
213
+
214
+ var cursorInfoResults : [ CursorInfo ] = [ ]
215
+ if let cursorInfo = CursorInfo ( dict, documentManager: documentManager, sourcekitd: sourcekitd) {
216
+ cursorInfoResults. append ( cursorInfo)
217
+ }
218
+ cursorInfoResults +=
219
+ dict [ keys. secondarySymbols] ?
220
+ . compactMap { CursorInfo ( $0, documentManager: documentManager, sourcekitd: sourcekitd) } ?? [ ]
221
+ let refactorActions =
222
+ [ SemanticRefactorCommand] (
223
+ array: dict [ keys. refactorActions] ,
224
+ range: range,
225
+ textDocument: TextDocumentIdentifier ( uri) ,
226
+ keys,
227
+ self . sourcekitd. api
228
+ ) ?? [ ]
229
+ let symbolGraph : String ? = dict [ keys. symbolGraph]
230
+
231
+ return ( cursorInfoResults, refactorActions, symbolGraph)
168
232
}
169
- cursorInfoResults +=
170
- dict [ keys. secondarySymbols] ?
171
- . compactMap { CursorInfo ( $0, documentManager: documentManager, sourcekitd: sourcekitd) } ?? [ ]
172
- let refactorActions =
173
- [ SemanticRefactorCommand] (
174
- array: dict [ keys. refactorActions] ,
175
- range: range,
176
- textDocument: TextDocumentIdentifier ( uri) ,
177
- keys,
178
- self . sourcekitd. api
179
- ) ?? [ ]
180
- let symbolGraph : String ? = dict [ keys. symbolGraph]
181
-
182
- return ( cursorInfoResults, refactorActions, symbolGraph)
183
233
}
184
234
}
0 commit comments