Skip to content

Commit f264427

Browse files
authored
Merge pull request #2 from kittaakos/win-url-path-segment-encoding
fix: URL path encoding on Windows: `C:` -> `c%3A`
2 parents b7458c0 + ce73b88 commit f264427

File tree

2 files changed

+38
-7
lines changed

2 files changed

+38
-7
lines changed

uri.go

+34-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,23 @@ 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 expUppercaseDriveID.MatchString(pathSegment) {
58+
chars := []rune(pathSegment)
59+
chars[0] = unicode.ToLower(chars[0])
60+
return string(chars)
61+
}
62+
return pathSegment
63+
}
64+
4865
func (uri DocumentURI) String() string {
4966
return uri.url.String()
5067
}
@@ -68,11 +85,23 @@ func NewDocumentURI(path string) DocumentURI {
6885
if len(path) == 0 || path[0] != '/' {
6986
path = "/" + path
7087
}
71-
uri, err := NewDocumentURIFromURL("file://")
88+
segments := strings.Split(path, "/")
89+
encodedSegments := make([]string, len(segments))
90+
for i, segment := range segments {
91+
if len(segment) == 0 {
92+
encodedSegments[i] = segment
93+
} else {
94+
segment = lowercaseDriveSegment(segment)
95+
segment = url.QueryEscape(segment)
96+
// Spaces must be turned into `%20`. Otherwise, `url.QueryEscape`` encodes them to `+`.
97+
encodedSegments[i] = strings.ReplaceAll(segment, "+", "%20")
98+
}
99+
}
100+
urlPath := strings.Join(encodedSegments, "/")
101+
uri, err := NewDocumentURIFromURL("file://" + urlPath)
72102
if err != nil {
73103
panic(err)
74104
}
75-
uri.url.Path = path
76105
return uri
77106
}
78107

@@ -89,7 +118,7 @@ func NewDocumentURIFromURL(inURL string) (DocumentURI, error) {
89118
func (uri *DocumentURI) UnmarshalJSON(data []byte) error {
90119
var s string
91120
if err := json.Unmarshal(data, &s); err != nil {
92-
return fmt.Errorf("expoected JSON string for DocumentURI: %s", err)
121+
return fmt.Errorf("expected JSON string for DocumentURI: %s", err)
93122
}
94123

95124
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)