Skip to content

Commit f73df6d

Browse files
authored
Merge pull request swiftlang#1192 from ahoppen/completionsession-improvements
Slightly simplify code completion logic
2 parents f9ce7ae + 2edfb2a commit f73df6d

File tree

1 file changed

+38
-62
lines changed

1 file changed

+38
-62
lines changed

Sources/SourceKitLSP/Swift/CodeCompletionSession.swift

+38-62
Original file line numberDiff line numberDiff line change
@@ -280,19 +280,11 @@ class CodeCompletionSession {
280280

281281
// MARK: - Helpers
282282

283-
private func expandClosurePlaceholders(
284-
insertText: String,
285-
utf8CodeUnitsToErase: Int,
286-
requestPosition: Position
287-
) -> String? {
283+
private func expandClosurePlaceholders(insertText: String) -> String? {
288284
guard insertText.contains("<#") && insertText.contains("->") else {
289285
// Fast path: There is no closure placeholder to expand
290286
return nil
291287
}
292-
guard requestPosition.line < snapshot.lineTable.count else {
293-
logger.error("Request position is past the last line")
294-
return nil
295-
}
296288

297289
let strippedPrefix: String
298290
let exprToExpand: String
@@ -336,79 +328,63 @@ class CodeCompletionSession {
336328
requestPosition: Position,
337329
isIncomplete: Bool
338330
) -> CompletionList {
339-
var result = CompletionList(isIncomplete: isIncomplete, items: [])
340-
341-
completions.forEach { (i: Int, value: SKDResponseDictionary) -> Bool in
342-
guard let name: String = value[keys.description] else {
343-
return true // continue
331+
let completionItems = completions.compactMap { (value: SKDResponseDictionary) -> CompletionItem? in
332+
guard let name: String = value[keys.description],
333+
var insertText: String = value[keys.sourceText]
334+
else {
335+
return nil
344336
}
345337

346338
var filterName: String? = value[keys.name]
347-
var insertText: String? = value[keys.sourceText]
348339
let typeName: String? = value[sourcekitd.keys.typeName]
349340
let docBrief: String? = value[sourcekitd.keys.docBrief]
350341
let utf8CodeUnitsToErase: Int = value[sourcekitd.keys.numBytesToErase] ?? 0
351342

352-
if let insertTextUnwrapped = insertText {
353-
insertText =
354-
expandClosurePlaceholders(
355-
insertText: insertTextUnwrapped,
356-
utf8CodeUnitsToErase: utf8CodeUnitsToErase,
357-
requestPosition: requestPosition
358-
) ?? insertText
343+
if let closureExpanded = expandClosurePlaceholders(insertText: insertText) {
344+
insertText = closureExpanded
359345
}
360346

361-
let text = insertText.map {
362-
rewriteSourceKitPlaceholders(in: $0, clientSupportsSnippets: clientSupportsSnippets)
363-
}
347+
let text = rewriteSourceKitPlaceholders(in: insertText, clientSupportsSnippets: clientSupportsSnippets)
364348
let isInsertTextSnippet = clientSupportsSnippets && text != insertText
365349

366350
let textEdit: TextEdit?
367-
if let text = text {
368-
let edit = self.computeCompletionTextEdit(
369-
completionPos: completionPos,
370-
requestPosition: requestPosition,
371-
utf8CodeUnitsToErase: utf8CodeUnitsToErase,
372-
newText: text,
373-
snapshot: snapshot
374-
)
375-
textEdit = edit
376-
377-
if utf8CodeUnitsToErase != 0, filterName != nil, let textEdit = textEdit {
378-
// To support the case where the client is doing prefix matching on the TextEdit range,
379-
// we need to prepend the deleted text to filterText.
380-
// This also works around a behaviour in VS Code that causes completions to not show up
381-
// if a '.' is being replaced for Optional completion.
382-
let filterPrefix = snapshot.text[snapshot.indexRange(of: textEdit.range.lowerBound..<completionPos)]
383-
filterName = filterPrefix + filterName!
384-
}
385-
} else {
386-
textEdit = nil
351+
let edit = self.computeCompletionTextEdit(
352+
completionPos: completionPos,
353+
requestPosition: requestPosition,
354+
utf8CodeUnitsToErase: utf8CodeUnitsToErase,
355+
newText: text,
356+
snapshot: snapshot
357+
)
358+
textEdit = edit
359+
360+
if utf8CodeUnitsToErase != 0, filterName != nil, let textEdit = textEdit {
361+
// To support the case where the client is doing prefix matching on the TextEdit range,
362+
// we need to prepend the deleted text to filterText.
363+
// This also works around a behaviour in VS Code that causes completions to not show up
364+
// if a '.' is being replaced for Optional completion.
365+
let filterPrefix = snapshot.text[snapshot.indexRange(of: textEdit.range.lowerBound..<completionPos)]
366+
filterName = filterPrefix + filterName!
387367
}
388368

389369
// Map SourceKit's not_recommended field to LSP's deprecated
390-
let notRecommended = (value[sourcekitd.keys.notRecommended] as Int?).map({ $0 != 0 })
370+
let notRecommended = (value[sourcekitd.keys.notRecommended] ?? 0) != 0
391371

392372
let kind: sourcekitd_api_uid_t? = value[sourcekitd.keys.kind]
393-
result.items.append(
394-
CompletionItem(
395-
label: name,
396-
kind: kind?.asCompletionItemKind(sourcekitd.values) ?? .value,
397-
detail: typeName,
398-
documentation: docBrief != nil ? .markupContent(MarkupContent(kind: .markdown, value: docBrief!)) : nil,
399-
deprecated: notRecommended ?? false,
400-
sortText: nil,
401-
filterText: filterName,
402-
insertText: text,
403-
insertTextFormat: isInsertTextSnippet ? .snippet : .plain,
404-
textEdit: textEdit.map(CompletionItemEdit.textEdit)
405-
)
373+
return CompletionItem(
374+
label: name,
375+
kind: kind?.asCompletionItemKind(sourcekitd.values) ?? .value,
376+
detail: typeName,
377+
documentation: docBrief != nil ? .markupContent(MarkupContent(kind: .markdown, value: docBrief!)) : nil,
378+
deprecated: notRecommended,
379+
sortText: nil,
380+
filterText: filterName,
381+
insertText: text,
382+
insertTextFormat: isInsertTextSnippet ? .snippet : .plain,
383+
textEdit: textEdit.map(CompletionItemEdit.textEdit)
406384
)
407-
408-
return true
409385
}
410386

411-
return result
387+
return CompletionList(isIncomplete: isIncomplete, items: completionItems)
412388
}
413389

414390
private func computeCompletionTextEdit(

0 commit comments

Comments
 (0)