From 7e45b5cb1566c33215ab5312c97f828057875b9c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 18 Jan 2021 17:40:55 +0100 Subject: [PATCH 1/6] 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. --- go.mod | 2 +- go.sum | 2 + handler/builder.go | 2 +- handler/handler.go | 89 ++++++++++++++++++++++++------------- handler/sourcemapper/ino.go | 13 +++--- lsp/structures.go | 2 + lsp/uri.go | 5 +++ lsp/uri_test.go | 8 ++++ 8 files changed, 84 insertions(+), 39 deletions(-) diff --git a/go.mod b/go.mod index aceee61..e6543b1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.12 require ( github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2 - github.com/arduino/go-paths-helper v1.4.0 + github.com/arduino/go-paths-helper v1.5.0 github.com/pkg/errors v0.9.1 github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index 2eacd34..82a9597 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3 github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= github.com/arduino/go-paths-helper v1.4.0 h1:ilnseAdxmN1bFnLxxXHRtcdmt9jBf3O4jtYfWfqule4= github.com/arduino/go-paths-helper v1.4.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= +github.com/arduino/go-paths-helper v1.5.0 h1:RVo189hD+GhUS1rQ3gixwK1nSbvVR8MGIGa7Gxv2bdM= +github.com/arduino/go-paths-helper v1.5.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= github.com/arduino/go-properties-orderedmap v1.3.0 h1:4No/vQopB36e7WUIk6H6TxiSEJPiMrVOCZylYmua39o= github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4= diff --git a/handler/builder.go b/handler/builder.go index bf1ae83..e7c0b4b 100644 --- a/handler/builder.go +++ b/handler/builder.go @@ -112,7 +112,7 @@ func (handler *InoHandler) generateBuildEnvironment() (*paths.Path, error) { } data := overridesFile{Overrides: map[string]string{}} for uri, trackedFile := range handler.docs { - rel, err := uri.AsPath().RelFrom(handler.sketchRoot) + rel, err := paths.New(uri).RelFrom(handler.sketchRoot) if err != nil { return nil, errors.WithMessage(err, "dumping tracked files") } diff --git a/handler/handler.go b/handler/handler.go index 78400b9..5d4dcc2 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -58,8 +58,8 @@ type InoHandler struct { sketchName string sketchMapper *sourcemapper.InoMapper sketchTrackedFilesCount int - docs map[lsp.DocumentURI]*lsp.TextDocumentItem - inoDocsWithDiagnostics map[lsp.DocumentURI]bool + docs map[string]*lsp.TextDocumentItem + inoDocsWithDiagnostics map[string]bool config lsp.BoardConfig synchronizer Synchronizer @@ -68,8 +68,8 @@ type InoHandler struct { // NewInoHandler creates and configures an InoHandler. func NewInoHandler(stdio io.ReadWriteCloser, board lsp.Board) *InoHandler { handler := &InoHandler{ - docs: map[lsp.DocumentURI]*lsp.TextDocumentItem{}, - inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, + docs: map[string]*lsp.TextDocumentItem{}, + inoDocsWithDiagnostics: map[string]bool{}, config: lsp.BoardConfig{ SelectedBoard: board, }, @@ -542,7 +542,7 @@ func startClangd(compileCommandsDir, sketchCpp *paths.Path) (io.WriteCloser, io. func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (*lsp.DidOpenTextDocumentParams, error) { // Add the TextDocumentItem in the tracked files list inoItem := inoDidOpen.TextDocument - handler.docs[inoItem.URI] = &inoItem + handler.docs[inoItem.URI.Canonical()] = &inoItem // If we are tracking a .ino... if inoItem.URI.Ext() == ".ino" { @@ -566,8 +566,8 @@ func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (* func (handler *InoHandler) didClose(inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { inoIdentifier := inoDidClose.TextDocument - if _, exist := handler.docs[inoIdentifier.URI]; exist { - delete(handler.docs, inoIdentifier.URI) + if _, exist := handler.docs[inoIdentifier.URI.Canonical()]; exist { + delete(handler.docs, inoIdentifier.URI.Canonical()) } else { log.Printf(" didClose of untracked document: %s", inoIdentifier.URI) return nil, unknownURI(inoIdentifier.URI) @@ -602,8 +602,8 @@ func (handler *InoHandler) ino2cppTextDocumentItem(inoItem lsp.TextDocumentItem) cppItem.Text = handler.sketchMapper.CppText.Text cppItem.Version = handler.sketchMapper.CppText.Version } else { - cppItem.Text = handler.docs[inoItem.URI].Text - cppItem.Version = handler.docs[inoItem.URI].Version + cppItem.Text = handler.docs[inoItem.URI.Canonical()].Text + cppItem.Version = handler.docs[inoItem.URI.Canonical()].Version } return cppItem, nil @@ -612,7 +612,7 @@ func (handler *InoHandler) ino2cppTextDocumentItem(inoItem lsp.TextDocumentItem) func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { doc := req.TextDocument - trackedDoc, ok := handler.docs[doc.URI] + trackedDoc, ok := handler.docs[doc.URI.Canonical()] if !ok { return nil, unknownURI(doc.URI) } @@ -766,6 +766,23 @@ func (handler *InoHandler) ino2cppDocumentURI(inoURI lsp.DocumentURI) (lsp.Docum return lsp.NilURI, err } +func (handler *InoHandler) inoDocumentURIFromInoPath(inoPath string) (lsp.DocumentURI, error) { + if inoPath == sourcemapper.NotIno.File { + return sourcemapper.NotInoURI, nil + } + doc, ok := handler.docs[inoPath] + if !ok { + log.Printf(" !!! Unresolved .ino path: %s", inoPath) + log.Printf(" !!! Known doc paths are:") + for p := range handler.docs { + log.Printf(" !!! > %s", p) + } + uri := lsp.NewDocumentURI(inoPath) + return uri, unknownURI(uri) + } + return doc.URI, nil +} + func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { // Sketchbook/Sketch/Sketch.ino <- build-path/sketch/Sketch.ino.cpp // 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 } else { log.Printf(" URI: ERROR: %s", err) handler.sketchMapper.DebugLogAll() + return lsp.NilURI, lsp.NilRange, err } - return lsp.NewDocumentURI(inoPath), inoRange, err + inoURI, err := handler.inoDocumentURIFromInoPath(inoPath) + return inoURI, inoRange, err } inside, err := cppPath.IsInsideDir(handler.buildSketchRoot) if err != nil { log.Printf(" could not determine if '%s' is inside '%s'", cppPath, handler.buildSketchRoot) - return lsp.NilURI, lsp.Range{}, err + return lsp.NilURI, lsp.NilRange, err } if !inside { + log.Printf(" '%s' is not inside '%s'", cppPath, handler.buildSketchRoot) log.Printf(" keep doc identifier to '%s' as-is", cppPath) return cppURI, cppRange, nil } @@ -806,7 +826,7 @@ func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange l } log.Printf(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err) - return lsp.NilURI, lsp.Range{}, err + return lsp.NilURI, lsp.NilRange, err } func (handler *InoHandler) ino2cppTextDocumentPositionParams(inoParams *lsp.TextDocumentPositionParams) (*lsp.TextDocumentPositionParams, error) { @@ -1111,36 +1131,41 @@ func (handler *InoHandler) Cpp2InoCommand(command *lsp.Command) *lsp.Command { return inoCommand } -func (handler *InoHandler) cpp2inoWorkspaceEdit(origWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { - if origWorkspaceEdit == nil { +func (handler *InoHandler) cpp2inoWorkspaceEdit(cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { + if cppWorkspaceEdit == nil { return nil } - resWorkspaceEdit := &lsp.WorkspaceEdit{ + inoWorkspaceEdit := &lsp.WorkspaceEdit{ Changes: map[lsp.DocumentURI][]lsp.TextEdit{}, } - for editURI, edits := range origWorkspaceEdit.Changes { + for editURI, edits := range cppWorkspaceEdit.Changes { // if the edits are not relative to sketch file... if !editURI.AsPath().EquivalentTo(handler.buildSketchCpp) { // ...pass them through... - resWorkspaceEdit.Changes[editURI] = edits + inoWorkspaceEdit.Changes[editURI] = edits continue } // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits for _, edit := range edits { - cppRange := edit.Range - inoFile, inoRange := handler.sketchMapper.CppToInoRange(cppRange) - inoURI := lsp.NewDocumentURI(inoFile) - if _, have := resWorkspaceEdit.Changes[inoURI]; !have { - resWorkspaceEdit.Changes[inoURI] = []lsp.TextEdit{} + inoURI, inoRange, err := handler.cpp2inoDocumentURI(editURI, edit.Range) + if err != nil { + log.Printf(" error converting edit %s:%s: %s", editURI, edit.Range, err) + continue + } + //inoFile, inoRange := handler.sketchMapper.CppToInoRange(edit.Range) + //inoURI := lsp.NewDocumentURI(inoFile) + if _, have := inoWorkspaceEdit.Changes[inoURI]; !have { + inoWorkspaceEdit.Changes[inoURI] = []lsp.TextEdit{} } - resWorkspaceEdit.Changes[inoURI] = append(resWorkspaceEdit.Changes[inoURI], lsp.TextEdit{ + inoWorkspaceEdit.Changes[inoURI] = append(inoWorkspaceEdit.Changes[inoURI], lsp.TextEdit{ NewText: edit.NewText, Range: inoRange, }) } } - return resWorkspaceEdit + log.Printf(" done converting workspaceEdit") + return inoWorkspaceEdit } func (handler *InoHandler) cpp2inoLocation(cppLocation lsp.Location) (lsp.Location, error) { @@ -1260,7 +1285,7 @@ func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsPa return []*lsp.PublishDiagnosticsParams{}, nil } - inoURI, _, err := handler.cpp2inoDocumentURI(cppDiags.URI, lsp.Range{}) + inoURI, _, err := handler.cpp2inoDocumentURI(cppDiags.URI, lsp.NilRange) return []*lsp.PublishDiagnosticsParams{ { URI: inoURI, @@ -1332,9 +1357,9 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2. } // Push back to IDE the converted diagnostics - inoDocsWithDiagnostics := map[lsp.DocumentURI]bool{} + inoDocsWithDiagnostics := map[string]bool{} for _, inoDiag := range inoDiagnostics { - if inoDiag.URI == lsp.NewDocumentURI(sourcemapper.NotIno.File) { + if inoDiag.URI.String() == sourcemapper.NotInoURI.String() { cleanUpInoDiagnostics = true continue } @@ -1350,7 +1375,7 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2. // check for newly created symbols (that in turn may trigger a // new arduino-preprocessing of the sketch). if inoDiag.URI.Ext() == ".ino" { - inoDocsWithDiagnostics[inoDiag.URI] = true + inoDocsWithDiagnostics[inoDiag.URI.Canonical()] = true cleanUpInoDiagnostics = true for _, diag := range inoDiag.Diagnostics { 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. if cleanUpInoDiagnostics { // Remove diagnostics from all .ino where there are no errors coming from clang - for sourceURI := range handler.inoDocsWithDiagnostics { - if inoDocsWithDiagnostics[sourceURI] { + for sourcePath := range handler.inoDocsWithDiagnostics { + if inoDocsWithDiagnostics[sourcePath] { // skip if we already sent updated diagnostics continue } // otherwise clear previous diagnostics msg := lsp.PublishDiagnosticsParams{ - URI: sourceURI, + URI: lsp.NewDocumentURI(sourcePath), Diagnostics: []lsp.Diagnostic{}, } if enableLogging { diff --git a/handler/sourcemapper/ino.go b/handler/sourcemapper/ino.go index 3a05f07..b31ffc0 100644 --- a/handler/sourcemapper/ino.go +++ b/handler/sourcemapper/ino.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "github.com/arduino/go-paths-helper" "github.com/bcmi-labs/arduino-language-server/handler/textutils" "github.com/bcmi-labs/arduino-language-server/lsp" "github.com/pkg/errors" @@ -16,7 +17,6 @@ import ( // InoMapper is a mapping between the .ino sketch and the preprocessed .cpp file type InoMapper struct { - InoText map[lsp.DocumentURI]*SourceRevision CppText *SourceRevision toCpp map[InoLine]int // Converts File.ino:line -> line toIno map[int]InoLine // Convers line -> File.ino:line @@ -25,7 +25,10 @@ type InoMapper struct { } // NotIno are lines that do not belongs to an .ino file -var NotIno = InoLine{"not-ino", 0} +var NotIno = InoLine{"/not-ino", 0} + +// NotInoURI is the DocumentURI that do not belongs to an .ino file +var NotInoURI, _ = lsp.NewDocumentURIFromURL("file:///not-ino") type SourceRevision struct { Version int @@ -40,12 +43,12 @@ type InoLine struct { // InoToCppLine converts a source (.ino) line into a target (.cpp) line func (s *InoMapper) InoToCppLine(sourceURI lsp.DocumentURI, line int) int { - return s.toCpp[InoLine{sourceURI.Unbox(), line}] + return s.toCpp[InoLine{sourceURI.Canonical(), line}] } // InoToCppLineOk converts a source (.ino) line into a target (.cpp) line func (s *InoMapper) InoToCppLineOk(sourceURI lsp.DocumentURI, line int) (int, bool) { - res, ok := s.toCpp[InoLine{sourceURI.Unbox(), line}] + res, ok := s.toCpp[InoLine{sourceURI.Canonical(), line}] return res, ok } @@ -166,7 +169,7 @@ func CreateInoMapper(targetFile []byte) *InoMapper { if err == nil && l > 0 { sourceLine = l - 1 } - sourceFile = unquoteCppString(tokens[2]) + sourceFile = paths.New(unquoteCppString(tokens[2])).Canonical().String() mapper.toIno[targetLine] = NotIno } else if sourceFile != "" { mapper.mapLine(sourceFile, sourceLine, targetLine) diff --git a/lsp/structures.go b/lsp/structures.go index 9b5cb73..be93da4 100644 --- a/lsp/structures.go +++ b/lsp/structures.go @@ -48,6 +48,8 @@ type Range struct { End Position `json:"end"` } +var NilRange = Range{} + func (r Range) String() string { return fmt.Sprintf("%s-%s", r.Start, r.End) } diff --git a/lsp/uri.go b/lsp/uri.go index a5ae960..a7a5645 100644 --- a/lsp/uri.go +++ b/lsp/uri.go @@ -33,6 +33,11 @@ func (uri DocumentURI) Unbox() string { return path } +// Canonical returns the canonical path to the file pointed by the URI +func (uri DocumentURI) Canonical() string { + return uri.AsPath().Canonical().String() +} + func (uri DocumentURI) String() string { return uri.url.String() } diff --git a/lsp/uri_test.go b/lsp/uri_test.go index b09a649..882b4a9 100644 --- a/lsp/uri_test.go +++ b/lsp/uri_test.go @@ -2,6 +2,7 @@ package lsp import ( "encoding/json" + "fmt" "strings" "testing" @@ -74,6 +75,13 @@ func TestJSONMarshalUnmarshal(t *testing.T) { require.Equal(t, `"file:///%F0%9F%98%9B"`, string(data)) } +func TestNotInoFromSourceMapper(t *testing.T) { + d, err := NewDocumentURIFromURL("file:///not-ino") + require.NoError(t, err) + fmt.Println(d.String()) + fmt.Println(d.Unbox()) +} + func windowsToSlash(path string) string { return strings.ReplaceAll(path, `\`, "/") } From 956770ac3382bc3a273a29f084f77622ef22e34f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 18 Jan 2021 17:42:40 +0100 Subject: [PATCH 2/6] Added a long empty header while restarting logging --- streams/dumper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/streams/dumper.go b/streams/dumper.go index a948aa1..5cc001f 100644 --- a/streams/dumper.go +++ b/streams/dumper.go @@ -41,6 +41,7 @@ func OpenLogFileAs(filename string) *os.File { abs, _ := path.Abs() log.Printf("logging to %s", abs) } + res.WriteString("\n\n\n\n\n\n\nStarted logging.\n") return res } From 505b5831bf05af0a89fa1a0d4019c0ec75fcd6fd Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 18 Jan 2021 18:33:54 +0100 Subject: [PATCH 3/6] Removed useless logic from AsyncHandler --- handler/builder.go | 4 ++-- handler/handler.go | 42 ++++++++++++++++++++++++------------------ handler/syncer.go | 34 ++-------------------------------- main.go | 2 +- 4 files changed, 29 insertions(+), 53 deletions(-) diff --git a/handler/builder.go b/handler/builder.go index e7c0b4b..580e0d0 100644 --- a/handler/builder.go +++ b/handler/builder.go @@ -94,9 +94,9 @@ func (handler *InoHandler) rebuildEnvironmentLoop() { } }() - handler.synchronizer.DataMux.Lock() + handler.dataMux.Lock() handler.initializeWorkbench(nil) - handler.synchronizer.DataMux.Unlock() + handler.dataMux.Unlock() done <- true close(done) } diff --git a/handler/handler.go b/handler/handler.go index 5d4dcc2..dca4ba8 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -27,14 +27,12 @@ import ( var globalCliPath string var globalClangdPath string var enableLogging bool -var asyncProcessing bool // Setup initializes global variables. -func Setup(cliPath string, clangdPath string, _enableLogging bool, _asyncProcessing bool) { +func Setup(cliPath string, clangdPath string, _enableLogging bool) { globalCliPath = cliPath globalClangdPath = clangdPath enableLogging = _enableLogging - asyncProcessing = _asyncProcessing } // CLangdStarter starts clangd and returns its stdin/out/err @@ -42,8 +40,11 @@ type CLangdStarter func() (stdin io.WriteCloser, stdout io.ReadCloser, stderr io // InoHandler is a JSON-RPC handler that delegates messages to clangd. type InoHandler struct { - StdioConn *jsonrpc2.Conn - ClangdConn *jsonrpc2.Conn + StdioConn *jsonrpc2.Conn + ClangdConn *jsonrpc2.Conn + + clangdStarted *sync.Cond + dataMux sync.RWMutex lspInitializeParams *lsp.InitializeParams buildPath *paths.Path buildSketchRoot *paths.Path @@ -61,8 +62,7 @@ type InoHandler struct { docs map[string]*lsp.TextDocumentItem inoDocsWithDiagnostics map[string]bool - config lsp.BoardConfig - synchronizer Synchronizer + config lsp.BoardConfig } // NewInoHandler creates and configures an InoHandler. @@ -74,15 +74,9 @@ func NewInoHandler(stdio io.ReadWriteCloser, board lsp.Board) *InoHandler { SelectedBoard: board, }, } - + handler.clangdStarted = sync.NewCond(&handler.dataMux) stdStream := jsonrpc2.NewBufferedStream(stdio, jsonrpc2.VSCodeObjectCodec{}) var stdHandler jsonrpc2.Handler = jsonrpc2.HandlerWithError(handler.HandleMessageFromIDE) - if asyncProcessing { - stdHandler = AsyncHandler{ - handler: stdHandler, - synchronizer: &handler.synchronizer, - } - } handler.StdioConn = jsonrpc2.NewConn(context.Background(), stdStream, stdHandler) if enableLogging { log.Println("Initial board configuration:", board) @@ -118,11 +112,23 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr "textDocument/didClose": true, } if needsWriteLock[req.Method] { - handler.synchronizer.DataMux.Lock() - defer handler.synchronizer.DataMux.Unlock() + handler.dataMux.Lock() + defer handler.dataMux.Unlock() } else { - handler.synchronizer.DataMux.RLock() - defer handler.synchronizer.DataMux.RUnlock() + handler.dataMux.RLock() + defer handler.dataMux.RUnlock() + } + + // Wait for clangd start-up + doNotNeedClangd := map[string]bool{ + "initialize": true, + "initialized": true, + } + if handler.ClangdConn == nil && !doNotNeedClangd[req.Method] { + handler.clangdStarted.Wait() + if handler.ClangdConn == nil { + return nil, errors.New("could not run clangd, aborted") + } } // Handle LSP methods: transform parameters and send to clangd diff --git a/handler/syncer.go b/handler/syncer.go index 452d3c4..a8d4cbb 100644 --- a/handler/syncer.go +++ b/handler/syncer.go @@ -2,46 +2,16 @@ package handler import ( "context" - "sync" "github.com/sourcegraph/jsonrpc2" ) -// Synchronizer is used to block message processing while an edit or config change is applied. -type Synchronizer struct { - // FileMux is a read/write mutex for file access. It is locked during the processing of - // messages that modify target files for clangd. - FileMux sync.RWMutex - // DataMux is a mutex for document metadata access, i.e. source-target URI mappings and line mappings. - DataMux sync.RWMutex -} - // AsyncHandler wraps a Handler such that each request is handled in its own goroutine. type AsyncHandler struct { - handler jsonrpc2.Handler - synchronizer *Synchronizer + handler jsonrpc2.Handler } // Handle handles a request or notification func (ah AsyncHandler) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) { - needsWriteLock := req.Method == "textDocument/didOpen" || req.Method == "textDocument/didChange" - if needsWriteLock { - go func() { - ah.synchronizer.FileMux.Lock() - defer ah.synchronizer.FileMux.Unlock() - if enableLogging { - // log.Println("Message processing locked for", req.Method) - } - ah.handler.Handle(ctx, conn, req) - if enableLogging { - // log.Println("Message processing unlocked for", req.Method) - } - }() - } else { - go func() { - ah.synchronizer.FileMux.RLock() - ah.handler.Handle(ctx, conn, req) - ah.synchronizer.FileMux.RUnlock() - }() - } + go ah.handler.Handle(ctx, conn, req) } diff --git a/main.go b/main.go index d76c81a..fb6a3e4 100644 --- a/main.go +++ b/main.go @@ -44,7 +44,7 @@ func main() { log.SetOutput(os.Stderr) } - handler.Setup(cliPath, clangdPath, enableLogging, true) + handler.Setup(cliPath, clangdPath, enableLogging) initialBoard := lsp.Board{Fqbn: initialFqbn, Name: initialBoardName} stdio := streams.NewReadWriteCloser(os.Stdin, os.Stdout) From d7969be44a8510e1f1f9bfbc97be7fc455373c26 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 18 Jan 2021 18:38:45 +0100 Subject: [PATCH 4/6] Increased logging data --- handler/handler.go | 133 +++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 48 deletions(-) diff --git a/handler/handler.go b/handler/handler.go index dca4ba8..3c0f4e4 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/arduino/arduino-cli/arduino/builder" @@ -43,6 +44,9 @@ type InoHandler struct { StdioConn *jsonrpc2.Conn ClangdConn *jsonrpc2.Conn + stdioNotificationCount int64 + clangdNotificationCount int64 + clangdStarted *sync.Cond dataMux sync.RWMutex lspInitializeParams *lsp.InitializeParams @@ -105,12 +109,30 @@ func (handler *InoHandler) StopClangd() { func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) { defer streams.CatchAndLogPanic() + prefix := "IDE --> " + if req.Notif { + n := atomic.AddInt64(&handler.stdioNotificationCount, 1) + prefix += fmt.Sprintf("%s notif%d ", req.Method, n) + } else { + prefix += fmt.Sprintf("%s %v ", req.Method, req.ID) + } + defer log.Printf(prefix + "(done)") + + params, err := lsp.ReadParams(req.Method, req.Params) + if err != nil { + return nil, err + } + if params == nil { + params = req.Params + } + needsWriteLock := map[string]bool{ "initialize": true, "textDocument/didOpen": true, "textDocument/didChange": true, "textDocument/didClose": true, } + log.Printf(prefix + "(queued)") if needsWriteLock[req.Method] { handler.dataMux.Lock() defer handler.dataMux.Unlock() @@ -125,22 +147,19 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr "initialized": true, } if handler.ClangdConn == nil && !doNotNeedClangd[req.Method] { + log.Printf(prefix + "(throttled: waiting for clangd)") handler.clangdStarted.Wait() if handler.ClangdConn == nil { + log.Printf("Clangd startup failed: aborting call") return nil, errors.New("could not run clangd, aborted") } } + log.Printf(prefix + "(running)") + // Handle LSP methods: transform parameters and send to clangd var inoURI, cppURI lsp.DocumentURI - params, err := lsp.ReadParams(req.Method, req.Params) - if err != nil { - return nil, err - } - if params == nil { - params = req.Params - } switch p := params.(type) { case *lsp.InitializeParams: // method "initialize" @@ -148,21 +167,22 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr case *lsp.InitializedParams: // method "initialized" - log.Println("--> initialized") + log.Println(prefix + "notification is not propagated to clangd") + return nil, nil // Do not propagate to clangd case *lsp.DidOpenTextDocumentParams: // method "textDocument/didOpen" inoURI = p.TextDocument.URI - log.Printf("--> didOpen(%s@%d as '%s')", p.TextDocument.URI, p.TextDocument.Version, p.TextDocument.LanguageID) + log.Printf(prefix+"(%s@%d as '%s')", p.TextDocument.URI, p.TextDocument.Version, p.TextDocument.LanguageID) if res, e := handler.didOpen(p); e != nil { params = nil err = e } else if res == nil { - log.Println(" --X notification is not propagated to clangd") + log.Println(prefix + "notification is not propagated to clangd") return nil, nil // do not propagate to clangd } else { - log.Printf(" --> didOpen(%s@%d as '%s')", res.TextDocument.URI, res.TextDocument.Version, p.TextDocument.LanguageID) + log.Printf(prefix+"to clang: didOpen(%s@%d as '%s')", res.TextDocument.URI, res.TextDocument.Version, p.TextDocument.LanguageID) params = res } @@ -329,38 +349,39 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr err = handler.ino2cppExecuteCommand(p) } if err != nil { - log.Printf(" --E %s", err) + log.Printf(prefix+"Error: %s", err) return nil, err } var result interface{} if req.Notif { + log.Printf(prefix + "sent to Clang") err = handler.ClangdConn.Notify(ctx, req.Method, params) - // log.Println(" sent", req.Method, "notification to clangd") } else { + log.Printf(prefix + "sent to Clang") ctx, cancel := context.WithTimeout(ctx, 800*time.Millisecond) defer cancel() result, err = lsp.SendRequest(ctx, handler.ClangdConn, req.Method, params) - // log.Println(" sent", req.Method, "request id", req.ID, " to clangd") } if err == nil && handler.buildSketchSymbolsLoad { handler.buildSketchSymbolsLoad = false - log.Println("--! Resfreshing document symbols") + log.Println(prefix + "Queued resfreshing document symbols") err = handler.refreshCppDocumentSymbols() } if err == nil && handler.buildSketchSymbolsCheck { handler.buildSketchSymbolsCheck = false - log.Println("--! Resfreshing document symbols") + log.Println(prefix + "Queued check document symbols") err = handler.checkCppDocumentSymbols() } if err != nil { // Exit the process and trigger a restart by the client in case of a severe error if err.Error() == "context deadline exceeded" { - log.Println("Timeout exceeded while waiting for a reply from clangd.") + log.Println(prefix + "Timeout exceeded while waiting for a reply from clangd.") handler.exit() } if strings.Contains(err.Error(), "non-added document") || strings.Contains(err.Error(), "non-added file") { - log.Println("The clangd process has lost track of the open document.") + log.Printf(prefix + "The clangd process has lost track of the open document.") + log.Printf(prefix+" %s", err) handler.exit() } } @@ -381,13 +402,13 @@ func (handler *InoHandler) exit() { func (handler *InoHandler) initializeWorkbench(params *lsp.InitializeParams) error { currCppTextVersion := 0 if params != nil { - log.Printf("--> initialize(%s)\n", params.RootURI) + log.Printf(" --> initialize(%s)\n", params.RootURI) handler.lspInitializeParams = params handler.sketchRoot = params.RootURI.AsPath() handler.sketchName = handler.sketchRoot.Base() } else { + log.Printf(" --> RE-initialize()\n") currCppTextVersion = handler.sketchMapper.CppText.Version - log.Printf("--> RE-initialize()\n") } if buildPath, err := handler.generateBuildEnvironment(); err == nil { @@ -450,18 +471,27 @@ func (handler *InoHandler) initializeWorkbench(params *lsp.InitializeParams) err } func (handler *InoHandler) refreshCppDocumentSymbols() error { + prefix := "LS --- " + defer log.Printf(prefix + "(done)") + log.Printf(prefix + "(queued)") + handler.dataMux.Lock() + defer handler.dataMux.Unlock() + log.Printf(prefix + "(running)") + // Query source code symbols cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) - log.Printf(" --> documentSymbol(%s)", cppURI) + log.Printf(prefix+"sent to clangd: documentSymbol(%s)", cppURI) result, err := lsp.SendRequest(context.Background(), handler.ClangdConn, "textDocument/documentSymbol", &lsp.DocumentSymbolParams{ TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, }) if err != nil { + log.Printf(prefix+"error: %s", err) return errors.WithMessage(err, "quering source code symbols") } result = handler.transformClangdResult("textDocument/documentSymbol", cppURI, lsp.NilURI, result) if symbols, ok := result.([]lsp.DocumentSymbol); !ok { - return errors.WithMessage(err, "quering source code symbols (2)") + log.Printf(prefix + "error: invalid response from clangd") + return errors.New("invalid response from clangd") } else { // Filter non-functions symbols i := 0 @@ -474,7 +504,7 @@ func (handler *InoHandler) refreshCppDocumentSymbols() error { } symbols = symbols[:i] for _, symbol := range symbols { - log.Printf(" symbol: %s %s", symbol.Kind, symbol.Name) + log.Printf(prefix+" symbol: %s %s", symbol.Kind, symbol.Name) } handler.buildSketchSymbols = symbols } @@ -482,18 +512,19 @@ func (handler *InoHandler) refreshCppDocumentSymbols() error { } func (handler *InoHandler) checkCppDocumentSymbols() error { + prefix := "LS --- " oldSymbols := handler.buildSketchSymbols if err := handler.refreshCppDocumentSymbols(); err != nil { return err } if len(oldSymbols) != len(handler.buildSketchSymbols) { - log.Println("--! new symbols detected, triggering sketch rebuild!") + log.Println(prefix + "new symbols detected, triggering sketch rebuild!") handler.scheduleRebuildEnvironment() return nil } for i, old := range oldSymbols { if newName := handler.buildSketchSymbols[i].Name; old.Name != newName { - log.Printf("--! symbols changed, triggering sketch rebuild: '%s' -> '%s'", old.Name, newName) + log.Printf(prefix+"symbols changed, triggering sketch rebuild: '%s' -> '%s'", old.Name, newName) handler.scheduleRebuildEnvironment() return nil } @@ -1332,23 +1363,32 @@ func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsPa func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) { defer streams.CatchAndLogPanic() + prefix := "CLG <-- " + if req.Notif { + n := atomic.AddInt64(&handler.clangdNotificationCount, 1) + prefix += fmt.Sprintf("%s notif%d ", req.Method, n) + } else { + prefix += fmt.Sprintf("%s %v ", req.Method, req.ID) + } + defer log.Printf(prefix + "(done)") + + log.Printf(prefix + "(queued)") handler.synchronizer.DataMux.RLock() defer handler.synchronizer.DataMux.RUnlock() + log.Printf(prefix + "(running)") params, err := lsp.ReadParams(req.Method, req.Params) if err != nil { + log.Println(prefix+"parsing clang message:", err) return nil, errors.WithMessage(err, "parsing JSON message from clangd") } - if params == nil { - // passthrough - params = req.Params - } + switch p := params.(type) { case *lsp.PublishDiagnosticsParams: // "textDocument/publishDiagnostics" - log.Printf(" <-- publishDiagnostics(%s):", p.URI) + log.Printf(prefix+"publishDiagnostics(%s):", p.URI) for _, diag := range p.Diagnostics { - log.Printf(" > %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) + log.Printf(prefix+"> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) } // the diagnostics on sketch.cpp.ino once mapped into their @@ -1370,13 +1410,6 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2. continue } - if enableLogging { - log.Printf("<-- publishDiagnostics(%s):", inoDiag.URI) - for _, diag := range inoDiag.Diagnostics { - log.Printf(" > %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) - } - } - // If we have an "undefined reference" in the .ino code trigger a // check for newly created symbols (that in turn may trigger a // new arduino-preprocessing of the sketch). @@ -1390,6 +1423,10 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2. } } + log.Printf(prefix+"to IDE: publishDiagnostics(%s):", inoDiag.URI) + for _, diag := range inoDiag.Diagnostics { + log.Printf(prefix+"> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) + } if err := handler.StdioConn.Notify(ctx, "textDocument/publishDiagnostics", inoDiag); err != nil { return nil, err } @@ -1407,9 +1444,7 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2. URI: lsp.NewDocumentURI(sourcePath), Diagnostics: []lsp.Diagnostic{}, } - if enableLogging { - log.Printf("<-- publishDiagnostics(%s):", msg.URI) - } + log.Printf(prefix+"to IDE: publishDiagnostics(%s):", msg.URI) if err := handler.StdioConn.Notify(ctx, "textDocument/publishDiagnostics", msg); err != nil { return nil, err } @@ -1423,22 +1458,24 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2. // "workspace/applyEdit" p.Edit = *handler.cpp2inoWorkspaceEdit(&p.Edit) } - if err != nil { log.Println("From clangd: Method:", req.Method, "Error:", err) return nil, err } + + if params == nil { + // passthrough + log.Printf(prefix + "passing through message") + params = req.Params + } + var result interface{} if req.Notif { + log.Println(prefix + "to IDE") err = handler.StdioConn.Notify(ctx, req.Method, params) - if enableLogging { - log.Println("From clangd:", req.Method) - } } else { + log.Println(prefix + "to IDE") result, err = lsp.SendRequest(ctx, handler.StdioConn, req.Method, params) - if enableLogging { - log.Println("From clangd:", req.Method, "id", req.ID) - } } return result, err } From 0ce1169c0c0058fe2c5bd8acd9fdbd17b18dbe1f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 18 Jan 2021 18:52:58 +0100 Subject: [PATCH 5/6] detach clangd initialization from arduino LS initialization --- handler/handler.go | 126 +++++++++++++++++++++++++++++++++++---------- lsp/service.go | 2 +- 2 files changed, 101 insertions(+), 27 deletions(-) diff --git a/handler/handler.go b/handler/handler.go index 3c0f4e4..27877de 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -126,32 +126,38 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr params = req.Params } - needsWriteLock := map[string]bool{ - "initialize": true, - "textDocument/didOpen": true, - "textDocument/didChange": true, - "textDocument/didClose": true, - } log.Printf(prefix + "(queued)") - if needsWriteLock[req.Method] { + switch req.Method { + case // Write lock + "initialize", + "textDocument/didOpen", + "textDocument/didChange", + "textDocument/didClose": handler.dataMux.Lock() defer handler.dataMux.Unlock() - } else { + case // Read lock + "textDocument/publishDiagnostics", + "workspace/applyEdit": + handler.dataMux.RLock() + defer handler.dataMux.RUnlock() + default: // Default to read lock handler.dataMux.RLock() defer handler.dataMux.RUnlock() } - // Wait for clangd start-up - doNotNeedClangd := map[string]bool{ - "initialize": true, - "initialized": true, - } - if handler.ClangdConn == nil && !doNotNeedClangd[req.Method] { - log.Printf(prefix + "(throttled: waiting for clangd)") - handler.clangdStarted.Wait() + switch req.Method { + case // Do not need clangd + "initialize", + "initialized": + default: // Default to clangd required + // Wait for clangd start-up if handler.ClangdConn == nil { - log.Printf("Clangd startup failed: aborting call") - return nil, errors.New("could not run clangd, aborted") + log.Printf(prefix + "(throttled: waiting for clangd)") + handler.clangdStarted.Wait() + if handler.ClangdConn == nil { + log.Printf(prefix + "clangd startup failed: aborting call") + return nil, errors.New("could not start clangd, aborted") + } } } @@ -163,7 +169,51 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr switch p := params.(type) { case *lsp.InitializeParams: // method "initialize" - err = handler.initializeWorkbench(p) + + go func() { + // Start clangd asynchronously + log.Printf("LS --- initializing workbench (queued)") + handler.dataMux.Lock() + defer handler.dataMux.Unlock() + + log.Printf("LS --- initializing workbench (running)") + handler.initializeWorkbench(p) + + // clangd should be running now... + handler.clangdStarted.Broadcast() + + log.Printf("LS --- initializing workbench (done)") + }() + + T := true + F := false + return &lsp.InitializeResult{ + Capabilities: lsp.ServerCapabilities{ + TextDocumentSync: &lsp.TextDocumentSyncOptionsOrKind{Kind: &lsp.TDSKIncremental}, + HoverProvider: true, + CompletionProvider: &lsp.CompletionOptions{ + TriggerCharacters: []string{".", "\u003e", ":"}, + }, + SignatureHelpProvider: &lsp.SignatureHelpOptions{ + TriggerCharacters: []string{"(", ","}, + }, + DefinitionProvider: true, + ReferencesProvider: false, // TODO: true + DocumentHighlightProvider: true, + DocumentSymbolProvider: true, + WorkspaceSymbolProvider: true, + CodeActionProvider: &lsp.BoolOrCodeActionOptions{IsProvider: &T}, + DocumentFormattingProvider: true, + DocumentRangeFormattingProvider: true, + DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ + FirstTriggerCharacter: "\n", + }, + RenameProvider: &lsp.BoolOrRenameOptions{IsProvider: &F}, // TODO: &T + ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ + Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, + }, + }, + }, nil case *lsp.InitializedParams: // method "initialized" @@ -366,12 +416,12 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr if err == nil && handler.buildSketchSymbolsLoad { handler.buildSketchSymbolsLoad = false log.Println(prefix + "Queued resfreshing document symbols") - err = handler.refreshCppDocumentSymbols() + go handler.refreshCppDocumentSymbols() } if err == nil && handler.buildSketchSymbolsCheck { handler.buildSketchSymbolsCheck = false log.Println(prefix + "Queued check document symbols") - err = handler.checkCppDocumentSymbols() + go handler.checkCppDocumentSymbols() } if err != nil { // Exit the process and trigger a restart by the client in case of a severe error @@ -431,9 +481,6 @@ func (handler *InoHandler) initializeWorkbench(params *lsp.InitializeParams) err if params == nil { // If we are restarting re-synchronize clangd - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) cppTextDocumentIdentifier := lsp.TextDocumentIdentifier{URI: cppURI} @@ -447,6 +494,8 @@ func (handler *InoHandler) initializeWorkbench(params *lsp.InitializeParams) err }, } + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() if err := handler.ClangdConn.Notify(ctx, "textDocument/didChange", syncEvent); err != nil { log.Println(" error reinitilizing clangd:", err) return err @@ -465,6 +514,20 @@ func (handler *InoHandler) initializeWorkbench(params *lsp.InitializeParams) err clangdStream := jsonrpc2.NewBufferedStream(clangdStdio, jsonrpc2.VSCodeObjectCodec{}) clangdHandler := jsonrpc2.AsyncHandler(jsonrpc2.HandlerWithError(handler.FromClangd)) handler.ClangdConn = jsonrpc2.NewConn(context.Background(), clangdStream, clangdHandler) + + // Send initialization command to clangd + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + var resp lsp.InitializeResult + if err := handler.ClangdConn.Call(ctx, "initialize", handler.lspInitializeParams, &resp); err != nil { + log.Println(" error initilizing clangd:", err) + return err + } + + if err := handler.ClangdConn.Notify(ctx, "initialized", lsp.InitializedParams{}); err != nil { + log.Println(" error sending initialize to clangd:", err) + return err + } } return nil @@ -1373,8 +1436,19 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2. defer log.Printf(prefix + "(done)") log.Printf(prefix + "(queued)") - handler.synchronizer.DataMux.RLock() - defer handler.synchronizer.DataMux.RUnlock() + switch req.Method { + case // No locking required + "$/progress", + "window/workDoneProgress/create": + case // Read lock + "textDocument/publishDiagnostics", + "workspace/applyEdit": + handler.dataMux.RLock() + defer handler.dataMux.RUnlock() + default: // Default to read lock + handler.dataMux.RLock() + defer handler.dataMux.RUnlock() + } log.Printf(prefix + "(running)") params, err := lsp.ReadParams(req.Method, req.Params) diff --git a/lsp/service.go b/lsp/service.go index 84b258a..c4de03d 100644 --- a/lsp/service.go +++ b/lsp/service.go @@ -221,7 +221,7 @@ const ( // support JSON-(un)marshaling both). type TextDocumentSyncKind int -const ( +var ( TDSKNone TextDocumentSyncKind = 0 TDSKFull TextDocumentSyncKind = 1 TDSKIncremental TextDocumentSyncKind = 2 From 429acc57a27b8c8262aba792dd2f6e9c78b21fde Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 21 Jan 2021 01:36:31 +0100 Subject: [PATCH 6/6] fixed tests for sourcemapper --- handler/sourcemapper/ino_test.go | 204 ++++++++++++++++--------------- 1 file changed, 104 insertions(+), 100 deletions(-) diff --git a/handler/sourcemapper/ino_test.go b/handler/sourcemapper/ino_test.go index d6cf23e..5a89a77 100644 --- a/handler/sourcemapper/ino_test.go +++ b/handler/sourcemapper/ino_test.go @@ -4,19 +4,20 @@ import ( "fmt" "testing" + "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" ) func TestCreateSourceMaps(t *testing.T) { input := `#include -#line 1 "sketch_july2a.ino" -#line 1 "sketch_july2a.ino" +#line 1 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" +#line 1 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" -#line 2 "sketch_july2a.ino" +#line 2 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" void setup(); -#line 7 "sketch_july2a.ino" +#line 7 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" void loop(); -#line 2 "sketch_july2a.ino" +#line 2 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" void setup() { // put your setup code here, to run once: @@ -28,43 +29,44 @@ void loop() { } ` sourceMap := CreateInoMapper([]byte(input)) + sketchJuly2a := paths.New("/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino").Canonical().String() require.EqualValues(t, map[InoLine]int{ - {"sketch_july2a.ino", 0}: 3, - {"sketch_july2a.ino", 1}: 9, - {"sketch_july2a.ino", 2}: 10, - {"sketch_july2a.ino", 3}: 11, - {"sketch_july2a.ino", 4}: 12, - {"sketch_july2a.ino", 5}: 13, - {"sketch_july2a.ino", 6}: 14, - {"sketch_july2a.ino", 7}: 15, - {"sketch_july2a.ino", 8}: 16, - {"sketch_july2a.ino", 9}: 17, - {"sketch_july2a.ino", 10}: 18, + {sketchJuly2a, 0}: 3, + {sketchJuly2a, 1}: 9, + {sketchJuly2a, 2}: 10, + {sketchJuly2a, 3}: 11, + {sketchJuly2a, 4}: 12, + {sketchJuly2a, 5}: 13, + {sketchJuly2a, 6}: 14, + {sketchJuly2a, 7}: 15, + {sketchJuly2a, 8}: 16, + {sketchJuly2a, 9}: 17, + {sketchJuly2a, 10}: 18, }, sourceMap.toCpp) require.EqualValues(t, map[int]InoLine{ 0: NotIno, 1: NotIno, 2: NotIno, - 3: {"sketch_july2a.ino", 0}, + 3: {sketchJuly2a, 0}, 4: NotIno, - 5: {"sketch_july2a.ino", 1}, // setup + 5: {sketchJuly2a, 1}, // setup 6: NotIno, - 7: {"sketch_july2a.ino", 6}, // loop + 7: {sketchJuly2a, 6}, // loop 8: NotIno, - 9: {"sketch_july2a.ino", 1}, - 10: {"sketch_july2a.ino", 2}, - 11: {"sketch_july2a.ino", 3}, - 12: {"sketch_july2a.ino", 4}, - 13: {"sketch_july2a.ino", 5}, - 14: {"sketch_july2a.ino", 6}, - 15: {"sketch_july2a.ino", 7}, - 16: {"sketch_july2a.ino", 8}, - 17: {"sketch_july2a.ino", 9}, - 18: {"sketch_july2a.ino", 10}, + 9: {sketchJuly2a, 1}, + 10: {sketchJuly2a, 2}, + 11: {sketchJuly2a, 3}, + 12: {sketchJuly2a, 4}, + 13: {sketchJuly2a, 5}, + 14: {sketchJuly2a, 6}, + 15: {sketchJuly2a, 7}, + 16: {sketchJuly2a, 8}, + 17: {sketchJuly2a, 9}, + 18: {sketchJuly2a, 10}, }, sourceMap.toIno) require.EqualValues(t, map[int]InoLine{ - 5: {"sketch_july2a.ino", 1}, // setup - 7: {"sketch_july2a.ino", 6}, // loop + 5: {sketchJuly2a, 1}, // setup + 7: {sketchJuly2a, 6}, // loop }, sourceMap.cppPreprocessed) dumpCppToInoMap(sourceMap.toIno) @@ -122,88 +124,90 @@ void vino() { void secondFunction() { }` + ProvaSpazio := paths.New("/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino").Canonical().String() + SecondTab := paths.New("/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino").Canonical().String() sourceMap := CreateInoMapper([]byte(input)) require.EqualValues(t, sourceMap.toCpp, map[InoLine]int{ - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 0}: 2, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 1}: 3, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 2}: 4, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 3}: 14, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 4}: 15, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 5}: 16, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 6}: 17, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 7}: 18, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 8}: 19, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 9}: 20, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 10}: 21, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 11}: 22, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 12}: 23, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 13}: 24, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 14}: 25, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 15}: 26, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 16}: 27, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 17}: 28, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 18}: 29, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 19}: 30, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 20}: 31, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 21}: 32, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 22}: 33, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 23}: 34, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 24}: 35, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 0}: 37, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 1}: 38, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 2}: 39, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 3}: 40, - {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 4}: 41, + {ProvaSpazio, 0}: 2, + {ProvaSpazio, 1}: 3, + {ProvaSpazio, 2}: 4, + {ProvaSpazio, 3}: 14, + {ProvaSpazio, 4}: 15, + {ProvaSpazio, 5}: 16, + {ProvaSpazio, 6}: 17, + {ProvaSpazio, 7}: 18, + {ProvaSpazio, 8}: 19, + {ProvaSpazio, 9}: 20, + {ProvaSpazio, 10}: 21, + {ProvaSpazio, 11}: 22, + {ProvaSpazio, 12}: 23, + {ProvaSpazio, 13}: 24, + {ProvaSpazio, 14}: 25, + {ProvaSpazio, 15}: 26, + {ProvaSpazio, 16}: 27, + {ProvaSpazio, 17}: 28, + {ProvaSpazio, 18}: 29, + {ProvaSpazio, 19}: 30, + {ProvaSpazio, 20}: 31, + {ProvaSpazio, 21}: 32, + {ProvaSpazio, 22}: 33, + {ProvaSpazio, 23}: 34, + {ProvaSpazio, 24}: 35, + {SecondTab, 0}: 37, + {SecondTab, 1}: 38, + {SecondTab, 2}: 39, + {SecondTab, 3}: 40, + {SecondTab, 4}: 41, }) require.EqualValues(t, sourceMap.toIno, map[int]InoLine{ 0: NotIno, 1: NotIno, - 2: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 0}, - 3: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 1}, - 4: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 2}, + 2: {ProvaSpazio, 0}, + 3: {ProvaSpazio, 1}, + 4: {ProvaSpazio, 2}, 5: NotIno, - 6: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 3}, // setup + 6: {ProvaSpazio, 3}, // setup 7: NotIno, - 8: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 8}, // loop + 8: {ProvaSpazio, 8}, // loop 9: NotIno, - 10: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 22}, // vino + 10: {ProvaSpazio, 22}, // vino 11: NotIno, - 12: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 1}, // secondFunction + 12: {SecondTab, 1}, // secondFunction 13: NotIno, - 14: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 3}, - 15: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 4}, - 16: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 5}, - 17: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 6}, - 18: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 7}, - 19: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 8}, - 20: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 9}, - 21: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 10}, - 22: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 11}, - 23: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 12}, - 24: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 13}, - 25: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 14}, - 26: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 15}, - 27: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 16}, - 28: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 17}, - 29: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 18}, - 30: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 19}, - 31: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 20}, - 32: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 21}, - 33: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 22}, - 34: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 23}, - 35: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 24}, - 36: {"not-ino", 0}, - 37: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 0}, - 38: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 1}, - 39: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 2}, - 40: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 3}, - 41: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 4}, + 14: {ProvaSpazio, 3}, + 15: {ProvaSpazio, 4}, + 16: {ProvaSpazio, 5}, + 17: {ProvaSpazio, 6}, + 18: {ProvaSpazio, 7}, + 19: {ProvaSpazio, 8}, + 20: {ProvaSpazio, 9}, + 21: {ProvaSpazio, 10}, + 22: {ProvaSpazio, 11}, + 23: {ProvaSpazio, 12}, + 24: {ProvaSpazio, 13}, + 25: {ProvaSpazio, 14}, + 26: {ProvaSpazio, 15}, + 27: {ProvaSpazio, 16}, + 28: {ProvaSpazio, 17}, + 29: {ProvaSpazio, 18}, + 30: {ProvaSpazio, 19}, + 31: {ProvaSpazio, 20}, + 32: {ProvaSpazio, 21}, + 33: {ProvaSpazio, 22}, + 34: {ProvaSpazio, 23}, + 35: {ProvaSpazio, 24}, + 36: {"/not-ino", 0}, + 37: {SecondTab, 0}, + 38: {SecondTab, 1}, + 39: {SecondTab, 2}, + 40: {SecondTab, 3}, + 41: {SecondTab, 4}, }) require.EqualValues(t, map[int]InoLine{ - 6: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 3}, // setup - 8: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 8}, // loop - 10: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino", 22}, // vino - 12: {"/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino", 1}, // secondFunction + 6: {ProvaSpazio, 3}, // setup + 8: {ProvaSpazio, 8}, // loop + 10: {ProvaSpazio, 22}, // vino + 12: {SecondTab, 1}, // secondFunction }, sourceMap.cppPreprocessed) dumpCppToInoMap(sourceMap.toIno) dumpInoToCppMap(sourceMap.toCpp)