From 53ad329bd98f6eea2106d46f2c1655cbbdd28242 Mon Sep 17 00:00:00 2001 From: Rob Findley Date: Mon, 20 Nov 2023 16:24:08 -0500 Subject: [PATCH] gopls/internal/lsp/source: move edit logic into the protocol package Change-Id: I8a2dcafc109bf298b6edfe7a7029f8b9098880d6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/543920 Reviewed-by: Alan Donovan LUCI-TryBot-Result: Go LUCI Auto-Submit: Robert Findley --- gopls/internal/cmd/cmd.go | 3 +- gopls/internal/lsp/cache/mod_tidy.go | 2 +- gopls/internal/lsp/cache/pkg.go | 1 - gopls/internal/lsp/command.go | 4 +- gopls/internal/lsp/fake/edit.go | 3 +- gopls/internal/lsp/mod/format.go | 2 +- gopls/internal/lsp/protocol/edits.go | 62 +++++++++++++++++++ gopls/internal/lsp/regtest/marker.go | 13 ++-- gopls/internal/lsp/source/change_quote.go | 2 +- gopls/internal/lsp/source/change_signature.go | 2 +- gopls/internal/lsp/source/completion/util.go | 2 +- gopls/internal/lsp/source/format.go | 55 +--------------- gopls/internal/lsp/source/rename.go | 2 +- gopls/internal/lsp/work/format.go | 2 +- 14 files changed, 80 insertions(+), 75 deletions(-) create mode 100644 gopls/internal/lsp/protocol/edits.go diff --git a/gopls/internal/cmd/cmd.go b/gopls/internal/cmd/cmd.go index 83577f6c06f..08aaa851b7b 100644 --- a/gopls/internal/cmd/cmd.go +++ b/gopls/internal/cmd/cmd.go @@ -29,7 +29,6 @@ import ( "golang.org/x/tools/gopls/internal/lsp/filecache" "golang.org/x/tools/gopls/internal/lsp/lsprpc" "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" "golang.org/x/tools/gopls/internal/settings" "golang.org/x/tools/internal/constraints" "golang.org/x/tools/internal/diff" @@ -585,7 +584,7 @@ func applyTextEdits(mapper *protocol.Mapper, edits []protocol.TextEdit, flags *E if len(edits) == 0 { return nil } - newContent, renameEdits, err := source.ApplyProtocolEdits(mapper, edits) + newContent, renameEdits, err := protocol.ApplyEdits(mapper, edits) if err != nil { return err } diff --git a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_tidy.go index 19642590e67..31a35a52a86 100644 --- a/gopls/internal/lsp/cache/mod_tidy.go +++ b/gopls/internal/lsp/cache/mod_tidy.go @@ -457,7 +457,7 @@ func switchDirectness(req *modfile.Require, m *protocol.Mapper, computeEdits set } // Calculate the edits to be made due to the change. edits := computeEdits(string(m.Content), string(newContent)) - return ToProtocolEdits(m, edits) + return protocol.EditsFromDiffEdits(m, edits) } // missingModuleForImport creates an error for a given import path that comes diff --git a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go index 2ab4694ff18..44dcfa3c300 100644 --- a/gopls/internal/lsp/cache/pkg.go +++ b/gopls/internal/lsp/cache/pkg.go @@ -75,7 +75,6 @@ var ( RemoveIntermediateTestVariants = source.RemoveIntermediateTestVariants IsCommandLineArguments = source.IsCommandLineArguments BundleQuickFixes = source.BundleQuickFixes - ToProtocolEdits = source.ToProtocolEdits NewFilterer = source.NewFilterer ) diff --git a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go index 3ff48b62013..ce1b35866a2 100644 --- a/gopls/internal/lsp/command.go +++ b/gopls/internal/lsp/command.go @@ -471,7 +471,7 @@ func dropDependency(snapshot source.Snapshot, pm *source.ParsedModule, modulePat } // Calculate the edits to be made due to the change. diff := snapshot.Options().ComputeEdits(string(pm.Mapper.Content), string(newContent)) - return source.ToProtocolEdits(pm.Mapper, diff) + return protocol.EditsFromDiffEdits(pm.Mapper, diff) } func (c *commandHandler) Test(ctx context.Context, uri protocol.DocumentURI, tests, benchmarks []string) error { @@ -677,7 +677,7 @@ func collectFileEdits(ctx context.Context, snapshot *cache.Snapshot, uri protoco m := protocol.NewMapper(fh.URI(), oldContent) diff := snapshot.Options().ComputeEdits(string(oldContent), string(newContent)) - edits, err := source.ToProtocolEdits(m, diff) + edits, err := protocol.EditsFromDiffEdits(m, diff) if err != nil { return nil, err } diff --git a/gopls/internal/lsp/fake/edit.go b/gopls/internal/lsp/fake/edit.go index 40762f2ff1a..2fac24bf710 100644 --- a/gopls/internal/lsp/fake/edit.go +++ b/gopls/internal/lsp/fake/edit.go @@ -6,7 +6,6 @@ package fake import ( "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" "golang.org/x/tools/internal/diff" ) @@ -36,7 +35,7 @@ func EditToChangeEvent(e protocol.TextEdit) protocol.TextDocumentContentChangeEv // and returns a new slice containing the lines of the patched file. // It is a wrapper around diff.Apply; see that function for preconditions. func applyEdits(mapper *protocol.Mapper, edits []protocol.TextEdit, windowsLineEndings bool) ([]byte, error) { - diffEdits, err := source.FromProtocolEdits(mapper, edits) + diffEdits, err := protocol.EditsToDiffEdits(mapper, edits) if err != nil { return nil, err } diff --git a/gopls/internal/lsp/mod/format.go b/gopls/internal/lsp/mod/format.go index b04baa259f6..7b0c6b1dab6 100644 --- a/gopls/internal/lsp/mod/format.go +++ b/gopls/internal/lsp/mod/format.go @@ -27,5 +27,5 @@ func Format(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]pr } // Calculate the edits to be made due to the change. diffs := snapshot.Options().ComputeEdits(string(pm.Mapper.Content), string(formatted)) - return source.ToProtocolEdits(pm.Mapper, diffs) + return protocol.EditsFromDiffEdits(pm.Mapper, diffs) } diff --git a/gopls/internal/lsp/protocol/edits.go b/gopls/internal/lsp/protocol/edits.go new file mode 100644 index 00000000000..44694c5593e --- /dev/null +++ b/gopls/internal/lsp/protocol/edits.go @@ -0,0 +1,62 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +import "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 +func EditsFromDiffEdits(m *Mapper, edits []diff.Edit) ([]TextEdit, error) { + // LSP doesn't require TextEditArray to be sorted: + // this is the receiver's concern. But govim, and perhaps + // other clients have historically relied on the order. + edits = append([]diff.Edit(nil), edits...) + diff.SortEdits(edits) + + result := make([]TextEdit, len(edits)) + for i, edit := range edits { + rng, err := m.OffsetRange(edit.Start, edit.End) + if err != nil { + return nil, err + } + result[i] = TextEdit{ + Range: rng, + NewText: edit.New, + } + } + return result, nil +} + +// EditsToDiffEdits converts LSP TextEdits to diff.Edits. +// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray +func EditsToDiffEdits(m *Mapper, edits []TextEdit) ([]diff.Edit, error) { + if edits == nil { + return nil, nil + } + result := make([]diff.Edit, len(edits)) + for i, edit := range edits { + start, end, err := m.RangeOffsets(edit.Range) + if err != nil { + return nil, err + } + result[i] = diff.Edit{ + Start: start, + End: end, + New: edit.NewText, + } + } + return result, nil +} + +// ApplyEdits applies the patch (edits) to m.Content and returns the result. +// It also returns the edits converted to diff-package form. +func ApplyEdits(m *Mapper, edits []TextEdit) ([]byte, []diff.Edit, error) { + diffEdits, err := EditsToDiffEdits(m, edits) + if err != nil { + return nil, nil, err + } + out, err := diff.ApplyBytes(m.Content, diffEdits) + return out, diffEdits, err +} diff --git a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/marker.go index a96b409b358..e2793960e75 100644 --- a/gopls/internal/lsp/regtest/marker.go +++ b/gopls/internal/lsp/regtest/marker.go @@ -33,7 +33,6 @@ import ( "golang.org/x/tools/gopls/internal/lsp/lsprpc" "golang.org/x/tools/gopls/internal/lsp/protocol" "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/lsp/source" "golang.org/x/tools/gopls/internal/lsp/tests" "golang.org/x/tools/gopls/internal/lsp/tests/compare" "golang.org/x/tools/internal/diff" @@ -1675,9 +1674,7 @@ func acceptCompletionMarker(mark marker, src protocol.Location, label string, go } filename := mark.path() mapper := mark.mapper() - patched, _, err := source.ApplyProtocolEdits(mapper, append([]protocol.TextEdit{ - *selected.TextEdit, - }, selected.AdditionalTextEdits...)) + patched, _, err := protocol.ApplyEdits(mapper, append([]protocol.TextEdit{*selected.TextEdit}, selected.AdditionalTextEdits...)) if err != nil { mark.errorf("ApplyProtocolEdits failed: %v", err) @@ -1739,7 +1736,7 @@ func foldingRangeMarker(mark marker, g *Golden) { mark.errorf("Editor.Mapper(%s) failed: %v", filename, err) return } - got, _, err := source.ApplyProtocolEdits(mapper, edits) + got, _, err := protocol.ApplyEdits(mapper, edits) if err != nil { mark.errorf("ApplyProtocolEdits failed: %v", err) return @@ -1766,7 +1763,7 @@ func formatMarker(mark marker, golden *Golden) { mark.errorf("Editor.Mapper(%s) failed: %v", filename, err) } - got, _, err = source.ApplyProtocolEdits(mapper, edits) + got, _, err = protocol.ApplyEdits(mapper, edits) if err != nil { mark.errorf("ApplyProtocolEdits failed: %v", err) return @@ -2008,7 +2005,7 @@ func applyDocumentChanges(env *Env, changes []protocol.DocumentChanges, fileChan if err != nil { return err } - patched, _, err := source.ApplyProtocolEdits(mapper, change.TextDocumentEdit.Edits) + patched, _, err := protocol.ApplyEdits(mapper, change.TextDocumentEdit.Edits) if err != nil { return err } @@ -2400,7 +2397,7 @@ func inlayhintsMarker(mark marker, g *Golden) { } m := mark.mapper() - got, _, err := source.ApplyProtocolEdits(m, edits) + got, _, err := protocol.ApplyEdits(m, edits) if err != nil { mark.errorf("ApplyProtocolEdits: %v", err) return diff --git a/gopls/internal/lsp/source/change_quote.go b/gopls/internal/lsp/source/change_quote.go index 6bb7f37d31c..4c2661d39e2 100644 --- a/gopls/internal/lsp/source/change_quote.go +++ b/gopls/internal/lsp/source/change_quote.go @@ -69,7 +69,7 @@ func ConvertStringLiteral(pgf *ParsedGoFile, fh file.Handle, rng protocol.Range) End: end, New: newText, }} - pedits, err := ToProtocolEdits(pgf.Mapper, edits) + pedits, err := protocol.EditsFromDiffEdits(pgf.Mapper, edits) if err != nil { bug.Reportf("failed to convert diff.Edit to protocol.TextEdit:%v", err) return protocol.CodeAction{}, false diff --git a/gopls/internal/lsp/source/change_signature.go b/gopls/internal/lsp/source/change_signature.go index dcd11858c7b..5f3062920dd 100644 --- a/gopls/internal/lsp/source/change_signature.go +++ b/gopls/internal/lsp/source/change_signature.go @@ -164,7 +164,7 @@ func RemoveUnusedParameter(ctx context.Context, fh file.Handle, rng protocol.Ran } edits := diff.Bytes(before, after) mapper := protocol.NewMapper(uri, before) - pedits, err := ToProtocolEdits(mapper, edits) + pedits, err := protocol.EditsFromDiffEdits(mapper, edits) if err != nil { return nil, fmt.Errorf("computing edits for %s: %v", uri, err) } diff --git a/gopls/internal/lsp/source/completion/util.go b/gopls/internal/lsp/source/completion/util.go index 4b6ec09a092..e29e9f56b3b 100644 --- a/gopls/internal/lsp/source/completion/util.go +++ b/gopls/internal/lsp/source/completion/util.go @@ -316,7 +316,7 @@ func (c *completer) editText(from, to token.Pos, newText string) ([]protocol.Tex if err != nil { return nil, err // can't happen: from/to came from c } - return source.ToProtocolEdits(c.mapper, []diff.Edit{{ + return protocol.EditsFromDiffEdits(c.mapper, []diff.Edit{{ Start: start, End: end, New: newText, diff --git a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/format.go index 9f3597cdc1d..d36d50b72af 100644 --- a/gopls/internal/lsp/source/format.go +++ b/gopls/internal/lsp/source/format.go @@ -306,7 +306,7 @@ func computeTextEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, defer done() edits := snapshot.Options().ComputeEdits(string(pgf.Src), formatted) - return ToProtocolEdits(pgf.Mapper, edits) + return protocol.EditsFromDiffEdits(pgf.Mapper, edits) } // protocolEditsFromSource converts text edits to LSP edits using the original @@ -333,57 +333,6 @@ func protocolEditsFromSource(src []byte, edits []diff.Edit) ([]protocol.TextEdit return result, nil } -// ToProtocolEdits 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 func ToProtocolEdits(m *protocol.Mapper, edits []diff.Edit) ([]protocol.TextEdit, error) { - // LSP doesn't require TextEditArray to be sorted: - // this is the receiver's concern. But govim, and perhaps - // other clients have historically relied on the order. - edits = append([]diff.Edit(nil), edits...) - diff.SortEdits(edits) - - result := make([]protocol.TextEdit, len(edits)) - for i, edit := range edits { - rng, err := m.OffsetRange(edit.Start, edit.End) - if err != nil { - return nil, err - } - result[i] = protocol.TextEdit{ - Range: rng, - NewText: edit.New, - } - } - return result, nil -} - -// FromProtocolEdits converts LSP TextEdits to diff.Edits. -// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray -func FromProtocolEdits(m *protocol.Mapper, edits []protocol.TextEdit) ([]diff.Edit, error) { - if edits == nil { - return nil, nil - } - result := make([]diff.Edit, len(edits)) - for i, edit := range edits { - start, end, err := m.RangeOffsets(edit.Range) - if err != nil { - return nil, err - } - result[i] = diff.Edit{ - Start: start, - End: end, - New: edit.NewText, - } - } - return result, nil -} - -// ApplyProtocolEdits applies the patch (edits) to m.Content and returns the result. -// It also returns the edits converted to diff-package form. -func ApplyProtocolEdits(m *protocol.Mapper, edits []protocol.TextEdit) ([]byte, []diff.Edit, error) { - diffEdits, err := FromProtocolEdits(m, edits) - if err != nil { - return nil, nil, err - } - out, err := diff.ApplyBytes(m.Content, diffEdits) - return out, diffEdits, err + return protocol.EditsFromDiffEdits(m, edits) } diff --git a/gopls/internal/lsp/source/rename.go b/gopls/internal/lsp/source/rename.go index 936ef6a5a5c..42a22d22cb1 100644 --- a/gopls/internal/lsp/source/rename.go +++ b/gopls/internal/lsp/source/rename.go @@ -276,7 +276,7 @@ func Rename(ctx context.Context, snapshot Snapshot, f file.Handle, pp protocol.P return nil, false, err } m := protocol.NewMapper(uri, data) - protocolEdits, err := ToProtocolEdits(m, edits) + protocolEdits, err := protocol.EditsFromDiffEdits(m, edits) if err != nil { return nil, false, err } diff --git a/gopls/internal/lsp/work/format.go b/gopls/internal/lsp/work/format.go index 4fc6dd771ed..ef869526c7e 100644 --- a/gopls/internal/lsp/work/format.go +++ b/gopls/internal/lsp/work/format.go @@ -25,5 +25,5 @@ func Format(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]pr formatted := modfile.Format(pw.File.Syntax) // Calculate the edits to be made due to the change. diffs := snapshot.Options().ComputeEdits(string(pw.Mapper.Content), string(formatted)) - return source.ToProtocolEdits(pw.Mapper, diffs) + return protocol.EditsFromDiffEdits(pw.Mapper, diffs) }