Skip to content

Commit d815cba

Browse files
author
Dylan Le
committed
internal/lsp: update LSP protocol for WorkspaceEdit
Change DocumentChanges field type from []TextDocumentEdit to []DocumentChanges. DocumentChanges is a union type of TextDocumentEdit and RenameFile used for renaming package feature. It distinguishes a renaming directory ops from a text document edit ops. For golang/go#41567 Change-Id: I25d106b34821effc53b712800f7175248e59ee25 Reviewed-on: https://go-review.googlesource.com/c/tools/+/427538 gopls-CI: kokoro <noreply+kokoro@google.com> Run-TryBot: Dylan Le <dungtuanle@google.com> Reviewed-by: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent 6a585a2 commit d815cba

11 files changed

+148
-64
lines changed

internal/lsp/cmd/imports.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ func (t *imports) Run(ctx context.Context, args ...string) error {
7474
continue
7575
}
7676
for _, c := range a.Edit.DocumentChanges {
77-
if fileURI(c.TextDocument.URI) == uri {
78-
edits = append(edits, c.Edits...)
77+
if c.TextDocumentEdit != nil {
78+
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
79+
edits = append(edits, c.TextDocumentEdit.Edits...)
80+
}
7981
}
8082
}
8183
}

internal/lsp/cmd/rename.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,12 @@ func (r *rename) Run(ctx context.Context, args ...string) error {
8181
var orderedURIs []string
8282
edits := map[span.URI][]protocol.TextEdit{}
8383
for _, c := range edit.DocumentChanges {
84-
uri := fileURI(c.TextDocument.URI)
85-
edits[uri] = append(edits[uri], c.Edits...)
86-
orderedURIs = append(orderedURIs, string(uri))
84+
// Todo: Add handler for RenameFile edits
85+
if c.TextDocumentEdit != nil {
86+
uri := fileURI(c.TextDocumentEdit.TextDocument.URI)
87+
edits[uri] = append(edits[uri], c.TextDocumentEdit.Edits...)
88+
orderedURIs = append(orderedURIs, string(uri))
89+
}
8790
}
8891
sort.Strings(orderedURIs)
8992
changeCount := len(orderedURIs)

internal/lsp/cmd/suggested_fix.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
103103
}
104104
if !from.HasPosition() {
105105
for _, c := range a.Edit.DocumentChanges {
106-
if fileURI(c.TextDocument.URI) == uri {
107-
edits = append(edits, c.Edits...)
106+
if c.TextDocumentEdit != nil {
107+
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
108+
edits = append(edits, c.TextDocumentEdit.Edits...)
109+
}
108110
}
109111
}
110112
continue
@@ -118,8 +120,10 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
118120
}
119121
if span.ComparePoint(from.Start(), spn.Start()) == 0 {
120122
for _, c := range a.Edit.DocumentChanges {
121-
if fileURI(c.TextDocument.URI) == uri {
122-
edits = append(edits, c.Edits...)
123+
if c.TextDocumentEdit != nil {
124+
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
125+
edits = append(edits, c.TextDocumentEdit.Edits...)
126+
}
123127
}
124128
}
125129
break
@@ -129,8 +133,10 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
129133
// If suggested fix is not a diagnostic, still must collect edits.
130134
if len(a.Diagnostics) == 0 {
131135
for _, c := range a.Edit.DocumentChanges {
132-
if fileURI(c.TextDocument.URI) == uri {
133-
edits = append(edits, c.Edits...)
136+
if c.TextDocumentEdit != nil {
137+
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
138+
edits = append(edits, c.TextDocumentEdit.Edits...)
139+
}
134140
}
135141
}
136142
}

internal/lsp/code_action.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -338,16 +338,18 @@ func extractionFixes(ctx context.Context, snapshot source.Snapshot, pkg source.P
338338
return actions, nil
339339
}
340340

341-
func documentChanges(fh source.VersionedFileHandle, edits []protocol.TextEdit) []protocol.TextDocumentEdit {
342-
return []protocol.TextDocumentEdit{
341+
func documentChanges(fh source.VersionedFileHandle, edits []protocol.TextEdit) []protocol.DocumentChanges {
342+
return []protocol.DocumentChanges{
343343
{
344-
TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
345-
Version: fh.Version(),
346-
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
347-
URI: protocol.URIFromSpanURI(fh.URI()),
344+
TextDocumentEdit: &protocol.TextDocumentEdit{
345+
TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
346+
Version: fh.Version(),
347+
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
348+
URI: protocol.URIFromSpanURI(fh.URI()),
349+
},
348350
},
351+
Edits: edits,
349352
},
350-
Edits: edits,
351353
},
352354
}
353355
}
@@ -378,20 +380,22 @@ func codeActionsMatchingDiagnostics(ctx context.Context, snapshot source.Snapsho
378380
func codeActionsForDiagnostic(ctx context.Context, snapshot source.Snapshot, sd *source.Diagnostic, pd *protocol.Diagnostic) ([]protocol.CodeAction, error) {
379381
var actions []protocol.CodeAction
380382
for _, fix := range sd.SuggestedFixes {
381-
var changes []protocol.TextDocumentEdit
383+
var changes []protocol.DocumentChanges
382384
for uri, edits := range fix.Edits {
383385
fh, err := snapshot.GetVersionedFile(ctx, uri)
384386
if err != nil {
385387
return nil, err
386388
}
387-
changes = append(changes, protocol.TextDocumentEdit{
388-
TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
389-
Version: fh.Version(),
390-
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
391-
URI: protocol.URIFromSpanURI(uri),
389+
changes = append(changes, protocol.DocumentChanges{
390+
TextDocumentEdit: &protocol.TextDocumentEdit{
391+
TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
392+
Version: fh.Version(),
393+
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
394+
URI: protocol.URIFromSpanURI(fh.URI()),
395+
},
392396
},
397+
Edits: edits,
393398
},
394-
Edits: edits,
395399
})
396400
}
397401
action := protocol.CodeAction{

internal/lsp/command.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,15 @@ func (c *commandHandler) ApplyFix(ctx context.Context, args command.ApplyFixArgs
144144
if err != nil {
145145
return err
146146
}
147+
var changes []protocol.DocumentChanges
148+
for _, edit := range edits {
149+
changes = append(changes, protocol.DocumentChanges{
150+
TextDocumentEdit: &edit,
151+
})
152+
}
147153
r, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
148154
Edit: protocol.WorkspaceEdit{
149-
DocumentChanges: edits,
155+
DocumentChanges: changes,
150156
},
151157
})
152158
if err != nil {
@@ -322,15 +328,19 @@ func (c *commandHandler) RemoveDependency(ctx context.Context, args command.Remo
322328
}
323329
response, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
324330
Edit: protocol.WorkspaceEdit{
325-
DocumentChanges: []protocol.TextDocumentEdit{{
326-
TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
327-
Version: deps.fh.Version(),
328-
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
329-
URI: protocol.URIFromSpanURI(deps.fh.URI()),
331+
DocumentChanges: []protocol.DocumentChanges{
332+
{
333+
TextDocumentEdit: &protocol.TextDocumentEdit{
334+
TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
335+
Version: deps.fh.Version(),
336+
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
337+
URI: protocol.URIFromSpanURI(deps.fh.URI()),
338+
},
339+
},
340+
Edits: edits,
330341
},
331342
},
332-
Edits: edits,
333-
}},
343+
},
334344
},
335345
})
336346
if err != nil {
@@ -544,9 +554,15 @@ func (s *Server) runGoModUpdateCommands(ctx context.Context, snapshot source.Sna
544554
if len(changes) == 0 {
545555
return nil
546556
}
557+
var documentChanges []protocol.DocumentChanges
558+
for _, change := range changes {
559+
documentChanges = append(documentChanges, protocol.DocumentChanges{
560+
TextDocumentEdit: &change,
561+
})
562+
}
547563
response, err := s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
548564
Edit: protocol.WorkspaceEdit{
549-
DocumentChanges: changes,
565+
DocumentChanges: documentChanges,
550566
},
551567
})
552568
if err != nil {

internal/lsp/fake/client.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,14 @@ func (c *Client) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceE
121121
return &protocol.ApplyWorkspaceEditResult{FailureReason: "Edit.Changes is unsupported"}, nil
122122
}
123123
for _, change := range params.Edit.DocumentChanges {
124-
if err := c.editor.applyProtocolEdit(ctx, change); err != nil {
125-
return nil, err
124+
// Todo: Add a handler for RenameFile edits
125+
if change.RenameFile != nil {
126+
panic("Fake editor does not support the RenameFile edits.")
127+
}
128+
if change.TextDocumentEdit != nil {
129+
if err := c.editor.applyProtocolEdit(ctx, *change.TextDocumentEdit); err != nil {
130+
return nil, err
131+
}
126132
}
127133
}
128134
return &protocol.ApplyWorkspaceEditResult{Applied: true}, nil

internal/lsp/fake/editor.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -843,14 +843,16 @@ func (e *Editor) ApplyQuickFixes(ctx context.Context, path string, rng *protocol
843843
// ApplyCodeAction applies the given code action.
844844
func (e *Editor) ApplyCodeAction(ctx context.Context, action protocol.CodeAction) error {
845845
for _, change := range action.Edit.DocumentChanges {
846-
path := e.sandbox.Workdir.URIToPath(change.TextDocument.URI)
847-
if int32(e.buffers[path].version) != change.TextDocument.Version {
848-
// Skip edits for old versions.
849-
continue
850-
}
851-
edits := convertEdits(change.Edits)
852-
if err := e.EditBuffer(ctx, path, edits); err != nil {
853-
return fmt.Errorf("editing buffer %q: %w", path, err)
846+
if change.TextDocumentEdit != nil {
847+
path := e.sandbox.Workdir.URIToPath(change.TextDocumentEdit.TextDocument.URI)
848+
if int32(e.buffers[path].version) != change.TextDocumentEdit.TextDocument.Version {
849+
// Skip edits for old versions.
850+
continue
851+
}
852+
edits := convertEdits(change.TextDocumentEdit.Edits)
853+
if err := e.EditBuffer(ctx, path, edits); err != nil {
854+
return fmt.Errorf("editing buffer %q: %w", path, err)
855+
}
854856
}
855857
}
856858
// Execute any commands. The specification says that commands are
@@ -1155,8 +1157,10 @@ func (e *Editor) Rename(ctx context.Context, path string, pos Pos, newName strin
11551157
return err
11561158
}
11571159
for _, change := range wsEdits.DocumentChanges {
1158-
if err := e.applyProtocolEdit(ctx, change); err != nil {
1159-
return err
1160+
if change.TextDocumentEdit != nil {
1161+
if err := e.applyProtocolEdit(ctx, *change.TextDocumentEdit); err != nil {
1162+
return err
1163+
}
11601164
}
11611165
}
11621166
return nil

internal/lsp/lsp_test.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,27 +1086,29 @@ func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.Prepare
10861086
}
10871087
}
10881088

1089-
func applyTextDocumentEdits(r *runner, edits []protocol.TextDocumentEdit) (map[span.URI]string, error) {
1089+
func applyTextDocumentEdits(r *runner, edits []protocol.DocumentChanges) (map[span.URI]string, error) {
10901090
res := map[span.URI]string{}
10911091
for _, docEdits := range edits {
1092-
uri := docEdits.TextDocument.URI.SpanURI()
1093-
var m *protocol.ColumnMapper
1094-
// If we have already edited this file, we use the edited version (rather than the
1095-
// file in its original state) so that we preserve our initial changes.
1096-
if content, ok := res[uri]; ok {
1097-
m = protocol.NewColumnMapper(uri, []byte(content))
1098-
} else {
1099-
var err error
1100-
if m, err = r.data.Mapper(uri); err != nil {
1092+
if docEdits.TextDocumentEdit != nil {
1093+
uri := docEdits.TextDocumentEdit.TextDocument.URI.SpanURI()
1094+
var m *protocol.ColumnMapper
1095+
// If we have already edited this file, we use the edited version (rather than the
1096+
// file in its original state) so that we preserve our initial changes.
1097+
if content, ok := res[uri]; ok {
1098+
m = protocol.NewColumnMapper(uri, []byte(content))
1099+
} else {
1100+
var err error
1101+
if m, err = r.data.Mapper(uri); err != nil {
1102+
return nil, err
1103+
}
1104+
}
1105+
res[uri] = string(m.Content)
1106+
sedits, err := source.FromProtocolEdits(m, docEdits.TextDocumentEdit.Edits)
1107+
if err != nil {
11011108
return nil, err
11021109
}
1110+
res[uri] = applyEdits(res[uri], sedits)
11031111
}
1104-
res[uri] = string(m.Content)
1105-
sedits, err := source.FromProtocolEdits(m, docEdits.Edits)
1106-
if err != nil {
1107-
return nil, err
1108-
}
1109-
res[uri] = applyEdits(res[uri], sedits)
11101112
}
11111113
return res, nil
11121114
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
package protocol
5+
6+
import (
7+
"encoding/json"
8+
"fmt"
9+
)
10+
11+
// DocumentChanges is a union of a file edit and directory rename operations
12+
// for package renaming feature. At most one field of this struct is non-nil.
13+
type DocumentChanges struct {
14+
TextDocumentEdit *TextDocumentEdit
15+
RenameFile *RenameFile
16+
}
17+
18+
func (d *DocumentChanges) UnmarshalJSON(data []byte) error {
19+
var m map[string]interface{}
20+
21+
if err := json.Unmarshal(data, &m); err != nil {
22+
return err
23+
}
24+
25+
if _, ok := m["textDocument"]; ok {
26+
d.TextDocumentEdit = new(TextDocumentEdit)
27+
return json.Unmarshal(data, d.TextDocumentEdit)
28+
}
29+
30+
d.RenameFile = new(RenameFile)
31+
return json.Unmarshal(data, d.RenameFile)
32+
}
33+
34+
func (d *DocumentChanges) MarshalJSON() ([]byte, error) {
35+
if d.TextDocumentEdit != nil {
36+
return json.Marshal(d.TextDocumentEdit)
37+
} else if d.RenameFile != nil {
38+
return json.Marshal(d.RenameFile)
39+
}
40+
return nil, fmt.Errorf("Empty DocumentChanges union value")
41+
}

internal/lsp/protocol/tsprotocol.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/lsp/rename.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func (s *Server) rename(ctx context.Context, params *protocol.RenameParams) (*pr
2222
return nil, err
2323
}
2424

25-
var docChanges []protocol.TextDocumentEdit
25+
var docChanges []protocol.DocumentChanges
2626
for uri, e := range edits {
2727
fh, err := snapshot.GetVersionedFile(ctx, uri)
2828
if err != nil {

0 commit comments

Comments
 (0)