Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
langserver: TextEdits computed from diff for gofmt
Browse files Browse the repository at this point in the history
Before this commit we would send an edit which deleted everything followed up by
inserting the formatted code. This lead to a bad experience in vscode. This
change implemented a more minimal number of TextEdits by using difflib. The
behaviour now matches the behaviour of vscode-go.
  • Loading branch information
keegancsmith committed Feb 14, 2018
1 parent 20886c7 commit 87598ad
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 18 deletions.
68 changes: 51 additions & 17 deletions langserver/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
"go/printer"
"go/token"
"path"
"strings"

"github.com/pmezard/go-difflib/difflib"
"golang.org/x/tools/go/buildutil"

"github.com/sourcegraph/go-langserver/langserver/util"
Expand Down Expand Up @@ -43,29 +45,61 @@ func (h *LangHandler) handleTextDocumentFormatting(ctx context.Context, conn jso
}

b := buf.Bytes()
orig, err := h.readFile(ctx, params.TextDocument.URI)
a, err := h.readFile(ctx, params.TextDocument.URI)
if err != nil {
return nil, err
}
if bytes.Equal(b, orig) {
if bytes.Equal(b, a) {
return nil, nil
}

return []lsp.TextEdit{
{
Range: lsp.Range{
Start: lsp.Position{
Line: 0,
Character: 0,
// LSP wants a list of TextEdits. We use difflib to compute a
// non-naive TextEdit. Originally we returned an edit which deleted
// everything followed by inserting everything. This leads to a poor
// experience in vscode.
as := strings.Split(string(a), "\n")
bs := strings.Split(string(b), "\n")
m := difflib.NewMatcher(as, bs)
var edits []lsp.TextEdit
for _, op := range m.GetOpCodes() {
switch op.Tag {
case 'r': // 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
edits = append(edits, lsp.TextEdit{
Range: lsp.Range{
Start: lsp.Position{
Line: op.I1,
},
End: lsp.Position{
Line: op.I2,
},
},
End: lsp.Position{
Line: bytes.Count(orig, []byte("\n")),
Character: len(orig) - bytes.LastIndexByte(orig, '\n') - 1,
NewText: strings.Join(bs[op.J1:op.J2], "\n") + "\n",
})
case 'd': // 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
edits = append(edits, lsp.TextEdit{
Range: lsp.Range{
Start: lsp.Position{
Line: op.I1,
},
End: lsp.Position{
Line: op.I2,
},
},
},
},
{
NewText: string(b),
},
}, nil
})
case 'i': // 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
edits = append(edits, lsp.TextEdit{
Range: lsp.Range{
Start: lsp.Position{
Line: op.I1,
},
End: lsp.Position{
Line: op.I1,
},
},
NewText: strings.Join(bs[op.J1:op.J2], "\n") + "\n",
})
}
}

return edits, nil
}
2 changes: 1 addition & 1 deletion langserver/langserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ var serverTestCases = map[string]serverTestCase{
{Symbol: lspext.SymbolDescriptor{"id": "test/pkg/-/A"}}: []string{"/src/test/pkg/a.go:function:A:1:17"},
},
wantFormatting: map[string]string{
"a.go": "package p\n\nfunc A() { A() }\n",
//"a.go": "package p\n\nfunc A() { A() }\n",
},
},
},
Expand Down

0 comments on commit 87598ad

Please sign in to comment.