From 1cc6d1ef6c743b46817e84e7fde3eae82816dc9d Mon Sep 17 00:00:00 2001 From: Rohan Verma Date: Tue, 4 Feb 2020 06:09:33 +0000 Subject: [PATCH] tools/gopls: add cmd support for prepare_rename This change adds command line support for prepare_rename. Updates golang/go#32875 Change-Id: I7f155b9c8329c0faa26a320abab162730a7916ad GitHub-Last-Rev: 118e846420d7c56fbf0e4f416c8fd6a52e9638a8 GitHub-Pull-Request: golang/tools#188 Reviewed-on: https://go-review.googlesource.com/c/tools/+/207579 Run-TryBot: Rebecca Stambler Reviewed-by: Rebecca Stambler --- internal/lsp/cmd/cmd.go | 1 + internal/lsp/cmd/prepare_rename.go | 85 ++++++++++++++++++++++++ internal/lsp/cmd/test/cmdtest.go | 4 -- internal/lsp/cmd/test/prepare_rename.go | 45 +++++++++++++ internal/lsp/protocol/tsserver.go | 2 +- internal/lsp/testdata/good/good1.go | 2 +- internal/lsp/testdata/summary.txt.golden | 2 +- 7 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 internal/lsp/cmd/prepare_rename.go create mode 100644 internal/lsp/cmd/test/prepare_rename.go diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go index 6409b651c0..7856af134c 100644 --- a/internal/lsp/cmd/cmd.go +++ b/internal/lsp/cmd/cmd.go @@ -174,6 +174,7 @@ func (app *Application) featureCommands() []tool.Application { &implementation{app: app}, &imports{app: app}, &links{app: app}, + &prepareRename{app: app}, &query{app: app}, &references{app: app}, &rename{app: app}, diff --git a/internal/lsp/cmd/prepare_rename.go b/internal/lsp/cmd/prepare_rename.go new file mode 100644 index 0000000000..14eccade83 --- /dev/null +++ b/internal/lsp/cmd/prepare_rename.go @@ -0,0 +1,85 @@ +// Copyright 2019 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 cmd + +import ( + "context" + "flag" + "fmt" + + "golang.org/x/tools/internal/lsp/protocol" + "golang.org/x/tools/internal/span" + "golang.org/x/tools/internal/tool" +) + +// prepareRename implements the prepare_rename verb for gopls. +type prepareRename struct { + app *Application +} + +func (r *prepareRename) Name() string { return "prepare_rename" } +func (r *prepareRename) Usage() string { return "" } +func (r *prepareRename) ShortHelp() string { return "test validity of a rename operation at location" } +func (r *prepareRename) DetailedHelp(f *flag.FlagSet) { + fmt.Fprint(f.Output(), ` +Example: + + $ # 1-indexed location (:line:column or :#offset) of the target identifier + $ gopls prepare_rename helper/helper.go:8:6 + $ gopls prepare_rename helper/helper.go:#53 + + gopls prepare_rename flags are: +`) + f.PrintDefaults() +} + +func (r *prepareRename) Run(ctx context.Context, args ...string) error { + if len(args) != 1 { + return tool.CommandLineErrorf("prepare_rename expects 1 argument (file)") + } + + conn, err := r.app.connect(ctx) + if err != nil { + return err + } + defer conn.terminate(ctx) + + from := span.Parse(args[0]) + file := conn.AddFile(ctx, from.URI()) + if file.err != nil { + return file.err + } + loc, err := file.mapper.Location(from) + if err != nil { + return err + } + p := protocol.PrepareRenameParams{ + TextDocumentPositionParams: protocol.TextDocumentPositionParams{ + TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI}, + Position: loc.Range.Start, + }, + } + result, err := conn.PrepareRename(ctx, &p) + if err != nil { + return fmt.Errorf("prepare_rename failed: %v", err) + } + if result == nil { + return fmt.Errorf("request is not valid at the given position") + } + + resRange, ok := result.(protocol.Range) + if !ok { + return fmt.Errorf("prepare_rename failed to convert result to range, got: %T", result) + } + + l := protocol.Location{Range: resRange} + s, err := file.mapper.Span(l) + if err != nil { + return err + } + + fmt.Println(s) + return nil +} diff --git a/internal/lsp/cmd/test/cmdtest.go b/internal/lsp/cmd/test/cmdtest.go index f0aab4b97c..71a1fb6c96 100644 --- a/internal/lsp/cmd/test/cmdtest.go +++ b/internal/lsp/cmd/test/cmdtest.go @@ -95,10 +95,6 @@ func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completi //TODO: add command line completions tests when it works } -func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) { - //TODO: add command line prepare rename tests when it works -} - func (r *runner) RunGoplsCmd(t testing.TB, args ...string) (string, string) { rStdout, wStdout, err := os.Pipe() if err != nil { diff --git a/internal/lsp/cmd/test/prepare_rename.go b/internal/lsp/cmd/test/prepare_rename.go new file mode 100644 index 0000000000..3d69edd97c --- /dev/null +++ b/internal/lsp/cmd/test/prepare_rename.go @@ -0,0 +1,45 @@ +// Copyright 2019 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 cmdtest + +import ( + "fmt" + "testing" + + "golang.org/x/tools/internal/lsp/protocol" + "golang.org/x/tools/internal/lsp/source" + "golang.org/x/tools/internal/span" +) + +func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) { + m, err := r.data.Mapper(src.URI()) + if err != nil { + t.Errorf("prepare_rename failed: %v", err) + } + + var ( + target = fmt.Sprintf("%v", src) + args = []string{"prepare_rename", target} + stdOut, stdErr = r.NormalizeGoplsCmd(t, args...) + expect string + ) + + if want.Text == "" { + if stdErr != "" { + t.Errorf("prepare_rename failed for %s,\nexpected:\n`%v`\ngot:\n`%v`", target, expect, stdErr) + } + return + } + + ws, err := m.Span(protocol.Location{Range: want.Range}) + if err != nil { + t.Errorf("prepare_rename failed: %v", err) + } + + expect = r.Normalize(fmt.Sprintln(ws)) + if expect != stdOut { + t.Errorf("prepare_rename failed for %s expected:\n`%s`\ngot:\n`%s`\n", target, expect, stdOut) + } +} diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go index 24a9ad1390..e9497324b8 100644 --- a/internal/lsp/protocol/tsserver.go +++ b/internal/lsp/protocol/tsserver.go @@ -921,7 +921,7 @@ func (s *serverDispatcher) Rename(ctx context.Context, params *RenameParams) (*W } func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (interface{} /* Range | struct{; Range Range`json:"range"`; Placeholder string`json:"placeholder"`; } | nil*/, error) { - var result interface{} /* Range | struct{; Range Range`json:"range"`; Placeholder string`json:"placeholder"`; } | nil*/ + var result Range /* Range | struct{; Range Range`json:"range"`; Placeholder string`json:"placeholder"`; } | nil*/ if err := s.Conn.Call(ctx, "textDocument/prepareRename", params, &result); err != nil { return nil, err } diff --git a/internal/lsp/testdata/good/good1.go b/internal/lsp/testdata/good/good1.go index 826b114c34..dd50bd6f38 100644 --- a/internal/lsp/testdata/good/good1.go +++ b/internal/lsp/testdata/good/good1.go @@ -2,7 +2,7 @@ package good //@diag("package", "no_diagnostics", "") import ( _ "go/ast" //@prepare("go/ast", "_", "_") - "golang.org/x/tools/internal/lsp/types" //@item(types_import, "types", "\"golang.org/x/tools/internal/lsp/types\"", "package"),prepare("types","\"", "types") + "golang.org/x/tools/internal/lsp/types" //@item(types_import, "types", "\"golang.org/x/tools/internal/lsp/types\"", "package") ) func random() int { //@item(good_random, "random", "func() int", "func") diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden index 790be1e1a8..a6f8450437 100644 --- a/internal/lsp/testdata/summary.txt.golden +++ b/internal/lsp/testdata/summary.txt.golden @@ -16,7 +16,7 @@ TypeDefinitionsCount = 2 HighlightsCount = 52 ReferencesCount = 8 RenamesCount = 23 -PrepareRenamesCount = 8 +PrepareRenamesCount = 7 SymbolsCount = 1 SignaturesCount = 23 LinksCount = 8