Skip to content

Normalize Windows drive letter to be uppercase #1880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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