Skip to content

Commit e5c82d6

Browse files
committed
Fixed publishDiagnostics for .cpp files
1 parent d9eeb0c commit e5c82d6

File tree

1 file changed

+140
-64
lines changed

1 file changed

+140
-64
lines changed

Diff for: handler/handler.go

+140-64
Original file line numberDiff line numberDiff line change
@@ -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.
6969
func 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

697737
func (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.
10561142
func (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

Comments
 (0)