@@ -59,7 +59,7 @@ type InoHandler struct {
5959 sketchMapper * sourcemapper.InoMapper
6060 sketchTrackedFilesCount int
6161 docs map [lsp.DocumentURI ]* lsp.TextDocumentItem
62- docHasDiagnostics map [lsp.DocumentURI ]bool
62+ inoDocsWithDiagnostics map [lsp.DocumentURI ]bool
6363
6464 config lsp.BoardConfig
6565 synchronizer Synchronizer
@@ -68,8 +68,8 @@ type InoHandler struct {
6868// NewInoHandler creates and configures an InoHandler.
6969func NewInoHandler (stdio io.ReadWriteCloser , board lsp.Board ) * InoHandler {
7070 handler := & InoHandler {
71- docs : map [lsp.DocumentURI ]* lsp.TextDocumentItem {},
72- docHasDiagnostics : map [lsp.DocumentURI ]bool {},
71+ docs : map [lsp.DocumentURI ]* lsp.TextDocumentItem {},
72+ inoDocsWithDiagnostics : map [lsp.DocumentURI ]bool {},
7373 config : lsp.BoardConfig {
7474 SelectedBoard : board ,
7575 },
@@ -667,31 +667,71 @@ func (handler *InoHandler) ino2cppTextDocumentIdentifier(doc lsp.TextDocumentIde
667667 return res , err
668668}
669669
670- func (handler * InoHandler ) ino2cppDocumentURI (uri lsp.DocumentURI ) (lsp.DocumentURI , error ) {
670+ func (handler * InoHandler ) ino2cppDocumentURI (inoURI lsp.DocumentURI ) (lsp.DocumentURI , error ) {
671671 // Sketchbook/Sketch/Sketch.ino -> build-path/sketch/Sketch.ino.cpp
672672 // Sketchbook/Sketch/AnotherTab.ino -> build-path/sketch/Sketch.ino.cpp (different section from above)
673673 // Sketchbook/Sketch/AnotherFile.cpp -> build-path/sketch/AnotherFile.cpp (1:1)
674674 // another/path/source.cpp -> unchanged
675675
676676 // Convert sketch path to build path
677- inoPath := uri .AsPath ()
678- cppPath := inoPath
679-
677+ inoPath := inoURI .AsPath ()
680678 if inoPath .Ext () == ".ino" {
681- cppPath = handler .buildSketchCpp
682- } else if inside , err := inoPath .IsInsideDir (handler .sketchRoot ); err != nil {
679+ return lsp .NewDocumentURIFromPath (handler .buildSketchCpp ), nil
680+ }
681+
682+ inside , err := inoPath .IsInsideDir (handler .sketchRoot )
683+ if err != nil {
683684 log .Printf (" could not determine if '%s' is inside '%s'" , inoPath , handler .sketchRoot )
684- return "" , unknownURI (uri )
685- } else if ! inside {
685+ return "" , unknownURI (inoURI )
686+ }
687+ if ! inside {
686688 log .Printf (" passing doc identifier to '%s' as-is" , inoPath )
687- } else if rel , err := handler .sketchRoot .RelTo (inoPath ); err != nil {
688- log .Printf (" could not determine rel-path of '%s' in '%s" , inoPath , handler .sketchRoot )
689- return "" , unknownURI (uri )
690- } else {
691- cppPath = handler .buildSketchRoot .JoinPath (rel )
689+ return inoURI , nil
690+ }
691+
692+ rel , err := handler .sketchRoot .RelTo (inoPath )
693+ if err == nil {
694+ cppPath := handler .buildSketchRoot .JoinPath (rel )
695+ log .Printf (" URI: '%s' -> '%s'" , inoPath , cppPath )
696+ return lsp .NewDocumentURIFromPath (cppPath ), nil
692697 }
693- log .Printf (" URI: '%s' -> '%s'" , inoPath , cppPath )
694- return lsp .NewDocumentURIFromPath (cppPath ), nil
698+
699+ log .Printf (" could not determine rel-path of '%s' in '%s': %s" , inoPath , handler .sketchRoot , err )
700+ return "" , err
701+ }
702+
703+ func (handler * InoHandler ) cpp2inoDocumentURI (cppURI lsp.DocumentURI , cppRange lsp.Range ) (lsp.DocumentURI , lsp.Range , error ) {
704+ // Sketchbook/Sketch/Sketch.ino <- build-path/sketch/Sketch.ino.cpp
705+ // Sketchbook/Sketch/AnotherTab.ino <- build-path/sketch/Sketch.ino.cpp (different section from above)
706+ // Sketchbook/Sketch/AnotherFile.cpp <- build-path/sketch/AnotherFile.cpp (1:1)
707+ // another/path/source.cpp <- unchanged
708+
709+ // Convert build path to sketch path
710+ cppPath := cppURI .AsPath ()
711+ if cppPath .EquivalentTo (handler .buildSketchCpp ) {
712+ inoPath , inoRange := handler .sketchMapper .CppToInoRange (cppRange )
713+ return lsp .NewDocumentURI (inoPath ), inoRange , nil
714+ }
715+
716+ inside , err := cppPath .IsInsideDir (handler .buildSketchRoot )
717+ if err != nil {
718+ log .Printf (" could not determine if '%s' is inside '%s'" , cppPath , handler .buildSketchRoot )
719+ return "" , lsp.Range {}, err
720+ }
721+ if ! inside {
722+ log .Printf (" keep doc identifier to '%s' as-is" , cppPath )
723+ return cppURI , cppRange , nil
724+ }
725+
726+ rel , err := handler .buildSketchRoot .RelTo (cppPath )
727+ if err == nil {
728+ inoPath := handler .sketchRoot .JoinPath (rel )
729+ log .Printf (" URI: '%s' -> '%s'" , cppPath , inoPath )
730+ return lsp .NewDocumentURIFromPath (inoPath ), cppRange , nil
731+ }
732+
733+ log .Printf (" could not determine rel-path of '%s' in '%s': %s" , cppPath , handler .buildSketchRoot , err )
734+ return "" , lsp.Range {}, err
695735}
696736
697737func (handler * InoHandler ) ino2cppTextDocumentPositionParams (params * lsp.TextDocumentPositionParams ) error {
@@ -1052,6 +1092,52 @@ func (handler *InoHandler) cpp2inoSymbolInformation(syms []lsp.SymbolInformation
10521092 // return symbols
10531093}
10541094
1095+ func (handler * InoHandler ) cpp2inoDiagnostics (cppDiags * lsp.PublishDiagnosticsParams ) ([]* lsp.PublishDiagnosticsParams , error ) {
1096+
1097+ if len (cppDiags .Diagnostics ) == 0 {
1098+ // If we receive the empty diagnostic on the preprocessed sketch,
1099+ // just return an empty diagnostic array.
1100+ if cppDiags .URI .AsPath ().EquivalentTo (handler .buildSketchCpp ) {
1101+ return []* lsp.PublishDiagnosticsParams {}, nil
1102+ }
1103+
1104+ inoURI , _ , err := handler .cpp2inoDocumentURI (cppDiags .URI , lsp.Range {})
1105+ return []* lsp.PublishDiagnosticsParams {
1106+ {
1107+ URI : inoURI ,
1108+ Diagnostics : []lsp.Diagnostic {},
1109+ },
1110+ }, err
1111+ }
1112+
1113+ convertedDiagnostics := map [lsp.DocumentURI ]* lsp.PublishDiagnosticsParams {}
1114+ for _ , cppDiag := range cppDiags .Diagnostics {
1115+ inoURI , inoRange , err := handler .cpp2inoDocumentURI (cppDiags .URI , cppDiag .Range )
1116+ if err != nil {
1117+ return nil , err
1118+ }
1119+
1120+ inoDiagParam , created := convertedDiagnostics [inoURI ]
1121+ if ! created {
1122+ inoDiagParam = & lsp.PublishDiagnosticsParams {
1123+ URI : inoURI ,
1124+ Diagnostics : []lsp.Diagnostic {},
1125+ }
1126+ convertedDiagnostics [inoURI ] = inoDiagParam
1127+ }
1128+
1129+ inoDiag := cppDiag
1130+ inoDiag .Range = inoRange
1131+ inoDiagParam .Diagnostics = append (inoDiagParam .Diagnostics , inoDiag )
1132+ }
1133+
1134+ inoDiagParams := []* lsp.PublishDiagnosticsParams {}
1135+ for _ , v := range convertedDiagnostics {
1136+ inoDiagParams = append (inoDiagParams , v )
1137+ }
1138+ return inoDiagParams , nil
1139+ }
1140+
10551141// FromClangd handles a message received from clangd.
10561142func (handler * InoHandler ) FromClangd (ctx context.Context , connection * jsonrpc2.Conn , req * jsonrpc2.Request ) (interface {}, error ) {
10571143 handler .synchronizer .DataMux .RLock ()
@@ -1073,59 +1159,49 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
10731159 log .Printf (" > %d:%d - %v: %s" , diag .Range .Start .Line , diag .Range .Start .Character , diag .Severity , diag .Code )
10741160 }
10751161
1076- if p .URI .AsPath ().EquivalentTo (handler .buildSketchCpp ) {
1077- // we should transform back N diagnostics of sketch.cpp.ino into
1078- // their .ino counter parts (that may span over multiple files...)
1079-
1080- convertedDiagnostics := map [string ][]lsp.Diagnostic {}
1081- for _ , cppDiag := range p .Diagnostics {
1082- inoSource , inoRange := handler .sketchMapper .CppToInoRange (cppDiag .Range )
1083- inoDiag := cppDiag
1084- inoDiag .Range = inoRange
1085- if inoDiags , ok := convertedDiagnostics [inoSource ]; ! ok {
1086- convertedDiagnostics [inoSource ] = []lsp.Diagnostic {inoDiag }
1087- } else {
1088- convertedDiagnostics [inoSource ] = append (inoDiags , inoDiag )
1162+ // the diagnostics on sketch.cpp.ino once mapped into their
1163+ // .ino counter parts may span over multiple .ino files...
1164+ inoDiagnostics , err := handler .cpp2inoDiagnostics (p )
1165+ if err != nil {
1166+ return nil , err
1167+ }
1168+ cleanUpInoDiagnostics := false
1169+ if len (inoDiagnostics ) == 0 {
1170+ cleanUpInoDiagnostics = true
1171+ }
1172+
1173+ // Push back to IDE the converted diagnostics
1174+ inoDocsWithDiagnostics := map [lsp.DocumentURI ]bool {}
1175+ for _ , inoDiag := range inoDiagnostics {
1176+ if enableLogging {
1177+ log .Printf ("<-- publishDiagnostics(%s):" , inoDiag .URI )
1178+ for _ , diag := range inoDiag .Diagnostics {
1179+ log .Printf (" > %d:%d - %v: %s" , diag .Range .Start .Line , diag .Range .Start .Character , diag .Severity , diag .Code )
10891180 }
10901181 }
10911182
1092- // Push back to IDE the converted diagnostics
1093- docsWithDiagnostics := map [lsp.DocumentURI ]bool {}
1094- for filename , inoDiags := range convertedDiagnostics {
1095- msg := lsp.PublishDiagnosticsParams {
1096- URI : lsp .NewDocumentURI (filename ),
1097- Diagnostics : inoDiags ,
1098- }
1099- docsWithDiagnostics [msg .URI ] = true
1100- if enableLogging {
1101- log .Printf ("<-- publishDiagnostics(%s):" , msg .URI )
1102- for _ , diag := range msg .Diagnostics {
1103- log .Printf (" > %d:%d - %v: %s" , diag .Range .Start .Line , diag .Range .Start .Character , diag .Severity , diag .Code )
1183+ // If we have an "undefined reference" in the .ino code trigger a
1184+ // check for newly created symbols (that in turn may trigger a
1185+ // new arduino-preprocessing of the sketch).
1186+ if inoDiag .URI .Ext () == ".ino" {
1187+ inoDocsWithDiagnostics [inoDiag .URI ] = true
1188+ cleanUpInoDiagnostics = true
1189+ for _ , diag := range inoDiag .Diagnostics {
1190+ if diag .Code == "undeclared_var_use_suggest" {
1191+ handler .buildSketchSymbolsCheck = true
11041192 }
11051193 }
1194+ }
11061195
1107- // If we have an "undefined reference" in the .ino code trigger a
1108- // check for newly created symbols (that in turn may trigger a
1109- // new arduino-preprocessing of the sketch).
1110- if msg .URI .Ext () == ".ino" {
1111- for _ , diag := range msg .Diagnostics {
1112- if diag .Code == "undeclared_var_use_suggest" {
1113- handler .buildSketchSymbolsCheck = true
1114- }
1115- }
1116- }
1117- if err := handler .StdioConn .Notify (ctx , "textDocument/publishDiagnostics" , msg ); err != nil {
1118- return nil , err
1119- }
1196+ if err := handler .StdioConn .Notify (ctx , "textDocument/publishDiagnostics" , inoDiag ); err != nil {
1197+ return nil , err
11201198 }
1199+ }
11211200
1201+ if cleanUpInoDiagnostics {
11221202 // Remove diagnostics from all .ino where there are no errors coming from clang
1123- for sourceURI := range handler .docs {
1124- if ! handler .docHasDiagnostics [sourceURI ] {
1125- // skip if the document didn't have previously sent diagnostics
1126- continue
1127- }
1128- if docsWithDiagnostics [sourceURI ] {
1203+ for sourceURI := range handler .inoDocsWithDiagnostics {
1204+ if inoDocsWithDiagnostics [sourceURI ] {
11291205 // skip if we already sent updated diagnostics
11301206 continue
11311207 }
@@ -1142,9 +1218,9 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
11421218 }
11431219 }
11441220
1145- handler .docHasDiagnostics = docsWithDiagnostics
1146- return nil , err
1221+ handler .inoDocsWithDiagnostics = inoDocsWithDiagnostics
11471222 }
1223+ return nil , err
11481224
11491225 case * lsp.ApplyWorkspaceEditParams :
11501226 // "workspace/applyEdit"
0 commit comments