Skip to content

fix: URL path encoding on Windows: C: -> c%3A #2

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 3 commits into from
Jan 11, 2023
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
39 changes: 34 additions & 5 deletions uri.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"net/url"
"path/filepath"
"regexp"
"strings"
"unicode"

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

var expDriveID = regexp.MustCompile("^/[a-zA-Z]:")
// for example, `"/c:"` or `"/A:"`
var expDriveWithLeadingSlashID = regexp.MustCompile("^/[a-zA-Z]:")

// for example, `"C:"` or `"A:"`
var expUppercaseDriveID = regexp.MustCompile("^[A-Z]:")

// AsPath convert the DocumentURI to a paths.Path
func (uri DocumentURI) AsPath() *paths.Path {
Expand All @@ -39,12 +45,23 @@ func (uri DocumentURI) AsPath() *paths.Path {
// unbox convert the DocumentURI to a file path string
func (uri DocumentURI) unbox() string {
path := uri.url.Path
if expDriveID.MatchString(path) {
if expDriveWithLeadingSlashID.MatchString(path) {
return path[1:]
}
return path
}

// Converts `"C:"` to `"c:"` to be compatible with VS Code URI's drive letter casing
// https://github.com/Microsoft/vscode/issues/68325#issuecomment-462239992
func lowercaseDriveSegment(pathSegment string) string {
if expUppercaseDriveID.MatchString(pathSegment) {
chars := []rune(pathSegment)
chars[0] = unicode.ToLower(chars[0])
return string(chars)
}
return pathSegment
}

func (uri DocumentURI) String() string {
return uri.url.String()
}
Expand All @@ -68,11 +85,23 @@ func NewDocumentURI(path string) DocumentURI {
if len(path) == 0 || path[0] != '/' {
path = "/" + path
}
uri, err := NewDocumentURIFromURL("file://")
segments := strings.Split(path, "/")
encodedSegments := make([]string, len(segments))
for i, segment := range segments {
if len(segment) == 0 {
encodedSegments[i] = segment
} else {
segment = lowercaseDriveSegment(segment)
segment = url.QueryEscape(segment)
// Spaces must be turned into `%20`. Otherwise, `url.QueryEscape`` encodes them to `+`.
encodedSegments[i] = strings.ReplaceAll(segment, "+", "%20")
}
}
urlPath := strings.Join(encodedSegments, "/")
uri, err := NewDocumentURIFromURL("file://" + urlPath)
if err != nil {
panic(err)
}
uri.url.Path = path
return uri
}

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

newDocURI, err := NewDocumentURIFromURL(s)
Expand Down
6 changes: 4 additions & 2 deletions uri_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ func TestPathToUri(t *testing.T) {
toSlash = windowsToSlash // Emulate windows cases

d = NewDocumentURI("C:\\Users\\test\\Sketch.ino")
require.Equal(t, "file:///C:/Users/test/Sketch.ino", d.String())
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
d = NewDocumentURI("c:\\Users\\test\\Sketch.ino")
require.Equal(t, "file:///c%3A/Users/test/Sketch.ino", d.String())
d = NewDocumentURI("/Users/test/Sketch.ino")
require.Equal(t, "file:///Users/test/Sketch.ino", d.String())
d = NewDocumentURI("\U0001F61B")
Expand All @@ -70,7 +72,7 @@ func TestJSONMarshalUnmarshal(t *testing.T) {
d = NewDocumentURI("C:\\Users\\test\\Sketch.ino")
data, err := json.Marshal(d)
require.NoError(t, err)
require.Equal(t, `"file:///C:/Users/test/Sketch.ino"`, string(data))
require.Equal(t, `"file:///c%3A/Users/test/Sketch.ino"`, string(data))

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