1
0
mirror of https://github.com/golang/go synced 2024-10-01 04:18:33 -06:00
go/internal/lsp/cmd/rename.go
Rebecca Stambler e33b02e766 internal/lsp: use versioned URIs in rename and code actions
This change adds support for returning versions along with file URIs, so
that the client can know when to apply changes. The version is not yet
propagated along to the internal/lsp/cache package, so this change will
have no effect (VS Code ignores a version of 0 and still applies the
changes).

A few minor changes made in the rename code (to remove the view
parameter). Some minor staticcheck fixes.

Updates golang/go#35243

Change-Id: Icc26bd9d9e5703c699f555424b94034c97b01d6f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/206882
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2019-11-13 05:52:40 +00:00

126 lines
3.4 KiB
Go

// 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"
"io/ioutil"
"os"
"path/filepath"
"sort"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/tool"
errors "golang.org/x/xerrors"
)
// rename implements the rename verb for gopls.
type rename struct {
Diff bool `flag:"d" help:"display diffs instead of rewriting files"`
Write bool `flag:"w" help:"write result to (source) file instead of stdout"`
app *Application
}
func (r *rename) Name() string { return "rename" }
func (r *rename) Usage() string { return "<position>" }
func (r *rename) ShortHelp() string { return "rename selected identifier" }
func (r *rename) DetailedHelp(f *flag.FlagSet) {
fmt.Fprint(f.Output(), `
Example:
$ # 1-based location (:line:column or :#position) of the thing to change
$ gopls rename helper/helper.go:8:6
$ gopls rename helper/helper.go:#53
gopls rename flags are:
`)
f.PrintDefaults()
}
// Run renames the specified identifier and either;
// - if -w is specified, updates the file(s) in place;
// - if -d is specified, prints out unified diffs of the changes; or
// - otherwise, prints the new versions to stdout.
func (r *rename) Run(ctx context.Context, args ...string) error {
if len(args) != 2 {
return tool.CommandLineErrorf("definition expects 2 arguments (position, new name)")
}
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.RenameParams{
TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
Position: loc.Range.Start,
NewName: args[1],
}
edit, err := conn.Rename(ctx, &p)
if err != nil {
return err
}
var orderedURIs []string
edits := map[span.URI][]protocol.TextEdit{}
for _, c := range edit.DocumentChanges {
uri := span.URI(c.TextDocument.URI)
edits[uri] = append(edits[uri], c.Edits...)
orderedURIs = append(orderedURIs, c.TextDocument.URI)
}
sort.Strings(orderedURIs)
changeCount := len(orderedURIs)
for _, u := range orderedURIs {
uri := span.URI(u)
cmdFile := conn.AddFile(ctx, uri)
filename := cmdFile.uri.Filename()
// convert LSP-style edits to []diff.TextEdit cuz Spans are handy
renameEdits, err := source.FromProtocolEdits(cmdFile.mapper, edits[uri])
if err != nil {
return errors.Errorf("%v: %v", edits, err)
}
newContent := diff.ApplyEdits(string(cmdFile.mapper.Content), renameEdits)
switch {
case r.Write:
fmt.Fprintln(os.Stderr, filename)
err := os.Rename(filename, filename+".orig")
if err != nil {
return errors.Errorf("%v: %v", edits, err)
}
ioutil.WriteFile(filename, []byte(newContent), 0644)
case r.Diff:
diffs := diff.ToUnified(filename+".orig", filename, string(cmdFile.mapper.Content), renameEdits)
fmt.Print(diffs)
default:
if len(orderedURIs) > 1 {
fmt.Printf("%s:\n", filepath.Base(filename))
}
fmt.Print(string(newContent))
if changeCount > 1 { // if this wasn't last change, print newline
fmt.Println()
}
changeCount -= 1
}
}
return nil
}