Skip to content

Commit

Permalink
gopls/protocol: Allow AnnotatedTextEdits
Browse files Browse the repository at this point in the history
In the LSP specification, the Edits fields of a TextdocumentEdit
are a union type TextEdit | AnnotatedTextEdit. The latter has an
extra field that might be useful.

This CL contains one way of allowing AnnotatedTextEdits in this
context. The systematic naming of union type provides this one
with a long name, Or_TextDocumentEdit_edits_Elem. Fortunately
this name appears in very few places.

Fitting the new type in the existing gopls code requires
1. Conversions between []newtype and []TextEdit. This code
is AsTextEdits() and AsAnnotatedTextEdits() in protocol/edits.go
1a. Because gopls does not insist on go1.20, slices of the new
type cannot be used as the argument to protocol.NonNilSlice(),
so the second function cannot return nil, but must return a slice.
2. There are some places where previously a TextEdit was built.
These now require explicitly constructing an element of the new type.

This CL does not make any use of AnnotatedTextEdits, but it does
make their use possible.

Change-Id: Ib167f057068a5921c77572c43dc985da0e6b19bc
Reviewed-on: https://go-review.googlesource.com/c/tools/+/547295
Run-TryBot: Peter Weinberger <pjw@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
TryBot-Bypass: Peter Weinberger <pjw@google.com>
  • Loading branch information
pjweinbgo committed Dec 12, 2023
1 parent f40889d commit bbc30f1
Show file tree
Hide file tree
Showing 13 changed files with 63 additions and 20 deletions.
2 changes: 1 addition & 1 deletion gopls/internal/cmd/cmd.go
Expand Up @@ -554,7 +554,7 @@ func (cli *cmdClient) applyWorkspaceEdit(edit *protocol.WorkspaceEdit) error {
for _, c := range edit.DocumentChanges {
if c.TextDocumentEdit != nil {
uri := c.TextDocumentEdit.TextDocument.URI
edits[uri] = append(edits[uri], c.TextDocumentEdit.Edits...)
edits[uri] = append(edits[uri], protocol.AsTextEdits(c.TextDocumentEdit.Edits)...)
orderedURIs = append(orderedURIs, uri)
}
if c.RenameFile != nil {
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/cmd/imports.go
Expand Up @@ -71,7 +71,7 @@ func (t *imports) Run(ctx context.Context, args ...string) error {
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
edits = append(edits, protocol.AsTextEdits(c.TextDocumentEdit.Edits)...)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions gopls/internal/cmd/suggested_fix.go
Expand Up @@ -154,7 +154,7 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
edits = append(edits, protocol.AsTextEdits(c.TextDocumentEdit.Edits)...)
}
}
}
Expand All @@ -168,7 +168,7 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
edits = append(edits, protocol.AsTextEdits(c.TextDocumentEdit.Edits)...)
}
}
}
Expand All @@ -181,7 +181,7 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
edits = append(edits, protocol.AsTextEdits(c.TextDocumentEdit.Edits)...)
}
}
}
Expand Down
43 changes: 42 additions & 1 deletion gopls/internal/lsp/protocol/edits.go
Expand Up @@ -4,7 +4,11 @@

package protocol

import "golang.org/x/tools/internal/diff"
import (
"fmt"

"golang.org/x/tools/internal/diff"
)

// EditsFromDiffEdits converts diff.Edits to a non-nil slice of LSP TextEdits.
// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray
Expand Down Expand Up @@ -60,3 +64,40 @@ func ApplyEdits(m *Mapper, edits []TextEdit) ([]byte, []diff.Edit, error) {
out, err := diff.ApplyBytes(m.Content, diffEdits)
return out, diffEdits, err
}

// AsTextEdits converts a slice possibly containing AnnotatedTextEdits
// to a slice of TextEdits.
func AsTextEdits(edits []Or_TextDocumentEdit_edits_Elem) []TextEdit {
var result []TextEdit
for _, e := range edits {
var te TextEdit
if x, ok := e.Value.(AnnotatedTextEdit); ok {
te = x.TextEdit
} else if x, ok := e.Value.(TextEdit); ok {
te = x
} else {
panic(fmt.Sprintf("unexpected type %T, expected AnnotatedTextEdit or TextEdit", e.Value))
}
result = append(result, te)
}
return result
}

// AsAnnotatedTextEdits converts a slice of TextEdits
// to a slice of Or_TextDocumentEdit_edits_Elem.
// (returning a typed nil is required in server: in code_action.go and command.go))
func AsAnnotatedTextEdits(edits []TextEdit) []Or_TextDocumentEdit_edits_Elem {
if edits == nil {
return []Or_TextDocumentEdit_edits_Elem{}
}
var result []Or_TextDocumentEdit_edits_Elem
for _, e := range edits {
result = append(result, Or_TextDocumentEdit_edits_Elem{
Value: TextEdit{
Range: e.Range,
NewText: e.NewText,
},
})
}
return result
}
2 changes: 1 addition & 1 deletion gopls/internal/lsp/protocol/generate/tables.go
Expand Up @@ -24,6 +24,7 @@ const (
// A.B.C.D means that one of B or C would change to a pointer
// so a test or initialization would be needed
var goplsStar = map[prop]int{
{"AnnotatedTextEdit", "annotationId"}: wantOptStar,
{"ClientCapabilities", "textDocument"}: wantOpt, // A.B.C.D at fake/editor.go:255
{"ClientCapabilities", "window"}: wantOpt, // test failures
{"ClientCapabilities", "workspace"}: wantOpt, // test failures
Expand Down Expand Up @@ -85,7 +86,6 @@ var renameProp = map[prop]string{
{"ServerCapabilities", "semanticTokensProvider"}: "interface{}",
// slightly tricky
{"ServerCapabilities", "textDocumentSync"}: "interface{}",
{"TextDocumentEdit", "edits"}: "[]TextEdit",
{"TextDocumentSyncOptions", "save"}: "SaveOptions",
{"WorkspaceEdit", "documentChanges"}: "[]DocumentChanges",
}
Expand Down
4 changes: 2 additions & 2 deletions gopls/internal/lsp/protocol/tsprotocol.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion gopls/internal/lsp/source/change_quote.go
Expand Up @@ -85,7 +85,7 @@ func ConvertStringLiteral(pgf *ParsedGoFile, fh file.Handle, rng protocol.Range)
Version: fh.Version(),
TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI: fh.URI()},
},
Edits: pedits,
Edits: protocol.AsAnnotatedTextEdits(pedits),
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/lsp/source/change_signature.go
Expand Up @@ -176,7 +176,7 @@ func RemoveUnusedParameter(ctx context.Context, fh file.Handle, rng protocol.Ran
Version: fh.Version(),
TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI: uri},
},
Edits: pedits,
Edits: protocol.AsAnnotatedTextEdits(pedits),
},
})
}
Expand Down
8 changes: 5 additions & 3 deletions gopls/internal/lsp/source/fix.go
Expand Up @@ -130,9 +130,11 @@ func suggestedFixToEdits(ctx context.Context, snapshot *cache.Snapshot, fset *to
if err != nil {
return nil, err
}
te.Edits = append(te.Edits, protocol.TextEdit{
Range: rng,
NewText: string(edit.NewText),
te.Edits = append(te.Edits, protocol.Or_TextDocumentEdit_edits_Elem{
Value: protocol.TextEdit{
Range: rng,
NewText: string(edit.NewText),
},
})
}
var edits []protocol.TextDocumentEdit
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/server/code_action.go
Expand Up @@ -617,7 +617,7 @@ func documentChanges(fh file.Handle, edits []protocol.TextEdit) []protocol.Docum
URI: fh.URI(),
},
},
Edits: protocol.NonNilSlice(edits),
Edits: protocol.AsAnnotatedTextEdits(edits),
},
},
}
Expand Down
4 changes: 2 additions & 2 deletions gopls/internal/server/command.go
Expand Up @@ -451,7 +451,7 @@ func (c *commandHandler) RemoveDependency(ctx context.Context, args command.Remo
URI: deps.fh.URI(),
},
},
Edits: protocol.NonNilSlice(edits),
Edits: protocol.AsAnnotatedTextEdits(edits),
},
},
},
Expand Down Expand Up @@ -700,7 +700,7 @@ func collectFileEdits(ctx context.Context, snapshot *cache.Snapshot, uri protoco
URI: uri,
},
},
Edits: edits,
Edits: protocol.AsAnnotatedTextEdits(edits),
}}, nil
}

Expand Down
4 changes: 2 additions & 2 deletions gopls/internal/test/integration/fake/editor.go
Expand Up @@ -910,7 +910,7 @@ func (e *Editor) ApplyCodeAction(ctx context.Context, action protocol.CodeAction
// Skip edits for old versions.
continue
}
if err := e.EditBuffer(ctx, path, change.TextDocumentEdit.Edits); err != nil {
if err := e.EditBuffer(ctx, path, protocol.AsTextEdits(change.TextDocumentEdit.Edits)); err != nil {
return fmt.Errorf("editing buffer %q: %w", path, err)
}
}
Expand Down Expand Up @@ -1361,7 +1361,7 @@ func (e *Editor) applyTextDocumentEdit(ctx context.Context, change protocol.Text
return err
}
}
return e.EditBuffer(ctx, path, change.Edits)
return e.EditBuffer(ctx, path, protocol.AsTextEdits(change.Edits))
}

// Config returns the current editor configuration.
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/test/marker/marker_test.go
Expand Up @@ -1650,7 +1650,7 @@ func applyDocumentChanges(env *integration.Env, changes []protocol.DocumentChang
if err != nil {
return err
}
patched, _, err := protocol.ApplyEdits(mapper, change.TextDocumentEdit.Edits)
patched, _, err := protocol.ApplyEdits(mapper, protocol.AsTextEdits(change.TextDocumentEdit.Edits))
if err != nil {
return err
}
Expand Down

0 comments on commit bbc30f1

Please sign in to comment.