github.com/tiagovtristao/plz@v13.4.0+incompatible/tools/build_langserver/langserver/reformat.go (about) 1 package langserver 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "github.com/thought-machine/please/tools/build_langserver/lsp" 8 9 "github.com/bazelbuild/buildtools/build" 10 "github.com/sourcegraph/jsonrpc2" 11 ) 12 13 const reformatMethod = "textDocument/formatting" 14 15 func (h *LsHandler) handleReformatting(ctx context.Context, req *jsonrpc2.Request) (result interface{}, err error) { 16 if req.Params == nil { 17 return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} 18 } 19 20 var params lsp.DocumentFormattingParams 21 if err := json.Unmarshal(*req.Params, ¶ms); err != nil { 22 return nil, err 23 } 24 25 documentURI, err := getURIAndHandleErrors(params.TextDocument.URI, reformatMethod) 26 if err != nil { 27 return nil, err 28 } 29 30 h.mu.Lock() 31 defer h.mu.Unlock() 32 33 edits, err := h.getFormatEdits(documentURI) 34 if err != nil { 35 log.Warning("error occurred when formatting file: %s, skipping", err) 36 } 37 38 log.Info("formatting document with edits: %s", edits) 39 return edits, err 40 } 41 42 func (h *LsHandler) getFormatEdits(uri lsp.DocumentURI) ([]*lsp.TextEdit, error) { 43 44 filePath, err := GetPathFromURL(uri, "file") 45 if err != nil { 46 return nil, err 47 } 48 49 doc, ok := h.workspace.documents[uri] 50 if !ok { 51 return nil, fmt.Errorf("document not opened: %s", uri) 52 } 53 54 content := JoinLines(doc.textInEdit, true) 55 bytecontent := []byte(content) 56 57 var f *build.File 58 if h.analyzer.IsBuildFile(uri) { 59 f, err = build.ParseBuild(filePath, bytecontent) 60 if err != nil { 61 return nil, err 62 } 63 } else { 64 f, err = build.ParseDefault(filePath, bytecontent) 65 if err != nil { 66 return nil, err 67 } 68 } 69 70 reformatted := build.Format(f) 71 72 return getEdits(content, string(reformatted)), nil 73 } 74 75 func getEdits(before string, after string) []*lsp.TextEdit { 76 beforeLines := SplitLines(before, true) 77 afterLines := SplitLines(after, true) 78 79 var edits []*lsp.TextEdit 80 for i, line := range afterLines { 81 82 eRange := lsp.Range{ 83 Start: lsp.Position{ 84 Line: i, 85 Character: 0, 86 }, 87 End: lsp.Position{ 88 Line: i, 89 Character: 0, 90 }, 91 } 92 93 if i <= len(beforeLines)-1 { 94 if line == beforeLines[i] { 95 continue 96 } 97 98 if i < len(beforeLines)-1 { 99 eRange.End.Line = i + 1 100 } else if beforeLines[i] != "" { 101 // This is to ensure the original line gets overridden if there is no newline after: 102 // usually if the last line is new line, it gets formatted correctly with startline:currentline, endline:currline + 1 103 // however this does not apply we want to format the last line 104 eRange.End.Character = len(beforeLines[i]) - 1 105 } 106 } 107 108 edit := &lsp.TextEdit{ 109 Range: eRange, 110 NewText: line, 111 } 112 113 edits = append(edits, edit) 114 } 115 116 return edits 117 }