Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / cmd / suggested_fix.go
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         "golang.org/x/tools/internal/lsp/diff"
14         "golang.org/x/tools/internal/lsp/protocol"
15         "golang.org/x/tools/internal/lsp/source"
16         "golang.org/x/tools/internal/span"
17         "golang.org/x/tools/internal/tool"
18         errors "golang.org/x/xerrors"
19 )
20
21 // suggestedFix implements the fix verb for gopls.
22 type suggestedFix 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         All   bool `flag:"a" help:"apply all fixes, not just preferred fixes"`
26
27         app *Application
28 }
29
30 func (s *suggestedFix) Name() string      { return "fix" }
31 func (s *suggestedFix) Usage() string     { return "<filename>" }
32 func (s *suggestedFix) ShortHelp() string { return "apply suggested fixes" }
33 func (s *suggestedFix) DetailedHelp(f *flag.FlagSet) {
34         fmt.Fprintf(f.Output(), `
35 Example: apply suggested fixes for this file:
36
37   $ gopls fix -w internal/lsp/cmd/check.go
38
39 gopls fix flags are:
40 `)
41         f.PrintDefaults()
42 }
43
44 // Run performs diagnostic checks on the file specified and either;
45 // - if -w is specified, updates the file in place;
46 // - if -d is specified, prints out unified diffs of the changes; or
47 // - otherwise, prints the new versions to stdout.
48 func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
49         if len(args) < 1 {
50                 return tool.CommandLineErrorf("fix expects at least 1 argument")
51         }
52         conn, err := s.app.connect(ctx)
53         if err != nil {
54                 return err
55         }
56         defer conn.terminate(ctx)
57
58         from := span.Parse(args[0])
59         uri := from.URI()
60         file := conn.AddFile(ctx, uri)
61         if file.err != nil {
62                 return file.err
63         }
64
65         if err := conn.diagnoseFiles(ctx, []span.URI{uri}); err != nil {
66                 return err
67         }
68         conn.Client.filesMu.Lock()
69         defer conn.Client.filesMu.Unlock()
70
71         codeActionKinds := []protocol.CodeActionKind{protocol.QuickFix}
72         if len(args) > 1 {
73                 codeActionKinds = []protocol.CodeActionKind{}
74                 for _, k := range args[1:] {
75                         codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k))
76                 }
77         }
78
79         rng, err := file.mapper.Range(from)
80         if err != nil {
81                 return err
82         }
83         p := protocol.CodeActionParams{
84                 TextDocument: protocol.TextDocumentIdentifier{
85                         URI: protocol.URIFromSpanURI(uri),
86                 },
87                 Context: protocol.CodeActionContext{
88                         Only:        codeActionKinds,
89                         Diagnostics: file.diagnostics,
90                 },
91                 Range: rng,
92         }
93         actions, err := conn.CodeAction(ctx, &p)
94         if err != nil {
95                 return errors.Errorf("%v: %v", from, err)
96         }
97         var edits []protocol.TextEdit
98         for _, a := range actions {
99                 if a.Command != nil {
100                         return fmt.Errorf("ExecuteCommand is not yet supported on the command line")
101                 }
102                 if !a.IsPreferred && !s.All {
103                         continue
104                 }
105                 if !from.HasPosition() {
106                         for _, c := range a.Edit.DocumentChanges {
107                                 if fileURI(c.TextDocument.URI) == uri {
108                                         edits = append(edits, c.Edits...)
109                                 }
110                         }
111                         continue
112                 }
113                 // If the span passed in has a position, then we need to find
114                 // the codeaction that has the same range as the passed in span.
115                 for _, diag := range a.Diagnostics {
116                         spn, err := file.mapper.RangeSpan(diag.Range)
117                         if err != nil {
118                                 continue
119                         }
120                         if span.ComparePoint(from.Start(), spn.Start()) == 0 {
121                                 for _, c := range a.Edit.DocumentChanges {
122                                         if fileURI(c.TextDocument.URI) == uri {
123                                                 edits = append(edits, c.Edits...)
124                                         }
125                                 }
126                                 break
127                         }
128                 }
129
130                 // If suggested fix is not a diagnostic, still must collect edits.
131                 if len(a.Diagnostics) == 0 {
132                         for _, c := range a.Edit.DocumentChanges {
133                                 if fileURI(c.TextDocument.URI) == uri {
134                                         edits = append(edits, c.Edits...)
135                                 }
136                         }
137                 }
138         }
139
140         sedits, err := source.FromProtocolEdits(file.mapper, edits)
141         if err != nil {
142                 return errors.Errorf("%v: %v", edits, err)
143         }
144         newContent := diff.ApplyEdits(string(file.mapper.Content), sedits)
145
146         filename := file.uri.Filename()
147         switch {
148         case s.Write:
149                 if len(edits) > 0 {
150                         ioutil.WriteFile(filename, []byte(newContent), 0644)
151                 }
152         case s.Diff:
153                 diffs := diff.ToUnified(filename+".orig", filename, string(file.mapper.Content), sedits)
154                 fmt.Print(diffs)
155         default:
156                 fmt.Print(string(newContent))
157         }
158         return nil
159 }