Skip to content

Commit 15b81ad

Browse files
committed
Restructured DocumentURI handling
1 parent f88db97 commit 15b81ad

File tree

4 files changed

+133
-76
lines changed

4 files changed

+133
-76
lines changed

Diff for: handler/handler.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ func (handler *InoHandler) refreshCppDocumentSymbols() error {
453453
if err != nil {
454454
return errors.WithMessage(err, "quering source code symbols")
455455
}
456-
result = handler.transformClangdResult("textDocument/documentSymbol", cppURI, "", result)
456+
result = handler.transformClangdResult("textDocument/documentSymbol", cppURI, lsp.NilURI, result)
457457
if symbols, ok := result.([]lsp.DocumentSymbol); !ok {
458458
return errors.WithMessage(err, "quering source code symbols (2)")
459459
} else {
@@ -748,7 +748,7 @@ func (handler *InoHandler) ino2cppDocumentURI(inoURI lsp.DocumentURI) (lsp.Docum
748748
inside, err := inoPath.IsInsideDir(handler.sketchRoot)
749749
if err != nil {
750750
log.Printf(" could not determine if '%s' is inside '%s'", inoPath, handler.sketchRoot)
751-
return "", unknownURI(inoURI)
751+
return lsp.NilURI, unknownURI(inoURI)
752752
}
753753
if !inside {
754754
log.Printf(" passing doc identifier to '%s' as-is", inoPath)
@@ -763,7 +763,7 @@ func (handler *InoHandler) ino2cppDocumentURI(inoURI lsp.DocumentURI) (lsp.Docum
763763
}
764764

765765
log.Printf(" could not determine rel-path of '%s' in '%s': %s", inoPath, handler.sketchRoot, err)
766-
return "", err
766+
return lsp.NilURI, err
767767
}
768768

769769
func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) {
@@ -791,7 +791,7 @@ func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange l
791791
inside, err := cppPath.IsInsideDir(handler.buildSketchRoot)
792792
if err != nil {
793793
log.Printf(" could not determine if '%s' is inside '%s'", cppPath, handler.buildSketchRoot)
794-
return "", lsp.Range{}, err
794+
return lsp.NilURI, lsp.Range{}, err
795795
}
796796
if !inside {
797797
log.Printf(" keep doc identifier to '%s' as-is", cppPath)
@@ -806,7 +806,7 @@ func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange l
806806
}
807807

808808
log.Printf(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err)
809-
return "", lsp.Range{}, err
809+
return lsp.NilURI, lsp.Range{}, err
810810
}
811811

812812
func (handler *InoHandler) ino2cppTextDocumentPositionParams(inoParams *lsp.TextDocumentPositionParams) (*lsp.TextDocumentPositionParams, error) {
@@ -833,7 +833,7 @@ func (handler *InoHandler) ino2cppTextDocumentPositionParams(inoParams *lsp.Text
833833
func (handler *InoHandler) ino2cppRange(inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) {
834834
cppURI, err := handler.ino2cppDocumentURI(inoURI)
835835
if err != nil {
836-
return "", lsp.Range{}, err
836+
return lsp.NilURI, lsp.Range{}, err
837837
}
838838
if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) {
839839
cppRange := handler.sketchMapper.InoToCppLSPRange(inoURI, inoRange)
@@ -919,7 +919,7 @@ func (handler *InoHandler) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *ls
919919
}
920920

921921
func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI lsp.DocumentURI, result interface{}) interface{} {
922-
cppToIno := inoURI != "" && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp)
922+
cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp)
923923

924924
switch r := result.(type) {
925925
case *lsp.Hover:
@@ -1423,5 +1423,5 @@ func (handler *InoHandler) showMessage(ctx context.Context, msgType lsp.MessageT
14231423
}
14241424

14251425
func unknownURI(uri lsp.DocumentURI) error {
1426-
return errors.New("Document is not available: " + string(uri))
1426+
return errors.New("Document is not available: " + uri.String())
14271427
}

Diff for: lsp/service.go

-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ type InitializeParams struct {
2727

2828
type InitializedParams struct{}
2929

30-
type DocumentURI string
31-
3230
type ClientInfo struct {
3331
Name string `json:"name,omitempty"`
3432
Version string `json:"version,omitempty"`

Diff for: lsp/uri.go

+53-28
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
package lsp
22

33
import (
4+
"encoding/json"
45
"net/url"
56
"path/filepath"
67
"regexp"
7-
"runtime"
8-
"strings"
98

109
"github.com/arduino/go-paths-helper"
10+
"github.com/pkg/errors"
1111
)
1212

13-
var expDriveID = regexp.MustCompile("[a-zA-Z]:")
13+
type DocumentURI struct {
14+
url url.URL
15+
}
16+
17+
// NilURI is the empty DocumentURI
18+
var NilURI = DocumentURI{}
19+
20+
var expDriveID = regexp.MustCompile("^/[a-zA-Z]:")
1421

1522
// AsPath convert the DocumentURI to a paths.Path
1623
func (uri DocumentURI) AsPath() *paths.Path {
@@ -19,51 +26,69 @@ func (uri DocumentURI) AsPath() *paths.Path {
1926

2027
// Unbox convert the DocumentURI to a file path string
2128
func (uri DocumentURI) Unbox() string {
22-
urlObj, err := url.Parse(string(uri))
23-
if err != nil {
24-
return string(uri)
25-
}
26-
path := ""
27-
segments := strings.Split(urlObj.Path, "/")
28-
for _, segment := range segments {
29-
decoded, err := url.PathUnescape(segment)
30-
if err != nil {
31-
decoded = segment
32-
}
33-
if runtime.GOOS == "windows" && expDriveID.MatchString(decoded) {
34-
path += strings.ToUpper(decoded)
35-
} else if len(decoded) > 0 {
36-
path += string(filepath.Separator) + decoded
37-
}
29+
path := uri.url.Path
30+
if expDriveID.MatchString(path) {
31+
return path[1:]
3832
}
3933
return path
4034
}
4135

4236
func (uri DocumentURI) String() string {
43-
return string(uri)
37+
return uri.url.String()
4438
}
4539

4640
// Ext returns the extension of the file pointed by the URI
4741
func (uri DocumentURI) Ext() string {
48-
return filepath.Ext(string(uri))
42+
return filepath.Ext(uri.Unbox())
4943
}
5044

5145
// NewDocumentURIFromPath create a DocumentURI from the given Path object
5246
func NewDocumentURIFromPath(path *paths.Path) DocumentURI {
5347
return NewDocumentURI(path.String())
5448
}
5549

50+
var toSlash = filepath.ToSlash
51+
5652
// NewDocumentURI create a DocumentURI from the given string path
5753
func NewDocumentURI(path string) DocumentURI {
58-
urlObj, err := url.Parse("file://")
54+
// tranform path into URI
55+
path = toSlash(path)
56+
if len(path) == 0 || path[0] != '/' {
57+
path = "/" + path
58+
}
59+
uri, err := NewDocumentURIFromURL("file://" + path)
5960
if err != nil {
6061
panic(err)
6162
}
62-
segments := strings.Split(path, string(filepath.Separator))
63-
for _, segment := range segments {
64-
if len(segment) > 0 {
65-
urlObj.Path += "/" + url.PathEscape(segment)
66-
}
63+
return uri
64+
}
65+
66+
// NewDocumentURIFromURL converts an URL into a DocumentURI
67+
func NewDocumentURIFromURL(inURL string) (DocumentURI, error) {
68+
uri, err := url.Parse(inURL)
69+
if err != nil {
70+
return NilURI, err
6771
}
68-
return DocumentURI(urlObj.String())
72+
return DocumentURI{url: *uri}, nil
73+
}
74+
75+
// UnmarshalJSON implements json.Unmarshaller interface
76+
func (uri *DocumentURI) UnmarshalJSON(data []byte) error {
77+
var s string
78+
if err := json.Unmarshal(data, &s); err != nil {
79+
return errors.WithMessage(err, "expoected JSON string for DocumentURI")
80+
}
81+
82+
newDocURI, err := NewDocumentURIFromURL(s)
83+
if err != nil {
84+
return errors.WithMessage(err, "parsing DocumentURI")
85+
}
86+
87+
*uri = newDocURI
88+
return nil
89+
}
90+
91+
// MarshalJSON implements json.Marshaller interface
92+
func (uri DocumentURI) MarshalJSON() ([]byte, error) {
93+
return json.Marshal(uri.url.String())
6994
}

Diff for: lsp/uri_test.go

+72-38
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,83 @@
11
package lsp
22

33
import (
4-
"path/filepath"
5-
"runtime"
4+
"encoding/json"
5+
"strings"
66
"testing"
7+
8+
"github.com/stretchr/testify/require"
79
)
810

911
func TestUriToPath(t *testing.T) {
10-
var path string
11-
if runtime.GOOS == "windows" {
12-
path = DocumentURI("file:///C:/Users/test/Sketch.ino").Unbox()
13-
if path != "C:\\Users\\test\\Sketch.ino" {
14-
t.Error(path)
15-
}
16-
path = DocumentURI("file:///c%3A/Users/test/Sketch.ino").Unbox()
17-
if path != "C:\\Users\\test\\Sketch.ino" {
18-
t.Error(path)
19-
}
20-
} else {
21-
path = DocumentURI("file:///Users/test/Sketch.ino").Unbox()
22-
if path != "/Users/test/Sketch.ino" {
23-
t.Error(path)
24-
}
25-
}
26-
path = DocumentURI("file:///%25F0%259F%2598%259B").Unbox()
27-
if path != string(filepath.Separator)+"\U0001F61B" {
28-
t.Error(path)
29-
}
12+
d, err := NewDocumentURIFromURL("file:///C:/Users/test/Sketch.ino")
13+
require.NoError(t, err)
14+
require.Equal(t, "C:/Users/test/Sketch.ino", d.Unbox())
15+
16+
d, err = NewDocumentURIFromURL("file:///c%3A/Users/test/Sketch.ino")
17+
require.NoError(t, err)
18+
require.Equal(t, "c:/Users/test/Sketch.ino", d.Unbox())
19+
20+
d, err = NewDocumentURIFromURL("file:///Users/test/Sketch.ino")
21+
require.NoError(t, err)
22+
require.Equal(t, "/Users/test/Sketch.ino", d.Unbox())
23+
24+
d, err = NewDocumentURIFromURL("file:///c%3A/Users/USERNA~1/AppData/Local/Temp/.arduinoProIDE-unsaved202108-10416-j28c17.lru6k/sketch_jan8a/sketch_jan8a.ino")
25+
require.NoError(t, err)
26+
require.Equal(t, "c:/Users/USERNA~1/AppData/Local/Temp/.arduinoProIDE-unsaved202108-10416-j28c17.lru6k/sketch_jan8a/sketch_jan8a.ino", d.Unbox())
27+
28+
d, err = NewDocumentURIFromURL("file:///%F0%9F%98%9B")
29+
require.NoError(t, err)
30+
require.Equal(t, "/\U0001F61B", d.Unbox())
3031
}
3132

3233
func TestPathToUri(t *testing.T) {
33-
var uri DocumentURI
34-
if runtime.GOOS == "windows" {
35-
uri = NewDocumentURI("C:\\Users\\test\\Sketch.ino")
36-
if uri != "file:///C:/Users/test/Sketch.ino" {
37-
t.Error(uri)
38-
}
39-
} else {
40-
uri = NewDocumentURI("/Users/test/Sketch.ino")
41-
if uri != "file:///Users/test/Sketch.ino" {
42-
t.Error(uri)
43-
}
44-
}
45-
uri = NewDocumentURI("\U0001F61B")
46-
if uri != "file:///%25F0%259F%2598%259B" {
47-
t.Error(uri)
48-
}
34+
toSlash = windowsToSlash // Emulate windows cases
35+
36+
d := NewDocumentURI("C:\\Users\\test\\Sketch.ino")
37+
require.Equal(t, "file:///C:/Users/test/Sketch.ino", d.String())
38+
d = NewDocumentURI("/Users/test/Sketch.ino")
39+
require.Equal(t, "file:///Users/test/Sketch.ino", d.String())
40+
d = NewDocumentURI("\U0001F61B")
41+
require.Equal(t, "file:///%F0%9F%98%9B", d.String())
42+
}
43+
44+
func TestJSONMarshalUnmarshal(t *testing.T) {
45+
toSlash = windowsToSlash // Emulate windows cases
46+
47+
var d DocumentURI
48+
err := json.Unmarshal([]byte(`"file:///Users/test/Sketch.ino"`), &d)
49+
require.NoError(t, err)
50+
require.Equal(t, "/Users/test/Sketch.ino", d.Unbox())
51+
52+
err = json.Unmarshal([]byte(`"file:///%F0%9F%98%9B"`), &d)
53+
require.NoError(t, err)
54+
require.Equal(t, "/\U0001F61B", d.Unbox())
55+
56+
d = NewDocumentURI("C:\\Users\\test\\Sketch.ino")
57+
data, err := json.Marshal(d)
58+
require.NoError(t, err)
59+
require.Equal(t, `"file:///C:/Users/test/Sketch.ino"`, string(data))
60+
61+
d = NewDocumentURI("/Users/test/Sketch.ino")
62+
data, err = json.Marshal(d)
63+
require.NoError(t, err)
64+
require.Equal(t, `"file:///Users/test/Sketch.ino"`, string(data))
65+
66+
d = NewDocumentURI("/User nàmé/test/Sketch.ino")
67+
data, err = json.Marshal(d)
68+
require.NoError(t, err)
69+
require.Equal(t, `"file:///User%20n%C3%A0m%C3%A9/test/Sketch.ino"`, string(data))
70+
71+
d = NewDocumentURI("\U0001F61B")
72+
data, err = json.Marshal(d)
73+
require.NoError(t, err)
74+
require.Equal(t, `"file:///%F0%9F%98%9B"`, string(data))
75+
}
76+
77+
func windowsToSlash(path string) string {
78+
return strings.ReplaceAll(path, `\`, "/")
79+
}
80+
81+
func windowsFromSlash(path string) string {
82+
return strings.ReplaceAll(path, "/", `\`)
4983
}

0 commit comments

Comments
 (0)