@@ -59,7 +59,7 @@ type InoHandler struct {
59
59
sketchMapper * sourcemapper.InoMapper
60
60
sketchTrackedFilesCount int
61
61
docs map [lsp.DocumentURI ]* lsp.TextDocumentItem
62
- docHasDiagnostics map [lsp.DocumentURI ]bool
62
+ inoDocsWithDiagnostics map [lsp.DocumentURI ]bool
63
63
64
64
config lsp.BoardConfig
65
65
synchronizer Synchronizer
@@ -68,8 +68,8 @@ type InoHandler struct {
68
68
// NewInoHandler creates and configures an InoHandler.
69
69
func NewInoHandler (stdio io.ReadWriteCloser , board lsp.Board ) * InoHandler {
70
70
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 {},
73
73
config : lsp.BoardConfig {
74
74
SelectedBoard : board ,
75
75
},
@@ -667,31 +667,71 @@ func (handler *InoHandler) ino2cppTextDocumentIdentifier(doc lsp.TextDocumentIde
667
667
return res , err
668
668
}
669
669
670
- func (handler * InoHandler ) ino2cppDocumentURI (uri lsp.DocumentURI ) (lsp.DocumentURI , error ) {
670
+ func (handler * InoHandler ) ino2cppDocumentURI (inoURI lsp.DocumentURI ) (lsp.DocumentURI , error ) {
671
671
// Sketchbook/Sketch/Sketch.ino -> build-path/sketch/Sketch.ino.cpp
672
672
// Sketchbook/Sketch/AnotherTab.ino -> build-path/sketch/Sketch.ino.cpp (different section from above)
673
673
// Sketchbook/Sketch/AnotherFile.cpp -> build-path/sketch/AnotherFile.cpp (1:1)
674
674
// another/path/source.cpp -> unchanged
675
675
676
676
// Convert sketch path to build path
677
- inoPath := uri .AsPath ()
678
- cppPath := inoPath
679
-
677
+ inoPath := inoURI .AsPath ()
680
678
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 {
683
684
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 {
686
688
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
692
697
}
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
695
735
}
696
736
697
737
func (handler * InoHandler ) ino2cppTextDocumentPositionParams (params * lsp.TextDocumentPositionParams ) error {
@@ -1052,6 +1092,52 @@ func (handler *InoHandler) cpp2inoSymbolInformation(syms []lsp.SymbolInformation
1052
1092
// return symbols
1053
1093
}
1054
1094
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
+
1055
1141
// FromClangd handles a message received from clangd.
1056
1142
func (handler * InoHandler ) FromClangd (ctx context.Context , connection * jsonrpc2.Conn , req * jsonrpc2.Request ) (interface {}, error ) {
1057
1143
handler .synchronizer .DataMux .RLock ()
@@ -1073,59 +1159,49 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
1073
1159
log .Printf (" > %d:%d - %v: %s" , diag .Range .Start .Line , diag .Range .Start .Character , diag .Severity , diag .Code )
1074
1160
}
1075
1161
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 )
1089
1180
}
1090
1181
}
1091
1182
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
1104
1192
}
1105
1193
}
1194
+ }
1106
1195
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
1120
1198
}
1199
+ }
1121
1200
1201
+ if cleanUpInoDiagnostics {
1122
1202
// 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 ] {
1129
1205
// skip if we already sent updated diagnostics
1130
1206
continue
1131
1207
}
@@ -1142,9 +1218,9 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
1142
1218
}
1143
1219
}
1144
1220
1145
- handler .docHasDiagnostics = docsWithDiagnostics
1146
- return nil , err
1221
+ handler .inoDocsWithDiagnostics = inoDocsWithDiagnostics
1147
1222
}
1223
+ return nil , err
1148
1224
1149
1225
case * lsp.ApplyWorkspaceEditParams :
1150
1226
// "workspace/applyEdit"
0 commit comments