Skip to content

Commit 53df6b2

Browse files
committed
Normalize Windows drive letter to be uppercase
VS Code spells file paths with a lowercase drive letter, while the rest of Windows APIs use an uppercase drive letter. Normalize the drive letter spelling to be uppercase. Fixes #1855 rdar://141001203
1 parent 8f9aaed commit 53df6b2

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,21 @@ public struct DocumentURI: Codable, Hashable, Sendable {
4848
/// fallback mode that drops semantic functionality.
4949
public var pseudoPath: String {
5050
if storage.isFileURL {
51-
return storage.withUnsafeFileSystemRepresentation {
52-
String(cString: $0!)
51+
return storage.withUnsafeFileSystemRepresentation { filePathPtr in
52+
guard let filePathPtr else {
53+
return ""
54+
}
55+
let filePath = String(cString: filePathPtr)
56+
#if os(Windows)
57+
// VS Code spells file paths with a lowercase drive letter, while the rest of Windows APIs use an uppercase
58+
// drive letter. Normalize the drive letter spelling to be uppercase.
59+
if filePath.first?.isASCII ?? false, filePath.first?.isLetter ?? false, filePath.first?.isLowercase ?? false,
60+
filePath.count > 1, filePath[filePath.index(filePath.startIndex, offsetBy: 1)] == ":"
61+
{
62+
return filePath.first!.uppercased() + filePath.dropFirst()
63+
}
64+
#endif
65+
return filePath
5366
}
5467
} else {
5568
return storage.absoluteString

Sources/SKTestSupport/SkipUnless.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,10 @@ public actor SkipUnless {
415415
try XCTSkipUnless(Platform.current == .darwin, message)
416416
}
417417

418+
public static func platformIsWindows(_ message: String) throws {
419+
try XCTSkipUnless(Platform.current == .windows, message)
420+
}
421+
418422
public static func platformSupportsTaskPriorityElevation() throws {
419423
#if os(macOS)
420424
guard #available(macOS 14.0, *) else {

Tests/SourceKitLSPTests/PullDiagnosticsTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,35 @@ final class PullDiagnosticsTests: XCTestCase {
4242
XCTAssertEqual(diagnostic.range, Position(line: 1, utf16index: 2)..<Position(line: 1, utf16index: 9))
4343
}
4444

45+
func testDiagnosticsIfFileIsOpenedWithLowercaseDriveLetter() async throws {
46+
try SkipUnless.platformIsWindows("Drive letters only exist on Windows")
47+
48+
let fileContents = """
49+
func foo() {
50+
invalid
51+
}
52+
"""
53+
54+
// We use `IndexedSingleSwiftFileTestProject` so that the test file exists on disk, which causes sourcekitd to
55+
// uppercase the drive letter.
56+
let project = try await IndexedSingleSwiftFileTestProject(fileContents, allowBuildFailure: true)
57+
project.testClient.send(DidCloseTextDocumentNotification(textDocument: TextDocumentIdentifier(project.fileURI)))
58+
59+
let filePath = try XCTUnwrap(project.fileURI.fileURL?.filePath)
60+
XCTAssertEqual(filePath[filePath.index(filePath.startIndex, offsetBy: 1)], ":")
61+
let lowercaseDriveLetterPath = filePath.first!.lowercased() + filePath.dropFirst()
62+
let uri = DocumentURI(filePath: lowercaseDriveLetterPath, isDirectory: false)
63+
project.testClient.openDocument(fileContents, uri: uri)
64+
65+
let report = try await project.testClient.send(
66+
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
67+
)
68+
69+
XCTAssertEqual(report.fullReport?.items.count, 1)
70+
let diagnostic = try XCTUnwrap(report.fullReport?.items.first)
71+
XCTAssertEqual(diagnostic.range, Position(line: 1, utf16index: 2)..<Position(line: 1, utf16index: 9))
72+
}
73+
4574
/// Test that we can get code actions for pulled diagnostics (https://github.com/swiftlang/sourcekit-lsp/issues/776)
4675
func testCodeActions() async throws {
4776
let testClient = try await TestSourceKitLSPClient(

0 commit comments

Comments
 (0)