1010//
1111//===----------------------------------------------------------------------===//
1212
13+ import BuildSystemIntegration
1314import Csourcekitd
1415import LanguageServerProtocol
1516import SKLogging
@@ -121,13 +122,62 @@ extension CursorInfoError: CustomStringConvertible {
121122}
122123
123124extension 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+
124170 /// Provides detailed information about a symbol under the cursor, if any.
125171 ///
126172 /// Wraps the information returned by sourcekitd's `cursor_info` request, such as symbol name,
127173 /// USR, and declaration location. This request does minimal processing of the result.
128174 ///
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+ ///
129179 /// - 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.
131181 /// - range: The position range within the document to lookup the symbol at.
132182 /// - includeSymbolGraph: Whether or not to ask sourcekitd for the complete symbol graph.
133183 /// - fallbackSettingsAfterTimeout: Whether fallback build settings should be used for the cursor info request if no
@@ -139,46 +189,46 @@ extension SwiftLanguageService {
139189 fallbackSettingsAfterTimeout: Bool ,
140190 additionalParameters appendAdditionalParameters: ( ( SKDRequestDictionary ) -> Void ) ? = nil
141191 ) 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)
168232 }
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)
183233 }
184234}
0 commit comments