Skip to content

Commit 998631f

Browse files
committed
fix(lsp): handle Windows file URIs correctly
On Windows, file URIs must use the format file:///C:/path (with three slashes and forward slashes). The URL parser produces paths like /C:/path which need the leading slash stripped to be valid Windows paths. - Use url.URL to construct proper file:// URIs in tests - Handle leading slash before drive letter when parsing Windows file URIs - Normalize path separators for cross-platform compatibility
1 parent e818cb4 commit 998631f

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

internal/lsp/server.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@ func (s *Server) uriToRepoPath(uri string) (string, error) {
290290
}
291291

292292
absPath := parsed.Path
293+
// On Windows, file URIs like file:///C:/path produce a path like "/C:/path".
294+
// We need to strip the leading slash to get a valid Windows path.
295+
if len(absPath) >= 3 && absPath[0] == '/' && absPath[2] == ':' {
296+
absPath = absPath[1:]
297+
}
298+
// Convert forward slashes to the native path separator
299+
absPath = filepath.FromSlash(absPath)
293300

294301
gitRoot, err := getGitRoot()
295302
if err != nil {
@@ -301,7 +308,8 @@ func (s *Server) uriToRepoPath(uri string) (string, error) {
301308
return "", errors.Wrap(err, "failed to compute relative path")
302309
}
303310

304-
return relPath, nil
311+
// Ensure we always return forward slashes for consistency
312+
return filepath.ToSlash(relPath), nil
305313
}
306314

307315
func getGitRoot() (string, error) {

internal/lsp/server_test.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package lsp
22

33
import (
44
"context"
5+
"net/url"
56
"os"
67
"path/filepath"
78
"testing"
@@ -12,6 +13,16 @@ import (
1213
"github.com/stretchr/testify/require"
1314
)
1415

16+
// pathToFileURI converts a file path to a proper file:// URI that works on all platforms.
17+
// On Windows, this properly handles drive letters (e.g., C:\path -> file:///C:/path).
18+
func pathToFileURI(path string) string {
19+
u := url.URL{
20+
Scheme: "file",
21+
Path: filepath.ToSlash(path),
22+
}
23+
return u.String()
24+
}
25+
1526
func TestUriToRepoPath(t *testing.T) {
1627
gitRoot, err := getGitRoot()
1728
require.NoError(t, err)
@@ -23,17 +34,17 @@ func TestUriToRepoPath(t *testing.T) {
2334
}{
2435
{
2536
name: "simple file URI",
26-
uri: "file://" + filepath.Join(gitRoot, "cmd/src/main.go"),
37+
uri: pathToFileURI(filepath.Join(gitRoot, "cmd/src/main.go")),
2738
wantPath: "cmd/src/main.go",
2839
},
2940
{
3041
name: "nested path",
31-
uri: "file://" + filepath.Join(gitRoot, "internal/lsp/server.go"),
42+
uri: pathToFileURI(filepath.Join(gitRoot, "internal/lsp/server.go")),
3243
wantPath: "internal/lsp/server.go",
3344
},
3445
{
3546
name: "root file",
36-
uri: "file://" + filepath.Join(gitRoot, "go.mod"),
47+
uri: pathToFileURI(filepath.Join(gitRoot, "go.mod")),
3748
wantPath: "go.mod",
3849
},
3950
}
@@ -266,7 +277,7 @@ func TestHandleTextDocumentDocumentHighlight(t *testing.T) {
266277
commit: "abc123",
267278
}
268279

269-
uri := "file://" + filepath.Join(gitRoot, tt.path)
280+
uri := pathToFileURI(filepath.Join(gitRoot, tt.path))
270281
params := &protocol.DocumentHighlightParams{
271282
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
272283
TextDocument: protocol.TextDocumentIdentifier{URI: uri},

0 commit comments

Comments
 (0)