Skip to content

Commit cda483e

Browse files
julienduchesnejdbaldry
authored andcommitted
feat: On format, apply edits to single lines instead of replacing the whole file
Otherwise, vscode will jump to the end of the file every time the user saves Uses https://github.com/hexops/gotextdiff/ which in turn just copies the code used by the golang LSP: https://github.com/golang/tools/tree/master/internal/lsp/diff Closes #15 Signed-off-by: Julien Duchesne <[email protected]>
1 parent 5dc8ad9 commit cda483e

File tree

6 files changed

+125
-17
lines changed

6 files changed

+125
-17
lines changed

.github/workflows/test.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: go test
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request: {}
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- uses: actions/setup-go@v2
13+
- run: go test ./...
14+

diff.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package main
2+
3+
import (
4+
"github.com/hexops/gotextdiff/myers"
5+
"github.com/hexops/gotextdiff/span"
6+
"github.com/jdbaldry/go-language-server-protocol/lsp/protocol"
7+
)
8+
9+
func getTextEdits(before, after string) []protocol.TextEdit {
10+
edits := myers.ComputeEdits(span.URI("any"), before, after)
11+
12+
var result []protocol.TextEdit
13+
for _, edit := range edits {
14+
result = append(result, protocol.TextEdit{
15+
Range: protocol.Range{
16+
Start: protocol.Position{Line: uint32(edit.Span.Start().Line()) - 1, Character: uint32(edit.Span.Start().Column()) - 1},
17+
End: protocol.Position{Line: uint32(edit.Span.End().Line()) - 1, Character: uint32(edit.Span.End().Column()) - 1},
18+
},
19+
NewText: edit.NewText,
20+
})
21+
}
22+
23+
return result
24+
}

diff_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/jdbaldry/go-language-server-protocol/lsp/protocol"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestGetTextEdits(t *testing.T) {
11+
testCases := []struct {
12+
name string
13+
before, after string
14+
expected []protocol.TextEdit
15+
}{
16+
{
17+
name: "delete whole file",
18+
before: "one\ntwo\nthree",
19+
after: "",
20+
expected: []protocol.TextEdit{
21+
{
22+
Range: protocol.Range{
23+
Start: protocol.Position{Line: 0, Character: 0},
24+
End: protocol.Position{Line: 3, Character: 0},
25+
},
26+
NewText: "",
27+
},
28+
},
29+
},
30+
{
31+
name: "add one char (replaces the whole line)",
32+
before: "one\ntwo\nthree",
33+
after: "one\ntwoo\nthree",
34+
expected: []protocol.TextEdit{
35+
{
36+
Range: protocol.Range{
37+
Start: protocol.Position{Line: 1, Character: 0},
38+
End: protocol.Position{Line: 2, Character: 0},
39+
},
40+
NewText: "",
41+
},
42+
{
43+
Range: protocol.Range{
44+
Start: protocol.Position{Line: 2, Character: 0},
45+
End: protocol.Position{Line: 2, Character: 0},
46+
},
47+
NewText: "twoo\n",
48+
},
49+
},
50+
},
51+
{
52+
name: "delete a line",
53+
before: "one\ntwo\nthree",
54+
after: "one\nthree",
55+
expected: []protocol.TextEdit{
56+
{
57+
Range: protocol.Range{
58+
Start: protocol.Position{Line: 1, Character: 0},
59+
End: protocol.Position{Line: 2, Character: 0},
60+
},
61+
NewText: "",
62+
},
63+
},
64+
},
65+
}
66+
67+
for _, tc := range testCases {
68+
t.Run(tc.name, func(t *testing.T) {
69+
got := getTextEdits(tc.before, tc.after)
70+
assert.Equal(t, tc.expected, got)
71+
})
72+
}
73+
}

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ go 1.16
44

55
require (
66
github.com/google/go-jsonnet v0.17.0
7+
github.com/hexops/gotextdiff v1.0.3
78
github.com/jdbaldry/go-language-server-protocol v0.0.0-20211013214444-3022da0884b2
9+
github.com/stretchr/testify v1.4.0
810
)

go.sum

+10
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,35 @@
11
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
23
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
34
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
45
github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY=
56
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
7+
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
8+
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
69
github.com/jdbaldry/go-language-server-protocol v0.0.0-20211013214444-3022da0884b2 h1:t0A10MAY8Z3eeBIBzlzrPpdjsag6Biuxq8iMCHmdGU8=
710
github.com/jdbaldry/go-language-server-protocol v0.0.0-20211013214444-3022da0884b2/go.mod h1:Hp8QDOEcdn4aDZ+DFTda+smIB0b5MvII4Q0Jo0y2VkA=
11+
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
812
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
913
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
14+
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
1015
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1116
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
1217
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
1318
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
19+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1420
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
21+
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
1522
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
1623
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
24+
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
1725
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
1826
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1927
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2028
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
2129
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2230
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
31+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
2332
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2433
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
34+
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
2535
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

server.go

+2-17
Original file line numberDiff line numberDiff line change
@@ -410,23 +410,8 @@ func (s *server) Formatting(ctx context.Context, params *protocol.DocumentFormat
410410
fmt.Fprintln(os.Stderr, err)
411411
return nil, err
412412
}
413-
// TODO(#15): Consider applying individual edits instead of replacing the whole file when formatting.
414-
return []protocol.TextEdit{
415-
{
416-
Range: protocol.Range{
417-
Start: protocol.Position{Line: 0, Character: 0},
418-
End: protocol.Position{Line: 0, Character: 0},
419-
},
420-
NewText: formatted,
421-
},
422-
{
423-
Range: protocol.Range{
424-
Start: protocol.Position{Line: 0, Character: 0},
425-
End: protocol.Position{Line: uint32(strings.Count(formatted+doc.item.Text, "\n")), Character: ^uint32(0)},
426-
},
427-
NewText: "",
428-
},
429-
}, nil
413+
414+
return getTextEdits(doc.item.Text, formatted), nil
430415
}
431416

432417
func (s *server) Hover(context.Context, *protocol.HoverParams) (*protocol.Hover, error) {

0 commit comments

Comments
 (0)