Skip to content

Commit bbbc93e

Browse files
committed
Refactor cursorInfoFromDisk to share the underlying logic of invoking cursor info
1 parent 4fb5323 commit bbbc93e

8 files changed

+122
-150
lines changed

Sources/SourceKitLSP/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ target_sources(SourceKitLSP PRIVATE
4444
Swift/CodeCompletionSession.swift
4545
Swift/CommentXML.swift
4646
Swift/CursorInfo.swift
47-
Swift/CursorInfoFromDisk.swift
4847
Swift/Diagnostic.swift
4948
Swift/DiagnosticReportManager.swift
5049
Swift/DocumentFormatting.swift
@@ -77,6 +76,7 @@ target_sources(SourceKitLSP PRIVATE
7776
Swift/SyntaxHighlightingTokens.swift
7877
Swift/SyntaxTreeManager.swift
7978
Swift/VariableTypeInfo.swift
79+
Swift/WithSnapshotFromDiskOpenedInSourcekitd.swift
8080
)
8181
set_target_properties(SourceKitLSP PROPERTIES
8282
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})

Sources/SourceKitLSP/Documentation/DoccDocumentationHandler.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,18 @@ extension DocumentationLanguageService {
8181
)
8282
}
8383
let position = symbolSnapshot.position(of: symbolOccurrence.location)
84-
let cursorInfo = try await languageService.cursorInfoFromDisk(
85-
symbolOccurrence.location.documentUri,
86-
position..<position,
87-
includeSymbolGraph: true,
84+
let symbolGraph = try await languageService.withSnapshotFromDiskOpenedInSourcekitd(
85+
uri: symbolOccurrence.location.documentUri,
8886
fallbackSettingsAfterTimeout: false
89-
)
90-
guard let symbolGraph = cursorInfo.symbolGraph else {
87+
) { snapshot, compileCommand in
88+
try await languageService.cursorInfo(
89+
snapshot,
90+
compileCommand: compileCommand,
91+
Range(position),
92+
includeSymbolGraph: true
93+
).symbolGraph
94+
}
95+
guard let symbolGraph else {
9196
throw ResponseError.internalError("Unable to retrieve symbol graph for \(symbolOccurrence.symbol.name)")
9297
}
9398
return try await documentationManager.renderDocCDocumentation(

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import struct TSCBasic.AbsolutePath
2929
import protocol TSCBasic.FileSystem
3030

3131
#if canImport(DocCDocumentation)
32-
package import DocCDocumentation
32+
import DocCDocumentation
3333
#endif
3434

3535
/// Disambiguate LanguageServerProtocol.Language and IndexstoreDB.Language

Sources/SourceKitLSP/Swift/CursorInfo.swift

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,14 @@ struct CursorInfo {
4646
self.documentation = documentation
4747
}
4848

49+
/// `snapshot` is the snapshot from which the cursor info was invoked. It is necessary to pass that snapshot in here
50+
/// because snapshot might not be open in `documentManager` if cursor info was invoked using file contents from disk
51+
/// using `cursorInfoFromDisk` and thus we couldn't convert positions if the snapshot wasn't passed in explicitly.
52+
/// For all document other than `snapshot`, `documentManager` is used to find the latest snapshot of a document to
53+
/// convert positions.
4954
init?(
5055
_ dict: SKDResponseDictionary,
56+
snapshot: DocumentSnapshot,
5157
documentManager: DocumentManager,
5258
sourcekitd: some SourceKitD
5359
) {
@@ -63,7 +69,13 @@ struct CursorInfo {
6369
let column: Int = dict[keys.column]
6470
{
6571
let uri = DocumentURI(filePath: filepath, isDirectory: false)
66-
if let snapshot = documentManager.latestSnapshotOrDisk(uri, language: .swift) {
72+
let snapshot: DocumentSnapshot? =
73+
if snapshot.uri.sourcekitdSourceFile == filepath {
74+
snapshot
75+
} else {
76+
documentManager.latestSnapshotOrDisk(uri, language: .swift)
77+
}
78+
if let snapshot {
6779
let position = snapshot.positionOf(zeroBasedLine: line - 1, utf8Column: column - 1)
6880
location = Location(uri: uri, range: Range(position))
6981
} else {
@@ -133,14 +145,13 @@ extension SwiftLanguageService {
133145
/// - fallbackSettingsAfterTimeout: Whether fallback build settings should be used for the cursor info request if no
134146
/// build settings can be retrieved within a timeout.
135147
func cursorInfo(
136-
_ uri: DocumentURI,
148+
_ snapshot: DocumentSnapshot,
149+
compileCommand: SwiftCompileCommand?,
137150
_ range: Range<Position>,
138151
includeSymbolGraph: Bool = false,
139-
fallbackSettingsAfterTimeout: Bool,
140152
additionalParameters appendAdditionalParameters: ((SKDRequestDictionary) -> Void)? = nil
141153
) async throws -> (cursorInfo: [CursorInfo], refactorActions: [SemanticRefactorCommand], symbolGraph: String?) {
142154
let documentManager = try self.documentManager
143-
let snapshot = try await self.latestSnapshot(for: uri)
144155

145156
let offsetRange = snapshot.utf8OffsetRange(of: range)
146157

@@ -154,31 +165,46 @@ extension SwiftLanguageService {
154165
keys.sourceFile: snapshot.uri.sourcekitdSourceFile,
155166
keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath,
156167
keys.retrieveSymbolGraph: includeSymbolGraph ? 1 : 0,
157-
keys.compilerArgs: await self.compileCommand(for: uri, fallbackAfterTimeout: fallbackSettingsAfterTimeout)?
158-
.compilerArgs as [SKDRequestValue]?,
168+
keys.compilerArgs: compileCommand?.compilerArgs as [SKDRequestValue]?,
159169
])
160170

161171
appendAdditionalParameters?(skreq)
162172

163173
let dict = try await sendSourcekitdRequest(skreq, fileContents: snapshot.text)
164174

165175
var cursorInfoResults: [CursorInfo] = []
166-
if let cursorInfo = CursorInfo(dict, documentManager: documentManager, sourcekitd: sourcekitd) {
176+
if let cursorInfo = CursorInfo(dict, snapshot: snapshot, documentManager: documentManager, sourcekitd: sourcekitd) {
167177
cursorInfoResults.append(cursorInfo)
168178
}
169179
cursorInfoResults +=
170180
dict[keys.secondarySymbols]?
171-
.compactMap { CursorInfo($0, documentManager: documentManager, sourcekitd: sourcekitd) } ?? []
181+
.compactMap { CursorInfo($0, snapshot: snapshot, documentManager: documentManager, sourcekitd: sourcekitd) } ?? []
172182
let refactorActions =
173183
[SemanticRefactorCommand](
174184
array: dict[keys.refactorActions],
175185
range: range,
176-
textDocument: TextDocumentIdentifier(uri),
186+
textDocument: TextDocumentIdentifier(snapshot.uri),
177187
keys,
178188
self.sourcekitd.api
179189
) ?? []
180190
let symbolGraph: String? = dict[keys.symbolGraph]
181191

182192
return (cursorInfoResults, refactorActions, symbolGraph)
183193
}
194+
195+
func cursorInfo(
196+
_ uri: DocumentURI,
197+
_ range: Range<Position>,
198+
includeSymbolGraph: Bool = false,
199+
fallbackSettingsAfterTimeout: Bool,
200+
additionalParameters appendAdditionalParameters: ((SKDRequestDictionary) -> Void)? = nil
201+
) async throws -> (cursorInfo: [CursorInfo], refactorActions: [SemanticRefactorCommand], symbolGraph: String?) {
202+
return try await self.cursorInfo(
203+
self.latestSnapshot(for: uri),
204+
compileCommand: await self.compileCommand(for: uri, fallbackAfterTimeout: fallbackSettingsAfterTimeout),
205+
range,
206+
includeSymbolGraph: includeSymbolGraph,
207+
additionalParameters: appendAdditionalParameters
208+
)
209+
}
184210
}

Sources/SourceKitLSP/Swift/CursorInfoFromDisk.swift

Lines changed: 0 additions & 129 deletions
This file was deleted.

Sources/SourceKitLSP/Swift/GeneratedInterfaceDocumentURLData.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import Foundation
1414
import LanguageServerProtocol
1515

1616
/// Represents url of generated interface reference document.
17-
1817
package struct GeneratedInterfaceDocumentURLData: Hashable, ReferenceURLData {
1918
package static let documentType = "generated-swift-interface"
2019

Sources/SourceKitLSP/Swift/SwiftLanguageService.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ extension SwiftLanguageService {
528528

529529
// MARK: - Text synchronization
530530

531-
private func openDocumentSourcekitdRequest(
531+
func openDocumentSourcekitdRequest(
532532
snapshot: DocumentSnapshot,
533533
compileCommand: SwiftCompileCommand?
534534
) -> SKDRequestDictionary {
@@ -568,7 +568,9 @@ extension SwiftLanguageService {
568568
buildSettingsForOpenFiles[snapshot.uri] = buildSettings
569569

570570
let req = openDocumentSourcekitdRequest(snapshot: snapshot, compileCommand: buildSettings)
571-
_ = try? await self.sendSourcekitdRequest(req, fileContents: snapshot.text)
571+
await orLog("Opening sourcekitd document") {
572+
_ = try await self.sendSourcekitdRequest(req, fileContents: snapshot.text)
573+
}
572574
await publishDiagnosticsIfNeeded(for: notification.textDocument.uri)
573575
}
574576
}
@@ -585,7 +587,9 @@ extension SwiftLanguageService {
585587
await generatedInterfaceManager.close(document: data)
586588
case nil:
587589
let req = closeDocumentSourcekitdRequest(uri: notification.textDocument.uri)
588-
_ = try? await self.sendSourcekitdRequest(req, fileContents: nil)
590+
await orLog("Closing sourcekitd document") {
591+
_ = try await self.sendSourcekitdRequest(req, fileContents: nil)
592+
}
589593
}
590594
}
591595

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import BuildSystemIntegration
14+
import Foundation
15+
import LanguageServerProtocol
16+
import SKLogging
17+
import SKUtilities
18+
import SwiftExtensions
19+
20+
extension SwiftLanguageService {
21+
/// Open a unique dummy document in sourcekitd that has the contents of the file on disk for `uri` but an arbitrary
22+
/// URI which doesn't exist on disk. Invoke `body` with a snapshot that contains the on-disk document contents and has
23+
/// that dummy URI as well as build settings that were inferred from `uri` but have that URI replaced with the dummy
24+
/// URI. Close the document in sourcekit after `body` has finished.
25+
func withSnapshotFromDiskOpenedInSourcekitd<Result: Sendable>(
26+
uri: DocumentURI,
27+
fallbackSettingsAfterTimeout: Bool,
28+
body: (_ snapshot: DocumentSnapshot, _ patchedCompileCommand: SwiftCompileCommand?) async throws -> Result
29+
) async throws -> Result {
30+
guard let fileURL = uri.fileURL else {
31+
throw ResponseError.unknown("Cannot create snapshot with on-disk contents for non-file URI \(uri.forLogging)")
32+
}
33+
let snapshot = DocumentSnapshot(
34+
uri: try DocumentURI(filePath: "\(UUID().uuidString)/\(fileURL.filePath)", isDirectory: false),
35+
language: .swift,
36+
version: 0,
37+
lineTable: LineTable(try String(contentsOf: fileURL, encoding: .utf8))
38+
)
39+
let patchedCompileCommand: SwiftCompileCommand? =
40+
if let buildSettings = await self.buildSettings(
41+
for: uri,
42+
fallbackAfterTimeout: fallbackSettingsAfterTimeout
43+
) {
44+
SwiftCompileCommand(buildSettings.patching(newFile: snapshot.uri, originalFile: uri))
45+
} else {
46+
nil
47+
}
48+
49+
_ = try await sendSourcekitdRequest(
50+
self.openDocumentSourcekitdRequest(snapshot: snapshot, compileCommand: patchedCompileCommand),
51+
fileContents: snapshot.text
52+
)
53+
let result: Swift.Result<Result, Error>
54+
do {
55+
result = .success(try await body(snapshot, patchedCompileCommand))
56+
} catch {
57+
result = .failure(error)
58+
}
59+
await orLog("Close helper document '\(snapshot.uri)' for cursorInfoFromDisk") {
60+
_ = try await sendSourcekitdRequest(
61+
self.closeDocumentSourcekitdRequest(uri: snapshot.uri),
62+
fileContents: snapshot.text
63+
)
64+
}
65+
return try result.get()
66+
}
67+
}

0 commit comments

Comments
 (0)