Skip to content

Commit 7e45b5c

Browse files
committed
Use canonical form for files when dealing with URI
This should avoid some weird cases on Windows when sometimes short path in the DOS8.3 form are mixed with the longer counterpart.
1 parent 79df7c1 commit 7e45b5c

File tree

8 files changed

+84
-39
lines changed

8 files changed

+84
-39
lines changed

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.12
44

55
require (
66
github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2
7-
github.com/arduino/go-paths-helper v1.4.0
7+
github.com/arduino/go-paths-helper v1.5.0
88
github.com/pkg/errors v0.9.1
99
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37
1010
github.com/stretchr/testify v1.6.1

Diff for: go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3
1717
github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
1818
github.com/arduino/go-paths-helper v1.4.0 h1:ilnseAdxmN1bFnLxxXHRtcdmt9jBf3O4jtYfWfqule4=
1919
github.com/arduino/go-paths-helper v1.4.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU=
20+
github.com/arduino/go-paths-helper v1.5.0 h1:RVo189hD+GhUS1rQ3gixwK1nSbvVR8MGIGa7Gxv2bdM=
21+
github.com/arduino/go-paths-helper v1.5.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU=
2022
github.com/arduino/go-properties-orderedmap v1.3.0 h1:4No/vQopB36e7WUIk6H6TxiSEJPiMrVOCZylYmua39o=
2123
github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk=
2224
github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4=

Diff for: handler/builder.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func (handler *InoHandler) generateBuildEnvironment() (*paths.Path, error) {
112112
}
113113
data := overridesFile{Overrides: map[string]string{}}
114114
for uri, trackedFile := range handler.docs {
115-
rel, err := uri.AsPath().RelFrom(handler.sketchRoot)
115+
rel, err := paths.New(uri).RelFrom(handler.sketchRoot)
116116
if err != nil {
117117
return nil, errors.WithMessage(err, "dumping tracked files")
118118
}

Diff for: handler/handler.go

+57-32
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ type InoHandler struct {
5858
sketchName string
5959
sketchMapper *sourcemapper.InoMapper
6060
sketchTrackedFilesCount int
61-
docs map[lsp.DocumentURI]*lsp.TextDocumentItem
62-
inoDocsWithDiagnostics map[lsp.DocumentURI]bool
61+
docs map[string]*lsp.TextDocumentItem
62+
inoDocsWithDiagnostics map[string]bool
6363

6464
config lsp.BoardConfig
6565
synchronizer Synchronizer
@@ -68,8 +68,8 @@ type InoHandler struct {
6868
// NewInoHandler creates and configures an InoHandler.
6969
func NewInoHandler(stdio io.ReadWriteCloser, board lsp.Board) *InoHandler {
7070
handler := &InoHandler{
71-
docs: map[lsp.DocumentURI]*lsp.TextDocumentItem{},
72-
inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{},
71+
docs: map[string]*lsp.TextDocumentItem{},
72+
inoDocsWithDiagnostics: map[string]bool{},
7373
config: lsp.BoardConfig{
7474
SelectedBoard: board,
7575
},
@@ -542,7 +542,7 @@ func startClangd(compileCommandsDir, sketchCpp *paths.Path) (io.WriteCloser, io.
542542
func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (*lsp.DidOpenTextDocumentParams, error) {
543543
// Add the TextDocumentItem in the tracked files list
544544
inoItem := inoDidOpen.TextDocument
545-
handler.docs[inoItem.URI] = &inoItem
545+
handler.docs[inoItem.URI.Canonical()] = &inoItem
546546

547547
// If we are tracking a .ino...
548548
if inoItem.URI.Ext() == ".ino" {
@@ -566,8 +566,8 @@ func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (*
566566

567567
func (handler *InoHandler) didClose(inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) {
568568
inoIdentifier := inoDidClose.TextDocument
569-
if _, exist := handler.docs[inoIdentifier.URI]; exist {
570-
delete(handler.docs, inoIdentifier.URI)
569+
if _, exist := handler.docs[inoIdentifier.URI.Canonical()]; exist {
570+
delete(handler.docs, inoIdentifier.URI.Canonical())
571571
} else {
572572
log.Printf(" didClose of untracked document: %s", inoIdentifier.URI)
573573
return nil, unknownURI(inoIdentifier.URI)
@@ -602,8 +602,8 @@ func (handler *InoHandler) ino2cppTextDocumentItem(inoItem lsp.TextDocumentItem)
602602
cppItem.Text = handler.sketchMapper.CppText.Text
603603
cppItem.Version = handler.sketchMapper.CppText.Version
604604
} else {
605-
cppItem.Text = handler.docs[inoItem.URI].Text
606-
cppItem.Version = handler.docs[inoItem.URI].Version
605+
cppItem.Text = handler.docs[inoItem.URI.Canonical()].Text
606+
cppItem.Version = handler.docs[inoItem.URI.Canonical()].Version
607607
}
608608

609609
return cppItem, nil
@@ -612,7 +612,7 @@ func (handler *InoHandler) ino2cppTextDocumentItem(inoItem lsp.TextDocumentItem)
612612
func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) {
613613
doc := req.TextDocument
614614

615-
trackedDoc, ok := handler.docs[doc.URI]
615+
trackedDoc, ok := handler.docs[doc.URI.Canonical()]
616616
if !ok {
617617
return nil, unknownURI(doc.URI)
618618
}
@@ -766,6 +766,23 @@ func (handler *InoHandler) ino2cppDocumentURI(inoURI lsp.DocumentURI) (lsp.Docum
766766
return lsp.NilURI, err
767767
}
768768

769+
func (handler *InoHandler) inoDocumentURIFromInoPath(inoPath string) (lsp.DocumentURI, error) {
770+
if inoPath == sourcemapper.NotIno.File {
771+
return sourcemapper.NotInoURI, nil
772+
}
773+
doc, ok := handler.docs[inoPath]
774+
if !ok {
775+
log.Printf(" !!! Unresolved .ino path: %s", inoPath)
776+
log.Printf(" !!! Known doc paths are:")
777+
for p := range handler.docs {
778+
log.Printf(" !!! > %s", p)
779+
}
780+
uri := lsp.NewDocumentURI(inoPath)
781+
return uri, unknownURI(uri)
782+
}
783+
return doc.URI, nil
784+
}
785+
769786
func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) {
770787
// Sketchbook/Sketch/Sketch.ino <- build-path/sketch/Sketch.ino.cpp
771788
// Sketchbook/Sketch/AnotherTab.ino <- build-path/sketch/Sketch.ino.cpp (different section from above)
@@ -784,16 +801,19 @@ func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange l
784801
} else {
785802
log.Printf(" URI: ERROR: %s", err)
786803
handler.sketchMapper.DebugLogAll()
804+
return lsp.NilURI, lsp.NilRange, err
787805
}
788-
return lsp.NewDocumentURI(inoPath), inoRange, err
806+
inoURI, err := handler.inoDocumentURIFromInoPath(inoPath)
807+
return inoURI, inoRange, err
789808
}
790809

791810
inside, err := cppPath.IsInsideDir(handler.buildSketchRoot)
792811
if err != nil {
793812
log.Printf(" could not determine if '%s' is inside '%s'", cppPath, handler.buildSketchRoot)
794-
return lsp.NilURI, lsp.Range{}, err
813+
return lsp.NilURI, lsp.NilRange, err
795814
}
796815
if !inside {
816+
log.Printf(" '%s' is not inside '%s'", cppPath, handler.buildSketchRoot)
797817
log.Printf(" keep doc identifier to '%s' as-is", cppPath)
798818
return cppURI, cppRange, nil
799819
}
@@ -806,7 +826,7 @@ func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange l
806826
}
807827

808828
log.Printf(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err)
809-
return lsp.NilURI, lsp.Range{}, err
829+
return lsp.NilURI, lsp.NilRange, err
810830
}
811831

812832
func (handler *InoHandler) ino2cppTextDocumentPositionParams(inoParams *lsp.TextDocumentPositionParams) (*lsp.TextDocumentPositionParams, error) {
@@ -1111,36 +1131,41 @@ func (handler *InoHandler) Cpp2InoCommand(command *lsp.Command) *lsp.Command {
11111131
return inoCommand
11121132
}
11131133

1114-
func (handler *InoHandler) cpp2inoWorkspaceEdit(origWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit {
1115-
if origWorkspaceEdit == nil {
1134+
func (handler *InoHandler) cpp2inoWorkspaceEdit(cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit {
1135+
if cppWorkspaceEdit == nil {
11161136
return nil
11171137
}
1118-
resWorkspaceEdit := &lsp.WorkspaceEdit{
1138+
inoWorkspaceEdit := &lsp.WorkspaceEdit{
11191139
Changes: map[lsp.DocumentURI][]lsp.TextEdit{},
11201140
}
1121-
for editURI, edits := range origWorkspaceEdit.Changes {
1141+
for editURI, edits := range cppWorkspaceEdit.Changes {
11221142
// if the edits are not relative to sketch file...
11231143
if !editURI.AsPath().EquivalentTo(handler.buildSketchCpp) {
11241144
// ...pass them through...
1125-
resWorkspaceEdit.Changes[editURI] = edits
1145+
inoWorkspaceEdit.Changes[editURI] = edits
11261146
continue
11271147
}
11281148

11291149
// ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits
11301150
for _, edit := range edits {
1131-
cppRange := edit.Range
1132-
inoFile, inoRange := handler.sketchMapper.CppToInoRange(cppRange)
1133-
inoURI := lsp.NewDocumentURI(inoFile)
1134-
if _, have := resWorkspaceEdit.Changes[inoURI]; !have {
1135-
resWorkspaceEdit.Changes[inoURI] = []lsp.TextEdit{}
1151+
inoURI, inoRange, err := handler.cpp2inoDocumentURI(editURI, edit.Range)
1152+
if err != nil {
1153+
log.Printf(" error converting edit %s:%s: %s", editURI, edit.Range, err)
1154+
continue
1155+
}
1156+
//inoFile, inoRange := handler.sketchMapper.CppToInoRange(edit.Range)
1157+
//inoURI := lsp.NewDocumentURI(inoFile)
1158+
if _, have := inoWorkspaceEdit.Changes[inoURI]; !have {
1159+
inoWorkspaceEdit.Changes[inoURI] = []lsp.TextEdit{}
11361160
}
1137-
resWorkspaceEdit.Changes[inoURI] = append(resWorkspaceEdit.Changes[inoURI], lsp.TextEdit{
1161+
inoWorkspaceEdit.Changes[inoURI] = append(inoWorkspaceEdit.Changes[inoURI], lsp.TextEdit{
11381162
NewText: edit.NewText,
11391163
Range: inoRange,
11401164
})
11411165
}
11421166
}
1143-
return resWorkspaceEdit
1167+
log.Printf(" done converting workspaceEdit")
1168+
return inoWorkspaceEdit
11441169
}
11451170

11461171
func (handler *InoHandler) cpp2inoLocation(cppLocation lsp.Location) (lsp.Location, error) {
@@ -1260,7 +1285,7 @@ func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsPa
12601285
return []*lsp.PublishDiagnosticsParams{}, nil
12611286
}
12621287

1263-
inoURI, _, err := handler.cpp2inoDocumentURI(cppDiags.URI, lsp.Range{})
1288+
inoURI, _, err := handler.cpp2inoDocumentURI(cppDiags.URI, lsp.NilRange)
12641289
return []*lsp.PublishDiagnosticsParams{
12651290
{
12661291
URI: inoURI,
@@ -1332,9 +1357,9 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
13321357
}
13331358

13341359
// Push back to IDE the converted diagnostics
1335-
inoDocsWithDiagnostics := map[lsp.DocumentURI]bool{}
1360+
inoDocsWithDiagnostics := map[string]bool{}
13361361
for _, inoDiag := range inoDiagnostics {
1337-
if inoDiag.URI == lsp.NewDocumentURI(sourcemapper.NotIno.File) {
1362+
if inoDiag.URI.String() == sourcemapper.NotInoURI.String() {
13381363
cleanUpInoDiagnostics = true
13391364
continue
13401365
}
@@ -1350,7 +1375,7 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
13501375
// check for newly created symbols (that in turn may trigger a
13511376
// new arduino-preprocessing of the sketch).
13521377
if inoDiag.URI.Ext() == ".ino" {
1353-
inoDocsWithDiagnostics[inoDiag.URI] = true
1378+
inoDocsWithDiagnostics[inoDiag.URI.Canonical()] = true
13541379
cleanUpInoDiagnostics = true
13551380
for _, diag := range inoDiag.Diagnostics {
13561381
if diag.Code == "undeclared_var_use_suggest" || diag.Code == "undeclared_var_use" {
@@ -1366,14 +1391,14 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
13661391

13671392
if cleanUpInoDiagnostics {
13681393
// Remove diagnostics from all .ino where there are no errors coming from clang
1369-
for sourceURI := range handler.inoDocsWithDiagnostics {
1370-
if inoDocsWithDiagnostics[sourceURI] {
1394+
for sourcePath := range handler.inoDocsWithDiagnostics {
1395+
if inoDocsWithDiagnostics[sourcePath] {
13711396
// skip if we already sent updated diagnostics
13721397
continue
13731398
}
13741399
// otherwise clear previous diagnostics
13751400
msg := lsp.PublishDiagnosticsParams{
1376-
URI: sourceURI,
1401+
URI: lsp.NewDocumentURI(sourcePath),
13771402
Diagnostics: []lsp.Diagnostic{},
13781403
}
13791404
if enableLogging {

Diff for: handler/sourcemapper/ino.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import (
99
"strconv"
1010
"strings"
1111

12+
"github.com/arduino/go-paths-helper"
1213
"github.com/bcmi-labs/arduino-language-server/handler/textutils"
1314
"github.com/bcmi-labs/arduino-language-server/lsp"
1415
"github.com/pkg/errors"
1516
)
1617

1718
// InoMapper is a mapping between the .ino sketch and the preprocessed .cpp file
1819
type InoMapper struct {
19-
InoText map[lsp.DocumentURI]*SourceRevision
2020
CppText *SourceRevision
2121
toCpp map[InoLine]int // Converts File.ino:line -> line
2222
toIno map[int]InoLine // Convers line -> File.ino:line
@@ -25,7 +25,10 @@ type InoMapper struct {
2525
}
2626

2727
// NotIno are lines that do not belongs to an .ino file
28-
var NotIno = InoLine{"not-ino", 0}
28+
var NotIno = InoLine{"/not-ino", 0}
29+
30+
// NotInoURI is the DocumentURI that do not belongs to an .ino file
31+
var NotInoURI, _ = lsp.NewDocumentURIFromURL("file:///not-ino")
2932

3033
type SourceRevision struct {
3134
Version int
@@ -40,12 +43,12 @@ type InoLine struct {
4043

4144
// InoToCppLine converts a source (.ino) line into a target (.cpp) line
4245
func (s *InoMapper) InoToCppLine(sourceURI lsp.DocumentURI, line int) int {
43-
return s.toCpp[InoLine{sourceURI.Unbox(), line}]
46+
return s.toCpp[InoLine{sourceURI.Canonical(), line}]
4447
}
4548

4649
// InoToCppLineOk converts a source (.ino) line into a target (.cpp) line
4750
func (s *InoMapper) InoToCppLineOk(sourceURI lsp.DocumentURI, line int) (int, bool) {
48-
res, ok := s.toCpp[InoLine{sourceURI.Unbox(), line}]
51+
res, ok := s.toCpp[InoLine{sourceURI.Canonical(), line}]
4952
return res, ok
5053
}
5154

@@ -166,7 +169,7 @@ func CreateInoMapper(targetFile []byte) *InoMapper {
166169
if err == nil && l > 0 {
167170
sourceLine = l - 1
168171
}
169-
sourceFile = unquoteCppString(tokens[2])
172+
sourceFile = paths.New(unquoteCppString(tokens[2])).Canonical().String()
170173
mapper.toIno[targetLine] = NotIno
171174
} else if sourceFile != "" {
172175
mapper.mapLine(sourceFile, sourceLine, targetLine)

Diff for: lsp/structures.go

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ type Range struct {
4848
End Position `json:"end"`
4949
}
5050

51+
var NilRange = Range{}
52+
5153
func (r Range) String() string {
5254
return fmt.Sprintf("%s-%s", r.Start, r.End)
5355
}

Diff for: lsp/uri.go

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ func (uri DocumentURI) Unbox() string {
3333
return path
3434
}
3535

36+
// Canonical returns the canonical path to the file pointed by the URI
37+
func (uri DocumentURI) Canonical() string {
38+
return uri.AsPath().Canonical().String()
39+
}
40+
3641
func (uri DocumentURI) String() string {
3742
return uri.url.String()
3843
}

Diff for: lsp/uri_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package lsp
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"strings"
67
"testing"
78

@@ -74,6 +75,13 @@ func TestJSONMarshalUnmarshal(t *testing.T) {
7475
require.Equal(t, `"file:///%F0%9F%98%9B"`, string(data))
7576
}
7677

78+
func TestNotInoFromSourceMapper(t *testing.T) {
79+
d, err := NewDocumentURIFromURL("file:///not-ino")
80+
require.NoError(t, err)
81+
fmt.Println(d.String())
82+
fmt.Println(d.Unbox())
83+
}
84+
7785
func windowsToSlash(path string) string {
7886
return strings.ReplaceAll(path, `\`, "/")
7987
}

0 commit comments

Comments
 (0)