Skip to content

Commit 68a90aa

Browse files
committed
Change CodeCompletionSession to not have a circular reference to SourceKitServer
This just clean up the layering slightly.
1 parent d9055b7 commit 68a90aa

File tree

2 files changed

+35
-37
lines changed

2 files changed

+35
-37
lines changed

Sources/SourceKitLSP/Swift/CodeCompletion.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,17 @@ extension SwiftLanguageServer {
8080
}
8181
session = currentSession
8282
} else {
83+
let clientSupportsSnippets = capabilityRegistry.clientCapabilities.textDocument?.completion?.completionItem?.snippetSupport ?? false
84+
8385
// FIXME: even if trigger kind is not from incomplete, we could to detect a compatible
8486
// location if we also check that the rest of the snapshot has not changed.
8587
session = CodeCompletionSession(
86-
server: self,
88+
sourcekitd: sourcekitd,
8789
snapshot: snapshot,
8890
utf8Offset: offset,
8991
position: completionPos,
90-
compileCommand: await buildSettings(for: snapshot.uri)
92+
compileCommand: await buildSettings(for: snapshot.uri),
93+
clientSupportsSnippets: clientSupportsSnippets
9194
)
9295

9396
await currentCompletionSession?.close()

Sources/SourceKitLSP/Swift/CodeCompletionSession.swift

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import SourceKitD
2626
/// At the sourcekitd level, this uses `codecomplete.open`, `codecomplete.update` and
2727
/// `codecomplete.close` requests.
2828
actor CodeCompletionSession {
29-
private unowned let server: SwiftLanguageServer
29+
private let sourcekitd: any SourceKitD
3030
private let snapshot: DocumentSnapshot
3131
let utf8StartOffset: Int
3232
private let position: Position
3333
private let compileCommand: SwiftCompileCommand?
34+
private let clientSupportsSnippets: Bool
3435
private var state: State = .closed
3536

3637
private enum State {
@@ -39,19 +40,22 @@ actor CodeCompletionSession {
3940
}
4041

4142
nonisolated var uri: DocumentURI { snapshot.uri }
43+
nonisolated var keys: sourcekitd_keys { return sourcekitd.keys }
4244

4345
init(
44-
server: SwiftLanguageServer,
46+
sourcekitd: any SourceKitD,
4547
snapshot: DocumentSnapshot,
4648
utf8Offset: Int,
4749
position: Position,
48-
compileCommand: SwiftCompileCommand?
50+
compileCommand: SwiftCompileCommand?,
51+
clientSupportsSnippets: Bool
4952
) {
50-
self.server = server
53+
self.sourcekitd = sourcekitd
5154
self.snapshot = snapshot
5255
self.utf8StartOffset = utf8Offset
5356
self.position = position
5457
self.compileCommand = compileCommand
58+
self.clientSupportsSnippets = clientSupportsSnippets
5559
}
5660

5761
/// Retrieve completions for the given `filterText`, opening or updating the session.
@@ -89,9 +93,8 @@ actor CodeCompletionSession {
8993
throw ResponseError(code: .invalidRequest, message: "open must use the original snapshot")
9094
}
9195

92-
let req = SKDRequestDictionary(sourcekitd: server.sourcekitd)
93-
let keys = server.sourcekitd.keys
94-
req[keys.request] = server.sourcekitd.requests.codecomplete_open
96+
let req = SKDRequestDictionary(sourcekitd: sourcekitd)
97+
req[keys.request] = sourcekitd.requests.codecomplete_open
9598
req[keys.offset] = utf8StartOffset
9699
req[keys.name] = uri.pseudoPath
97100
req[keys.sourcefile] = uri.pseudoPath
@@ -101,7 +104,7 @@ actor CodeCompletionSession {
101104
req[keys.compilerargs] = compileCommand.compilerArgs
102105
}
103106

104-
let dict = try await server.sourcekitd.send(req)
107+
let dict = try await sourcekitd.send(req)
105108

106109
guard let completions: SKDResponseArray = dict[keys.results] else {
107110
return CompletionList(isIncomplete: false, items: [])
@@ -127,14 +130,13 @@ actor CodeCompletionSession {
127130
// FIXME: Assertion for prefix of snapshot matching what we started with.
128131

129132
logger.info("Updating code completion session: \(self, privacy: .private) filter=\(filterText)")
130-
let req = SKDRequestDictionary(sourcekitd: server.sourcekitd)
131-
let keys = server.sourcekitd.keys
132-
req[keys.request] = server.sourcekitd.requests.codecomplete_update
133+
let req = SKDRequestDictionary(sourcekitd: sourcekitd)
134+
req[keys.request] = sourcekitd.requests.codecomplete_update
133135
req[keys.offset] = utf8StartOffset
134136
req[keys.name] = uri.pseudoPath
135137
req[keys.codecomplete_options] = optionsDictionary(filterText: filterText, options: options)
136138

137-
let dict = try await server.sourcekitd.send(req)
139+
let dict = try await sourcekitd.send(req)
138140
guard let completions: SKDResponseArray = dict[keys.results] else {
139141
return CompletionList(isIncomplete: false, items: [])
140142
}
@@ -152,8 +154,7 @@ actor CodeCompletionSession {
152154
filterText: String,
153155
options: SKCompletionOptions
154156
) -> SKDRequestDictionary {
155-
let dict = SKDRequestDictionary(sourcekitd: server.sourcekitd)
156-
let keys = server.sourcekitd.keys
157+
let dict = SKDRequestDictionary(sourcekitd: sourcekitd)
157158
// Sorting and priority options.
158159
dict[keys.codecomplete_hideunderscores] = 0
159160
dict[keys.codecomplete_hidelowpriority] = 0
@@ -169,26 +170,22 @@ actor CodeCompletionSession {
169170
return dict
170171
}
171172

172-
private func sendClose(_ server: SwiftLanguageServer) {
173-
let req = SKDRequestDictionary(sourcekitd: server.sourcekitd)
174-
let keys = server.sourcekitd.keys
175-
req[keys.request] = server.sourcekitd.requests.codecomplete_close
173+
private func sendClose() {
174+
let req = SKDRequestDictionary(sourcekitd: sourcekitd)
175+
req[keys.request] = sourcekitd.requests.codecomplete_close
176176
req[keys.offset] = self.utf8StartOffset
177177
req[keys.name] = self.snapshot.uri.pseudoPath
178178
logger.info("Closing code completion session: \(self, privacy: .private)")
179-
_ = try? server.sourcekitd.sendSync(req)
179+
_ = try? sourcekitd.sendSync(req)
180180
}
181181

182182
func close() async {
183-
// Temporary back-reference to server to keep it alive during close().
184-
let server = self.server
185-
186183
switch self.state {
187184
case .closed:
188185
// Already closed, nothing to do.
189186
break
190187
case .open:
191-
self.sendClose(server)
188+
self.sendClose()
192189
self.state = .closed
193190
}
194191
}
@@ -204,26 +201,24 @@ actor CodeCompletionSession {
204201
) -> CompletionList {
205202
var result = CompletionList(isIncomplete: isIncomplete, items: [])
206203

207-
completions.forEach { (i, value) -> Bool in
208-
guard let name: String = value[server.keys.description] else {
204+
completions.forEach { (i: Int, value: SKDResponseDictionary) -> Bool in
205+
guard let name: String = value[keys.description] else {
209206
return true // continue
210207
}
211208

212-
var filterName: String? = value[server.keys.name]
213-
let insertText: String? = value[server.keys.sourcetext]
214-
let typeName: String? = value[server.keys.typename]
215-
let docBrief: String? = value[server.keys.doc_brief]
209+
var filterName: String? = value[keys.name]
210+
let insertText: String? = value[keys.sourcetext]
211+
let typeName: String? = value[sourcekitd.keys.typename]
212+
let docBrief: String? = value[sourcekitd.keys.doc_brief]
216213

217-
let completionCapabilities = server.capabilityRegistry.clientCapabilities.textDocument?.completion
218-
let clientSupportsSnippets = completionCapabilities?.completionItem?.snippetSupport == true
219214
let text = insertText.map {
220215
rewriteSourceKitPlaceholders(inString: $0, clientSupportsSnippets: clientSupportsSnippets)
221216
}
222217
let isInsertTextSnippet = clientSupportsSnippets && text != insertText
223218

224219
let textEdit: TextEdit?
225220
if let text = text {
226-
let utf8CodeUnitsToErase: Int = value[server.keys.num_bytes_to_erase] ?? 0
221+
let utf8CodeUnitsToErase: Int = value[sourcekitd.keys.num_bytes_to_erase] ?? 0
227222

228223
textEdit = self.computeCompletionTextEdit(
229224
completionPos: completionPos,
@@ -254,13 +249,13 @@ actor CodeCompletionSession {
254249
}
255250

256251
// Map SourceKit's not_recommended field to LSP's deprecated
257-
let notRecommended = (value[server.keys.not_recommended] as Int?).map({ $0 != 0 })
252+
let notRecommended = (value[sourcekitd.keys.not_recommended] as Int?).map({ $0 != 0 })
258253

259-
let kind: sourcekitd_uid_t? = value[server.keys.kind]
254+
let kind: sourcekitd_uid_t? = value[sourcekitd.keys.kind]
260255
result.items.append(
261256
CompletionItem(
262257
label: name,
263-
kind: kind?.asCompletionItemKind(server.values) ?? .value,
258+
kind: kind?.asCompletionItemKind(sourcekitd.values) ?? .value,
264259
detail: typeName,
265260
documentation: docBrief != nil ? .markupContent(MarkupContent(kind: .markdown, value: docBrief!)) : nil,
266261
deprecated: notRecommended ?? false,

0 commit comments

Comments
 (0)