github.com/v2fly/tools@v0.100.0/internal/lsp/cmd/imports.go (about) 1 // Copyright 2019 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 5 package cmd 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 "io/ioutil" 12 13 "github.com/v2fly/tools/internal/lsp/diff" 14 "github.com/v2fly/tools/internal/lsp/protocol" 15 "github.com/v2fly/tools/internal/lsp/source" 16 "github.com/v2fly/tools/internal/span" 17 "github.com/v2fly/tools/internal/tool" 18 errors "golang.org/x/xerrors" 19 ) 20 21 // imports implements the import verb for gopls. 22 type imports struct { 23 Diff bool `flag:"d" help:"display diffs instead of rewriting files"` 24 Write bool `flag:"w" help:"write result to (source) file instead of stdout"` 25 26 app *Application 27 } 28 29 func (t *imports) Name() string { return "imports" } 30 func (t *imports) Usage() string { return "<filename>" } 31 func (t *imports) ShortHelp() string { return "updates import statements" } 32 func (t *imports) DetailedHelp(f *flag.FlagSet) { 33 fmt.Fprintf(f.Output(), ` 34 Example: update imports statements in a file: 35 36 $ gopls imports -w internal/lsp/cmd/check.go 37 38 gopls imports flags are: 39 `) 40 f.PrintDefaults() 41 } 42 43 // Run performs diagnostic checks on the file specified and either; 44 // - if -w is specified, updates the file in place; 45 // - if -d is specified, prints out unified diffs of the changes; or 46 // - otherwise, prints the new versions to stdout. 47 func (t *imports) Run(ctx context.Context, args ...string) error { 48 if len(args) != 1 { 49 return tool.CommandLineErrorf("imports expects 1 argument") 50 } 51 conn, err := t.app.connect(ctx) 52 if err != nil { 53 return err 54 } 55 defer conn.terminate(ctx) 56 57 from := span.Parse(args[0]) 58 uri := from.URI() 59 file := conn.AddFile(ctx, uri) 60 if file.err != nil { 61 return file.err 62 } 63 actions, err := conn.CodeAction(ctx, &protocol.CodeActionParams{ 64 TextDocument: protocol.TextDocumentIdentifier{ 65 URI: protocol.URIFromSpanURI(uri), 66 }, 67 }) 68 if err != nil { 69 return errors.Errorf("%v: %v", from, err) 70 } 71 var edits []protocol.TextEdit 72 for _, a := range actions { 73 if a.Title != "Organize Imports" { 74 continue 75 } 76 for _, c := range a.Edit.DocumentChanges { 77 if fileURI(c.TextDocument.URI) == uri { 78 edits = append(edits, c.Edits...) 79 } 80 } 81 } 82 sedits, err := source.FromProtocolEdits(file.mapper, edits) 83 if err != nil { 84 return errors.Errorf("%v: %v", edits, err) 85 } 86 newContent := diff.ApplyEdits(string(file.mapper.Content), sedits) 87 88 filename := file.uri.Filename() 89 switch { 90 case t.Write: 91 if len(edits) > 0 { 92 ioutil.WriteFile(filename, []byte(newContent), 0644) 93 } 94 case t.Diff: 95 diffs := diff.ToUnified(filename+".orig", filename, string(file.mapper.Content), sedits) 96 fmt.Print(diffs) 97 default: 98 fmt.Print(string(newContent)) 99 } 100 return nil 101 }