Skip to content

Commit 0f97832

Browse files
author
Akos Kitta
committed
fix: URL path encoding on Windows: C: -> c%3A
- Drive is lower-cased: `C:` -> `c:` Ref: microsoft/vscode#68325 Signed-off-by: Akos Kitta <[email protected]>
1 parent b7458c0 commit 0f97832

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

uri.go

+44-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"net/url"
1212
"path/filepath"
1313
"regexp"
14+
"strings"
15+
"unicode"
1416

1517
"github.com/arduino/go-paths-helper"
1618
"go.bug.st/json"
@@ -29,7 +31,11 @@ type DocumentURI struct {
2931
// NilURI is the empty DocumentURI
3032
var NilURI = DocumentURI{}
3133

32-
var expDriveID = regexp.MustCompile("^/[a-zA-Z]:")
34+
// for example, `"/c:"` or `"/A:"`
35+
var expDriveWithLeadingSlashID = regexp.MustCompile("^/[a-zA-Z]:")
36+
37+
// for example, `"C:"` or `"A:"`
38+
var expUppercaseDriveID = regexp.MustCompile("^[A-Z]:")
3339

3440
// AsPath convert the DocumentURI to a paths.Path
3541
func (uri DocumentURI) AsPath() *paths.Path {
@@ -39,12 +45,26 @@ func (uri DocumentURI) AsPath() *paths.Path {
3945
// unbox convert the DocumentURI to a file path string
4046
func (uri DocumentURI) unbox() string {
4147
path := uri.url.Path
42-
if expDriveID.MatchString(path) {
48+
if expDriveWithLeadingSlashID.MatchString(path) {
4349
return path[1:]
4450
}
4551
return path
4652
}
4753

54+
// Converts `"C:"` to `"c:"` to be compatible with VS Code URI's drive letter casing
55+
// https://github.com/Microsoft/vscode/issues/68325#issuecomment-462239992
56+
func lowercaseDriveSegment(pathSegment string) string {
57+
if len(pathSegment) == 0 {
58+
return pathSegment
59+
}
60+
if expUppercaseDriveID.MatchString(pathSegment) {
61+
chars := []rune(pathSegment)
62+
chars[0] = unicode.ToLower(chars[0])
63+
return string(chars)
64+
}
65+
return pathSegment
66+
}
67+
4868
func (uri DocumentURI) String() string {
4969
return uri.url.String()
5070
}
@@ -68,11 +88,30 @@ func NewDocumentURI(path string) DocumentURI {
6888
if len(path) == 0 || path[0] != '/' {
6989
path = "/" + path
7090
}
71-
uri, err := NewDocumentURIFromURL("file://")
91+
segments := strings.Split(path, "/")
92+
encodedSegments := make([]string, len(segments))
93+
for i, segment := range segments {
94+
if len(segment) == 0 {
95+
encodedSegments[i] = segment
96+
} else {
97+
segment = lowercaseDriveSegment(segment)
98+
chars := strings.SplitAfter(segment, "")
99+
for i, c := range chars {
100+
// Spaces must be turned into `%20`. Otherwise, `url.QueryEscape`` encodes them to `+`.
101+
if c == " " {
102+
chars[i] = "%20"
103+
} else {
104+
chars[i] = url.QueryEscape(c)
105+
}
106+
}
107+
encodedSegments[i] = strings.Join(chars, "")
108+
}
109+
}
110+
urlPath := strings.Join(encodedSegments, "/")
111+
uri, err := NewDocumentURIFromURL("file://" + urlPath)
72112
if err != nil {
73113
panic(err)
74114
}
75-
uri.url.Path = path
76115
return uri
77116
}
78117

@@ -89,7 +128,7 @@ func NewDocumentURIFromURL(inURL string) (DocumentURI, error) {
89128
func (uri *DocumentURI) UnmarshalJSON(data []byte) error {
90129
var s string
91130
if err := json.Unmarshal(data, &s); err != nil {
92-
return fmt.Errorf("expoected JSON string for DocumentURI: %s", err)
131+
return fmt.Errorf("expected JSON string for DocumentURI: %s", err)
93132
}
94133

95134
newDocURI, err := NewDocumentURIFromURL(s)

uri_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ func TestPathToUri(t *testing.T) {
4848
toSlash = windowsToSlash // Emulate windows cases
4949

5050
d = NewDocumentURI("C:\\Users\\test\\Sketch.ino")
51-
require.Equal(t, "file:///C:/Users/test/Sketch.ino", d.String())
51+
require.Equal(t, "file:///c%3A/Users/test/Sketch.ino", d.String()) // driver letter is converted to lower case https://github.com/Microsoft/vscode/issues/68325#issuecomment-462239992
52+
d = NewDocumentURI("c:\\Users\\test\\Sketch.ino")
53+
require.Equal(t, "file:///c%3A/Users/test/Sketch.ino", d.String())
5254
d = NewDocumentURI("/Users/test/Sketch.ino")
5355
require.Equal(t, "file:///Users/test/Sketch.ino", d.String())
5456
d = NewDocumentURI("\U0001F61B")
@@ -70,7 +72,7 @@ func TestJSONMarshalUnmarshal(t *testing.T) {
7072
d = NewDocumentURI("C:\\Users\\test\\Sketch.ino")
7173
data, err := json.Marshal(d)
7274
require.NoError(t, err)
73-
require.Equal(t, `"file:///C:/Users/test/Sketch.ino"`, string(data))
75+
require.Equal(t, `"file:///c%3A/Users/test/Sketch.ino"`, string(data))
7476

7577
d = NewDocumentURI("/Users/test/Sketch.ino")
7678
data, err = json.Marshal(d)

0 commit comments

Comments
 (0)