Skip to content

Commit

Permalink
Normalize Windows drive letter to be uppercase
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ahoppen committed Dec 11, 2024
1 parent e7f9530 commit 03d58e8
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 8 deletions.
19 changes: 14 additions & 5 deletions Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,21 @@ public struct DocumentURI: Codable, Hashable, Sendable {
/// fallback mode that drops semantic functionality.
public var pseudoPath: String {
if storage.isFileURL {
return storage.withUnsafeFileSystemRepresentation { filePath in
if let filePath {
String(cString: filePath)
} else {
""
return storage.withUnsafeFileSystemRepresentation { filePathPtr in
guard let filePathPtr else {
return ""
}
let filePath = String(cString: filePathPtr)
#if os(Windows)
// 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.
if filePath.first?.isASCII ?? false, filePath.first?.isLetter ?? false, filePath.first?.isLowercase ?? false,
filePath.count > 1, filePath[filePath.index(filePath.startIndex, offsetBy: 1)] == ":"
{
return filePath.first!.uppercased() + filePath.dropFirst()
}
#endif
return filePath
}
} else {
return storage.absoluteString
Expand Down
4 changes: 4 additions & 0 deletions Sources/SKTestSupport/SkipUnless.swift
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,10 @@ package actor SkipUnless {
try XCTSkipUnless(Platform.current == .darwin, message)
}

package static func platformIsWindows(_ message: String) throws {
try XCTSkipUnless(Platform.current == .windows, message)
}

package static func platformSupportsTaskPriorityElevation() throws {
#if os(macOS)
guard #available(macOS 14.0, *) else {
Expand Down
16 changes: 13 additions & 3 deletions Sources/SwiftExtensions/URLExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,21 @@ extension URL {
guard self.isFileURL else {
throw FilePathError.noFileURL(self)
}
return try self.withUnsafeFileSystemRepresentation { buffer in
guard let buffer else {
return try self.withUnsafeFileSystemRepresentation { filePathPtr in
guard let filePathPtr else {
throw FilePathError.noFileSystemRepresentation(self)
}
return String(cString: buffer)
let filePath = String(cString: filePathPtr)
#if os(Windows)
// 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.
if filePath.first?.isASCII ?? false, filePath.first?.isLetter ?? false, filePath.first?.isLowercase ?? false,
filePath.count > 1, filePath[filePath.index(filePath.startIndex, offsetBy: 1)] == ":"
{
return filePath.first!.uppercased() + filePath.dropFirst()
}
#endif
return filePath
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions Tests/SourceKitLSPTests/PullDiagnosticsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,35 @@ final class PullDiagnosticsTests: XCTestCase {
XCTAssertEqual(diagnostic.range, Position(line: 1, utf16index: 2)..<Position(line: 1, utf16index: 9))
}

func testDiagnosticsIfFileIsOpenedWithLowercaseDriveLetter() async throws {
try SkipUnless.platformIsWindows("Drive letters only exist on Windows")

let fileContents = """
func foo() {
invalid
}
"""

// We use `IndexedSingleSwiftFileTestProject` so that the test file exists on disk, which causes sourcekitd to
// uppercase the drive letter.
let project = try await IndexedSingleSwiftFileTestProject(fileContents, allowBuildFailure: true)
project.testClient.send(DidCloseTextDocumentNotification(textDocument: TextDocumentIdentifier(project.fileURI)))

let filePath = try XCTUnwrap(project.fileURI.fileURL?.filePath)
XCTAssertEqual(filePath[filePath.index(filePath.startIndex, offsetBy: 1)], ":")
let lowercaseDriveLetterPath = filePath.first!.lowercased() + filePath.dropFirst()
let uri = DocumentURI(filePath: lowercaseDriveLetterPath, isDirectory: false)
project.testClient.openDocument(fileContents, uri: uri)

let report = try await project.testClient.send(
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
)

XCTAssertEqual(report.fullReport?.items.count, 1)
let diagnostic = try XCTUnwrap(report.fullReport?.items.first)
XCTAssertEqual(diagnostic.range, Position(line: 1, utf16index: 2)..<Position(line: 1, utf16index: 9))
}

/// Test that we can get code actions for pulled diagnostics (https://github.com/swiftlang/sourcekit-lsp/issues/776)
func testCodeActions() async throws {
let testClient = try await TestSourceKitLSPClient(
Expand Down

0 comments on commit 03d58e8

Please sign in to comment.