Skip to content

Commit 9831476

Browse files
authored
Merge pull request #40 from bcmi-labs/formatter
Implemented "textDocument/formatter"
2 parents 69048b8 + a5afeeb commit 9831476

File tree

2 files changed

+142
-52
lines changed

2 files changed

+142
-52
lines changed

Diff for: handler/handler.go

+87-42
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr
126126
}
127127

128128
// Handle LSP methods: transform parameters and send to clangd
129-
var uri lsp.DocumentURI
129+
var inoURI, cppURI lsp.DocumentURI
130130

131131
params, err := lsp.ReadParams(req.Method, req.Params)
132132
if err != nil {
@@ -146,7 +146,7 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr
146146

147147
case *lsp.DidOpenTextDocumentParams:
148148
// method "textDocument/didOpen"
149-
uri = p.TextDocument.URI
149+
inoURI = p.TextDocument.URI
150150
log.Printf("--> didOpen(%s@%d as '%s')", p.TextDocument.URI, p.TextDocument.Version, p.TextDocument.LanguageID)
151151

152152
res, err := handler.didOpen(ctx, p)
@@ -161,7 +161,7 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr
161161

162162
case *lsp.DidChangeTextDocumentParams:
163163
// notification "textDocument/didChange"
164-
uri = p.TextDocument.URI
164+
inoURI = p.TextDocument.URI
165165
log.Printf("--> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version)
166166
for _, change := range p.ContentChanges {
167167
log.Printf(" > %s -> %s", change.Range, strconv.Quote(change.Text))
@@ -186,33 +186,33 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr
186186

187187
case *lsp.CompletionParams:
188188
// method: "textDocument/completion"
189-
uri = p.TextDocument.URI
189+
inoURI = p.TextDocument.URI
190190
log.Printf("--> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character)
191191

192192
err = handler.ino2cppTextDocumentPositionParams(&p.TextDocumentPositionParams)
193193
log.Printf(" --> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character)
194194

195195
case *lsp.CodeActionParams:
196196
// method "textDocument/codeAction"
197-
uri = p.TextDocument.URI
197+
inoURI = p.TextDocument.URI
198198
log.Printf("--> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start)
199199

200200
p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument)
201201
if err != nil {
202202
break
203203
}
204204
if p.TextDocument.URI.AsPath().EquivalentTo(handler.buildSketchCpp) {
205-
p.Range = handler.sketchMapper.InoToCppLSPRange(uri, p.Range)
205+
p.Range = handler.sketchMapper.InoToCppLSPRange(inoURI, p.Range)
206206
for index := range p.Context.Diagnostics {
207207
r := &p.Context.Diagnostics[index].Range
208-
*r = handler.sketchMapper.InoToCppLSPRange(uri, *r)
208+
*r = handler.sketchMapper.InoToCppLSPRange(inoURI, *r)
209209
}
210210
}
211211
log.Printf(" --> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start)
212212

213213
case *lsp.HoverParams:
214214
// method: "textDocument/hover"
215-
uri = p.TextDocument.URI
215+
inoURI = p.TextDocument.URI
216216
doc := &p.TextDocumentPositionParams
217217
log.Printf("--> hover(%s:%d:%d)\n", doc.TextDocument.URI, doc.Position.Line, doc.Position.Character)
218218

@@ -221,16 +221,24 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr
221221

222222
case *lsp.DocumentSymbolParams:
223223
// method "textDocument/documentSymbol"
224-
uri = p.TextDocument.URI
224+
inoURI = p.TextDocument.URI
225225
log.Printf("--> documentSymbol(%s)", p.TextDocument.URI)
226226

227227
p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument)
228228
log.Printf(" --> documentSymbol(%s)", p.TextDocument.URI)
229229

230+
case *lsp.DocumentFormattingParams:
231+
// method "textDocument/formatting"
232+
inoURI = p.TextDocument.URI
233+
log.Printf("--> formatting(%s)", p.TextDocument.URI)
234+
p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument)
235+
cppURI = p.TextDocument.URI
236+
log.Printf(" --> formatting(%s)", p.TextDocument.URI)
237+
230238
case *lsp.DidSaveTextDocumentParams: // "textDocument/didSave":
231239
log.Printf("--X " + req.Method)
232240
return nil, nil
233-
uri = p.TextDocument.URI
241+
inoURI = p.TextDocument.URI
234242
p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument)
235243
case *lsp.DidCloseTextDocumentParams: // "textDocument/didClose":
236244
log.Printf("--X " + req.Method)
@@ -249,32 +257,27 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr
249257
case *lsp.TextDocumentPositionParams: // "textDocument/documentHighlight":
250258
log.Printf("--X " + req.Method)
251259
return nil, nil
252-
uri = p.TextDocument.URI
260+
inoURI = p.TextDocument.URI
253261
err = handler.ino2cppTextDocumentPositionParams(p)
254262
case *lsp.ReferenceParams: // "textDocument/references":
255263
log.Printf("--X " + req.Method)
256264
return nil, nil
257-
uri = p.TextDocument.URI
265+
inoURI = p.TextDocument.URI
258266
err = handler.ino2cppTextDocumentPositionParams(&p.TextDocumentPositionParams)
259-
case *lsp.DocumentFormattingParams: // "textDocument/formatting":
260-
log.Printf("--X " + req.Method)
261-
return nil, nil
262-
uri = p.TextDocument.URI
263-
p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument)
264267
case *lsp.DocumentRangeFormattingParams: // "textDocument/rangeFormatting":
265268
log.Printf("--X " + req.Method)
266269
return nil, nil
267-
uri = p.TextDocument.URI
270+
inoURI = p.TextDocument.URI
268271
err = handler.ino2cppDocumentRangeFormattingParams(p)
269272
case *lsp.DocumentOnTypeFormattingParams: // "textDocument/onTypeFormatting":
270273
log.Printf("--X " + req.Method)
271274
return nil, nil
272-
uri = p.TextDocument.URI
275+
inoURI = p.TextDocument.URI
273276
err = handler.ino2cppDocumentOnTypeFormattingParams(p)
274277
case *lsp.RenameParams: // "textDocument/rename":
275278
log.Printf("--X " + req.Method)
276279
return nil, nil
277-
uri = p.TextDocument.URI
280+
inoURI = p.TextDocument.URI
278281
err = handler.ino2cppRenameParams(p)
279282
case *lsp.DidChangeWatchedFilesParams: // "workspace/didChangeWatchedFiles":
280283
log.Printf("--X " + req.Method)
@@ -324,7 +327,7 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr
324327

325328
// Transform and return the result
326329
if result != nil {
327-
result = handler.transformClangdResult(req.Method, uri, result)
330+
result = handler.transformClangdResult(req.Method, inoURI, cppURI, result)
328331
}
329332
return result, err
330333
}
@@ -416,7 +419,7 @@ func (handler *InoHandler) refreshCppDocumentSymbols() error {
416419
if err != nil {
417420
return errors.WithMessage(err, "quering source code symbols")
418421
}
419-
result = handler.transformClangdResult("textDocument/documentSymbol", cppURI, result)
422+
result = handler.transformClangdResult("textDocument/documentSymbol", cppURI, "", result)
420423
if symbols, ok := result.([]lsp.DocumentSymbol); !ok {
421424
return errors.WithMessage(err, "quering source code symbols (2)")
422425
} else {
@@ -702,8 +705,17 @@ func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange l
702705
// Convert build path to sketch path
703706
cppPath := cppURI.AsPath()
704707
if cppPath.EquivalentTo(handler.buildSketchCpp) {
705-
inoPath, inoRange := handler.sketchMapper.CppToInoRange(cppRange)
706-
return lsp.NewDocumentURI(inoPath), inoRange, nil
708+
inoPath, inoRange, err := handler.sketchMapper.CppToInoRangeOk(cppRange)
709+
if err == nil {
710+
log.Printf(" URI: converted %s to %s:%s", cppRange, inoPath, inoRange)
711+
} else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok {
712+
log.Printf(" URI: converted %s to %s:%s (END LINE ADJUSTED)", cppRange, inoPath, inoRange)
713+
err = nil
714+
} else {
715+
log.Printf(" URI: ERROR: %s", err)
716+
handler.sketchMapper.DebugLogAll()
717+
}
718+
return lsp.NewDocumentURI(inoPath), inoRange, err
707719
}
708720

709721
inside, err := cppPath.IsInsideDir(handler.buildSketchRoot)
@@ -817,8 +829,8 @@ func (handler *InoHandler) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *ls
817829
return &newEdit
818830
}
819831

820-
func (handler *InoHandler) transformClangdResult(method string, uri lsp.DocumentURI, result interface{}) interface{} {
821-
cppToIno := uri != "" && uri.AsPath().EquivalentTo(handler.buildSketchCpp)
832+
func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI lsp.DocumentURI, result interface{}) interface{} {
833+
cppToIno := inoURI != "" && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp)
822834

823835
switch r := result.(type) {
824836
case *lsp.Hover:
@@ -853,7 +865,7 @@ func (handler *InoHandler) transformClangdResult(method string, uri lsp.Document
853865

854866
if r.DocumentSymbolArray != nil {
855867
// Treat the input as []DocumentSymbol
856-
return handler.cpp2inoDocumentSymbols(*r.DocumentSymbolArray, uri)
868+
return handler.cpp2inoDocumentSymbols(*r.DocumentSymbolArray, inoURI)
857869
} else if r.SymbolInformationArray != nil {
858870
// Treat the input as []SymbolInformation
859871
return handler.cpp2inoSymbolInformation(*r.SymbolInformationArray)
@@ -873,11 +885,35 @@ func (handler *InoHandler) transformClangdResult(method string, uri lsp.Document
873885
}
874886
(*r)[i] = lsp.CommandOrCodeAction{
875887
Command: handler.Cpp2InoCommand(item.Command),
876-
CodeAction: handler.cpp2inoCodeAction(item.CodeAction, uri),
888+
CodeAction: handler.cpp2inoCodeAction(item.CodeAction, inoURI),
877889
}
878890
}
879891
log.Printf("<-- codeAction(%d elements)", len(*r))
880892

893+
case *[]lsp.TextEdit:
894+
// Method: "textDocument/rangeFormatting"
895+
// Method: "textDocument/onTypeFormatting"
896+
// Method: "textDocument/formatting"
897+
log.Printf(" <-- %s %s textEdit(%d elements)", method, cppURI, len(*r))
898+
for _, edit := range *r {
899+
log.Printf(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText))
900+
}
901+
sketchEdits, err := handler.cpp2inoTextEdits(cppURI, *r)
902+
if err != nil {
903+
log.Printf("ERROR converting textEdits: %s", err)
904+
return nil
905+
}
906+
907+
inoEdits, ok := sketchEdits[inoURI]
908+
if !ok {
909+
inoEdits = []lsp.TextEdit{}
910+
}
911+
log.Printf("<-- %s %s textEdit(%d elements)", method, inoURI, len(inoEdits))
912+
for _, edit := range inoEdits {
913+
log.Printf(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText))
914+
}
915+
return &inoEdits
916+
881917
// case "textDocument/definition":
882918
// fallthrough
883919
// case "textDocument/typeDefinition":
@@ -890,15 +926,7 @@ func (handler *InoHandler) transformClangdResult(method string, uri lsp.Document
890926
}
891927
case *[]lsp.DocumentHighlight: // "textDocument/documentHighlight":
892928
for index := range *r {
893-
handler.cpp2inoDocumentHighlight(&(*r)[index], uri)
894-
}
895-
// case "textDocument/formatting":
896-
// fallthrough
897-
// case "textDocument/rangeFormatting":
898-
// fallthrough
899-
case *[]lsp.TextEdit: // "textDocument/onTypeFormatting":
900-
for index := range *r {
901-
handler.cpp2inoTextEdit(&(*r)[index], uri)
929+
handler.cpp2inoDocumentHighlight(&(*r)[index], inoURI)
902930
}
903931
case *lsp.WorkspaceEdit: // "textDocument/rename":
904932
return handler.cpp2inoWorkspaceEdit(r)
@@ -1013,11 +1041,28 @@ func (handler *InoHandler) cpp2inoDocumentHighlight(highlight *lsp.DocumentHighl
10131041
// }
10141042
}
10151043

1016-
func (handler *InoHandler) cpp2inoTextEdit(edit *lsp.TextEdit, uri lsp.DocumentURI) {
1017-
panic("not implemented")
1018-
// if data, ok := handler.data[uri]; ok {
1019-
// _, edit.Range = data.sourceMap.CppToInoRange(edit.Range)
1020-
// }
1044+
func (handler *InoHandler) cpp2inoTextEdits(cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) {
1045+
res := map[lsp.DocumentURI][]lsp.TextEdit{}
1046+
for _, cppEdit := range cppEdits {
1047+
inoURI, inoEdit, err := handler.cpp2inoTextEdit(cppURI, cppEdit)
1048+
if err != nil {
1049+
return nil, err
1050+
}
1051+
inoEdits, ok := res[inoURI]
1052+
if !ok {
1053+
inoEdits = []lsp.TextEdit{}
1054+
}
1055+
inoEdits = append(inoEdits, inoEdit)
1056+
res[inoURI] = inoEdits
1057+
}
1058+
return res, nil
1059+
}
1060+
1061+
func (handler *InoHandler) cpp2inoTextEdit(cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) {
1062+
inoURI, inoRange, err := handler.cpp2inoDocumentURI(cppURI, cppEdit.Range)
1063+
inoEdit := cppEdit
1064+
inoEdit.Range = inoRange
1065+
return inoURI, inoEdit, err
10211066
}
10221067

10231068
func (handler *InoHandler) cpp2inoDocumentSymbols(origSymbols []lsp.DocumentSymbol, origURI lsp.DocumentURI) []lsp.DocumentSymbol {

Diff for: handler/sourcemapper/ino.go

+55-10
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"bufio"
55
"bytes"
66
"fmt"
7+
"log"
78
"sort"
89
"strconv"
910
"strings"
1011

1112
"github.com/bcmi-labs/arduino-language-server/handler/textutils"
1213
"github.com/bcmi-labs/arduino-language-server/lsp"
14+
"github.com/pkg/errors"
1315
)
1416

1517
// InoMapper is a mapping between the .ino sketch and the preprocessed .cpp file
@@ -78,17 +80,50 @@ func (s *InoMapper) CppToInoLine(targetLine int) (string, int) {
7880
return res.File, res.Line
7981
}
8082

81-
// CppToInoRange converts a target (.cpp) lsp.Range into a source.ino:lsp.Range
82-
func (s *InoMapper) CppToInoRange(r lsp.Range) (string, lsp.Range) {
83-
startFile, startLine := s.CppToInoLine(r.Start.Line)
84-
endFile, endLine := s.CppToInoLine(r.End.Line)
85-
res := r
86-
res.Start.Line = startLine
87-
res.End.Line = endLine
88-
if startFile != endFile {
89-
panic("invalid range conversion")
83+
// CppToInoRange converts a target (.cpp) lsp.Range into a source.ino:lsp.Range.
84+
// It will panic if the range spans across multiple ino files.
85+
func (s *InoMapper) CppToInoRange(cppRange lsp.Range) (string, lsp.Range) {
86+
inoFile, inoRange, err := s.CppToInoRangeOk(cppRange)
87+
if err != nil {
88+
panic(err.Error())
9089
}
91-
return startFile, res
90+
return inoFile, inoRange
91+
}
92+
93+
// AdjustedRangeErr is returned if the range overlaps with a non-ino section by just the
94+
// last newline character.
95+
type AdjustedRangeErr struct{}
96+
97+
func (e AdjustedRangeErr) Error() string {
98+
return "the range has been adjusted to allow final newline"
99+
}
100+
101+
// CppToInoRangeOk converts a target (.cpp) lsp.Range into a source.ino:lsp.Range.
102+
// It returns an error if the range spans across multiple ino files.
103+
// If the range ends on the beginning of a new line in another .ino file, the range
104+
// is adjusted and AdjustedRangeErr is reported as err: the range may be still valid.
105+
func (s *InoMapper) CppToInoRangeOk(cppRange lsp.Range) (string, lsp.Range, error) {
106+
inoFile, startLine := s.CppToInoLine(cppRange.Start.Line)
107+
endInoFile, endLine := s.CppToInoLine(cppRange.End.Line)
108+
inoRange := cppRange
109+
inoRange.Start.Line = startLine
110+
inoRange.End.Line = endLine
111+
if inoFile == endInoFile {
112+
// All done
113+
return inoFile, inoRange, nil
114+
}
115+
116+
// Special case: the last line ends up in the "not-ino" area
117+
if inoRange.End.Character == 0 {
118+
if checkFile, checkLine := s.CppToInoLine(cppRange.End.Line - 1); checkFile == inoFile {
119+
// Adjust the range and return it with an AdjustedRange notification
120+
inoRange.End.Line = checkLine + 1
121+
return inoFile, inoRange, AdjustedRangeErr{}
122+
}
123+
}
124+
125+
// otherwise the range is not recoverable, just report error
126+
return inoFile, inoRange, errors.Errorf("invalid range conversion %s -> %s:%d-%s:%d", cppRange, inoFile, startLine, endInoFile, endLine)
92127
}
93128

94129
// CppToInoLineOk converts a target (.cpp) line into a source (.ino) line and
@@ -319,3 +354,13 @@ func dumpInoToCppMap(s map[InoLine]int) {
319354
fmt.Printf("%s:%d -> %d\n", inoLine.File, inoLine.Line, cppLine)
320355
}
321356
}
357+
358+
// DebugLogAll dumps the internal status of the mapper
359+
func (s *InoMapper) DebugLogAll() {
360+
cpp := strings.Split(s.CppText.Text, "\n")
361+
log.Printf(" > Current sketchmapper content:")
362+
for l, cppLine := range cpp {
363+
inoFile, inoLine := s.CppToInoLine(l)
364+
log.Printf(" %3d: %-40s : %s:%d", l, cppLine, inoFile, inoLine)
365+
}
366+
}

0 commit comments

Comments
 (0)