Skip to content

Commit e0901f0

Browse files
authored
Merge pull request #1880 from ahoppen/normalize-drive-letter
Normalize Windows drive letter to be uppercase
2 parents ec461d6 + 03d58e8 commit e0901f0

File tree

4 files changed

+60
-8
lines changed

4 files changed

+60
-8
lines changed

Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift

+14-5
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,21 @@ public struct DocumentURI: Codable, Hashable, Sendable {
5656
/// fallback mode that drops semantic functionality.
5757
public var pseudoPath: String {
5858
if storage.isFileURL {
59-
return storage.withUnsafeFileSystemRepresentation { filePath in
60-
if let filePath {
61-
String(cString: filePath)
62-
} else {
63-
""
59+
return storage.withUnsafeFileSystemRepresentation { filePathPtr in
60+
guard let filePathPtr else {
61+
return ""
6462
}
63+
let filePath = String(cString: filePathPtr)
64+
#if os(Windows)
65+
// VS Code spells file paths with a lowercase drive letter, while the rest of Windows APIs use an uppercase
66+
// drive letter. Normalize the drive letter spelling to be uppercase.
67+
if filePath.first?.isASCII ?? false, filePath.first?.isLetter ?? false, filePath.first?.isLowercase ?? false,
68+
filePath.count > 1, filePath[filePath.index(filePath.startIndex, offsetBy: 1)] == ":"
69+
{
70+
return filePath.first!.uppercased() + filePath.dropFirst()
71+
}
72+
#endif
73+
return filePath
6574
}
6675
} else {
6776
return storage.absoluteString

Sources/SKTestSupport/SkipUnless.swift

+4
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ package actor SkipUnless {
418418
try XCTSkipUnless(Platform.current == .darwin, message)
419419
}
420420

421+
package static func platformIsWindows(_ message: String) throws {
422+
try XCTSkipUnless(Platform.current == .windows, message)
423+
}
424+
421425
package static func platformSupportsTaskPriorityElevation() throws {
422426
#if os(macOS)
423427
guard #available(macOS 14.0, *) else {

Sources/SwiftExtensions/URLExtensions.swift

+13-3
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,21 @@ extension URL {
6767
guard self.isFileURL else {
6868
throw FilePathError.noFileURL(self)
6969
}
70-
return try self.withUnsafeFileSystemRepresentation { buffer in
71-
guard let buffer else {
70+
return try self.withUnsafeFileSystemRepresentation { filePathPtr in
71+
guard let filePathPtr else {
7272
throw FilePathError.noFileSystemRepresentation(self)
7373
}
74-
return String(cString: buffer)
74+
let filePath = String(cString: filePathPtr)
75+
#if os(Windows)
76+
// VS Code spells file paths with a lowercase drive letter, while the rest of Windows APIs use an uppercase
77+
// drive letter. Normalize the drive letter spelling to be uppercase.
78+
if filePath.first?.isASCII ?? false, filePath.first?.isLetter ?? false, filePath.first?.isLowercase ?? false,
79+
filePath.count > 1, filePath[filePath.index(filePath.startIndex, offsetBy: 1)] == ":"
80+
{
81+
return filePath.first!.uppercased() + filePath.dropFirst()
82+
}
83+
#endif
84+
return filePath
7585
}
7686
}
7787
}

Tests/SourceKitLSPTests/PullDiagnosticsTests.swift

+29
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,35 @@ final class PullDiagnosticsTests: XCTestCase {
4848
XCTAssertEqual(diagnostic.range, Position(line: 1, utf16index: 2)..<Position(line: 1, utf16index: 9))
4949
}
5050

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

0 commit comments

Comments
 (0)