Skip to content

Commit 7e376e0

Browse files
committed
Reimplement inlay hints using CollectVariableType
Query VariableTypeInfos from sourcekitd and return inlay hints for all variable declarations without an explicit type annotation.
1 parent 134073d commit 7e376e0

File tree

3 files changed

+26
-63
lines changed

3 files changed

+26
-63
lines changed

Sources/SourceKitD/SKDResponseDictionary.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ public final class SKDResponseDictionary {
3232
public subscript(key: sourcekitd_uid_t?) -> Int? {
3333
return Int(sourcekitd.api.variant_dictionary_get_int64(dict, key))
3434
}
35+
public subscript(key: sourcekitd_uid_t?) -> Bool? {
36+
return sourcekitd.api.variant_dictionary_get_bool(dict, key)
37+
}
3538
public subscript(key: sourcekitd_uid_t?) -> sourcekitd_uid_t? {
3639
return sourcekitd.api.variant_dictionary_get_uid(dict, key)
3740
}

Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift

Lines changed: 18 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,77 +1055,33 @@ extension SwiftLanguageServer {
10551055
}
10561056

10571057
public func inlayHints(_ req: Request<InlayHintsRequest>) {
1058-
// TODO: Introduce a new SourceKit request for inlay hints
1059-
// instead of computing them from document symbols here.
1060-
10611058
guard req.params.only?.contains(.type) ?? true else {
10621059
req.reply([])
10631060
return
10641061
}
10651062

10661063
let uri = req.params.textDocument.uri
1067-
documentSymbols(uri) { symbolsResult in
1064+
variableTypeInfos(uri) { infosResult in
10681065
do {
1069-
/// Filters all the document symbols for which inlay type hints
1070-
/// should be displayed, i.e. variable bindings, fields and properties.
1071-
func bindings(_ symbols: [DocumentSymbol]) -> [DocumentSymbol] {
1072-
symbols
1073-
.flatMap { bindings($0.children ?? []) + ([.variable, .field, .property].contains($0.kind) ? [$0] : []) }
1074-
}
1075-
1076-
let symbols = try symbolsResult.get()
1077-
let bindingPositions = Set(bindings(symbols).map { $0.range.upperBound })
1078-
1079-
self.expressionTypeInfos(uri) { infosResult in
1080-
do {
1081-
let infos = try infosResult.get()
1082-
1083-
// The unfiltered infos may contain multiple infos for a single position
1084-
// as the ending position does not necessarily identify an expression uniquely.
1085-
// Consider the following example:
1086-
//
1087-
// var x = "abc" + "def"
1088-
//
1089-
// Both `"abc" + "def"` and `"def"` are matching expressions. Since we are only
1090-
// interested in the first expression, i.e. the one that corresponds to the
1091-
// bound expression, we have to do some pre-processing here. Note that this
1092-
// mechanism currently relies on the outermost expression being reported first.
1093-
1094-
var visitedPositions: Set<Position> = []
1095-
var processedInfos: [ExpressionTypeInfo] = []
1096-
1097-
// TODO: Compute inlay hints only for the requested range/categories
1098-
// instead of filtering them afterwards.
1099-
1100-
for info in infos {
1101-
let pos = info.range.upperBound
1102-
if (req.params.range?.contains(pos) ?? true)
1103-
&& bindingPositions.contains(pos)
1104-
&& !visitedPositions.contains(pos) {
1105-
processedInfos.append(info)
1106-
visitedPositions.insert(pos)
1107-
}
1108-
}
1109-
1110-
let hints = processedInfos
1111-
.lazy
1112-
.map { info in
1113-
InlayHint(
1114-
position: info.range.upperBound,
1115-
category: .type,
1116-
label: info.printedType
1117-
)
1118-
}
1119-
1120-
req.reply(.success(Array(hints)))
1121-
} catch {
1122-
let message = "expression types for inlay hints failed for \(uri): \(error)"
1123-
log(message, level: .warning)
1124-
req.reply(.failure(.unknown(message)))
1066+
let infos = try infosResult.get()
1067+
let hints = infos
1068+
.lazy
1069+
.filter { info in
1070+
// TODO: Include range in CollectVariableType request directly
1071+
(req.params.range?.contains(info.range.upperBound) ?? true)
1072+
&& !info.hasExplicitType
11251073
}
1126-
}
1074+
.map { info in
1075+
InlayHint(
1076+
position: info.range.upperBound,
1077+
category: .type,
1078+
label: info.printedType
1079+
)
1080+
}
1081+
1082+
req.reply(.success(Array(hints)))
11271083
} catch {
1128-
let message = "document symbols for inlay hints failed for \(uri): \(error)"
1084+
let message = "variable types for inlay hints failed for \(uri): \(error)"
11291085
log(message, level: .warning)
11301086
req.reply(.failure(.unknown(message)))
11311087
}

Sources/SourceKitLSP/Swift/VariableTypeInfo.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ struct VariableTypeInfo {
2020
var range: Range<Position>
2121
/// The printed type of the variable.
2222
var printedType: String
23+
/// Whether the variable has an explicit type annotation in the source file.
24+
var hasExplicitType: Bool
2325

2426
init?(_ dict: SKDResponseDictionary, in snapshot: DocumentSnapshot) {
2527
let keys = dict.sourcekitd.keys
@@ -28,12 +30,14 @@ struct VariableTypeInfo {
2830
let length: Int = dict[keys.variable_length],
2931
let startIndex = snapshot.positionOf(utf8Offset: offset),
3032
let endIndex = snapshot.positionOf(utf8Offset: offset + length),
31-
let printedType: String = dict[keys.variable_type] else {
33+
let printedType: String = dict[keys.variable_type],
34+
let hasExplicitType: Bool = dict[keys.variable_type_explicit] else {
3235
return nil
3336
}
3437

3538
self.range = startIndex..<endIndex
3639
self.printedType = printedType
40+
self.hasExplicitType = hasExplicitType
3741
}
3842
}
3943

0 commit comments

Comments
 (0)