Skip to content

Commit 68b3900

Browse files
committed
Return signature help parameter offsets as UTF-16 offsets
1 parent aeb3006 commit 68b3900

File tree

2 files changed

+78
-10
lines changed

2 files changed

+78
-10
lines changed

Sources/SwiftLanguageService/SignatureHelp.swift

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,43 @@ import SourceKitD
1717
import SourceKitLSP
1818
import SwiftBasicFormat
1919

20+
fileprivate extension String {
21+
func utf16Offset(of utf8Offset: Int, callerFile: StaticString = #fileID, callerLine: UInt = #line) -> Int {
22+
guard
23+
let stringIndex = self.utf8.index(self.startIndex, offsetBy: utf8Offset, limitedBy: self.endIndex)
24+
else {
25+
logger.fault(
26+
"""
27+
UTF-8 offset is past the end of the string while getting UTF-16 offset of \(utf8Offset) \
28+
(\(callerFile, privacy: .public):\(callerLine, privacy: .public))
29+
"""
30+
)
31+
return self.utf16.count
32+
}
33+
return self.utf16.distance(from: self.startIndex, to: stringIndex)
34+
}
35+
}
36+
2037
fileprivate extension ParameterInformation {
21-
init?(_ parameter: SKDResponseDictionary, _ keys: sourcekitd_api_keys) {
38+
init?(_ parameter: SKDResponseDictionary, _ signatureLabel: String, _ keys: sourcekitd_api_keys) {
2239
guard let nameOffset = parameter[keys.nameOffset] as Int?,
2340
let nameLength = parameter[keys.nameLength] as Int?
2441
else {
2542
return nil
2643
}
2744

28-
let documentation = parameter[keys.docComment].map {
29-
StringOrMarkupContent.markupContent(MarkupContent(kind: .markdown, value: $0))
30-
}
45+
let documentation: StringOrMarkupContent? =
46+
if let docComment: String = parameter[keys.docComment] {
47+
.markupContent(MarkupContent(kind: .markdown, value: docComment))
48+
} else {
49+
nil
50+
}
51+
52+
let labelStart = signatureLabel.utf16Offset(of: nameOffset)
53+
let labelEnd = signatureLabel.utf16Offset(of: nameOffset + nameLength)
3154

3255
self.init(
33-
label: .offsets(start: nameOffset, end: nameOffset + nameLength),
56+
label: .offsets(start: labelStart, end: labelEnd),
3457
documentation: documentation
3558
)
3659
}
@@ -45,11 +68,14 @@ fileprivate extension SignatureInformation {
4568
}
4669

4770
let activeParameter = signature[keys.activeParameter] as Int?
48-
let parameters = skParameters.compactMap { ParameterInformation($0, keys) }
49-
50-
let documentation = signature[keys.docComment].map {
51-
StringOrMarkupContent.markupContent(MarkupContent(kind: .markdown, value: $0))
52-
}
71+
let parameters = skParameters.compactMap { ParameterInformation($0, label, keys) }
72+
73+
let documentation: StringOrMarkupContent? =
74+
if let docComment: String = signature[keys.docComment] {
75+
.markupContent(MarkupContent(kind: .markdown, value: docComment))
76+
} else {
77+
nil
78+
}
5379

5480
self.init(
5581
label: label,

Tests/SourceKitLSPTests/SwiftSignatureHelpTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,48 @@ final class SwiftSignatureHelpTests: XCTestCase {
373373
)
374374
}
375375

376+
func testSignatureHelpNonASCII() async throws {
377+
let testClient = try await TestSourceKitLSPClient()
378+
let uri = DocumentURI(for: .swift)
379+
380+
let positions = testClient.openDocument(
381+
"""
382+
/// This is a test function
383+
func 🧑‍🧑‍🧒‍🧒🧑‍🧑‍🧒‍🧒(🧑🏽‍🚀🧑🏽‍🚀: Int, `🕵🏻‍♀️🕵🏻‍♀️`: String) -> Double { 0 }
384+
385+
func main() {
386+
🧑‍🧑‍🧒‍🧒🧑‍🧑‍🧒‍🧒(🧑🏽‍🚀🧑🏽‍🚀: 1️⃣)
387+
}
388+
""",
389+
uri: uri
390+
)
391+
392+
let result = try await testClient.send(
393+
SignatureHelpRequest(
394+
textDocument: TextDocumentIdentifier(uri),
395+
position: positions["1️⃣"]
396+
)
397+
)
398+
399+
let signatureHelp = try XCTUnwrap(result)
400+
let signature = try XCTUnwrap(signatureHelp.signatures.only)
401+
402+
XCTAssertEqual(signatureHelp.activeSignature, 0)
403+
XCTAssertEqual(signatureHelp.activeParameter, 0)
404+
XCTAssertEqual(signature.label, "🧑‍🧑‍🧒‍🧒🧑‍🧑‍🧒‍🧒(🧑🏽‍🚀🧑🏽‍🚀: Int, `🕵🏻‍♀️🕵🏻‍♀️`: String) -> Double")
405+
XCTAssertEqual(
406+
signature.documentation,
407+
.markupContent(MarkupContent(kind: .markdown, value: "This is a test function"))
408+
)
409+
XCTAssertEqual(
410+
signature.parameters,
411+
[
412+
ParameterInformation(label: .offsets(start: 23, end: 42)),
413+
ParameterInformation(label: .offsets(start: 44, end: 68)),
414+
]
415+
)
416+
}
417+
376418
func testSignatureHelpSwiftPMProject() async throws {
377419
let project = try await SwiftPMTestProject(
378420
files: [

0 commit comments

Comments
 (0)