Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / cmd / cmd.go
1 // Copyright 2018 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 handles the gopls command line.
6 // It contains a handler for each of the modes, along with all the flag handling
7 // and the command line output format.
8 package cmd
9
10 import (
11         "context"
12         "flag"
13         "fmt"
14         "go/token"
15         "io/ioutil"
16         "log"
17         "os"
18         "strings"
19         "sync"
20         "time"
21
22         "golang.org/x/tools/internal/jsonrpc2"
23         "golang.org/x/tools/internal/lsp"
24         "golang.org/x/tools/internal/lsp/cache"
25         "golang.org/x/tools/internal/lsp/debug"
26         "golang.org/x/tools/internal/lsp/lsprpc"
27         "golang.org/x/tools/internal/lsp/protocol"
28         "golang.org/x/tools/internal/lsp/source"
29         "golang.org/x/tools/internal/span"
30         "golang.org/x/tools/internal/tool"
31         "golang.org/x/tools/internal/xcontext"
32         errors "golang.org/x/xerrors"
33 )
34
35 // Application is the main application as passed to tool.Main
36 // It handles the main command line parsing and dispatch to the sub commands.
37 type Application struct {
38         // Core application flags
39
40         // Embed the basic profiling flags supported by the tool package
41         tool.Profile
42
43         // We include the server configuration directly for now, so the flags work
44         // even without the verb.
45         // TODO: Remove this when we stop allowing the serve verb by default.
46         Serve Serve
47
48         // the options configuring function to invoke when building a server
49         options func(*source.Options)
50
51         // The name of the binary, used in help and telemetry.
52         name string
53
54         // The working directory to run commands in.
55         wd string
56
57         // The environment variables to use.
58         env []string
59
60         // Support for remote LSP server.
61         Remote string `flag:"remote" help:"forward all commands to a remote lsp specified by this flag. With no special prefix, this is assumed to be a TCP address. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. If 'auto', or prefixed by 'auto;', the remote address is automatically resolved based on the executing environment."`
62
63         // Verbose enables verbose logging.
64         Verbose bool `flag:"v" help:"verbose output"`
65
66         // VeryVerbose enables a higher level of verbosity in logging output.
67         VeryVerbose bool `flag:"vv" help:"very verbose output"`
68
69         // Control ocagent export of telemetry
70         OCAgent string `flag:"ocagent" help:"the address of the ocagent (e.g. http://localhost:55678), or off"`
71
72         // PrepareOptions is called to update the options when a new view is built.
73         // It is primarily to allow the behavior of gopls to be modified by hooks.
74         PrepareOptions func(*source.Options)
75 }
76
77 func (app *Application) verbose() bool {
78         return app.Verbose || app.VeryVerbose
79 }
80
81 // New returns a new Application ready to run.
82 func New(name, wd string, env []string, options func(*source.Options)) *Application {
83         if wd == "" {
84                 wd, _ = os.Getwd()
85         }
86         app := &Application{
87                 options: options,
88                 name:    name,
89                 wd:      wd,
90                 env:     env,
91                 OCAgent: "off", //TODO: Remove this line to default the exporter to on
92
93                 Serve: Serve{
94                         RemoteListenTimeout: 1 * time.Minute,
95                 },
96         }
97         return app
98 }
99
100 // Name implements tool.Application returning the binary name.
101 func (app *Application) Name() string { return app.name }
102
103 // Usage implements tool.Application returning empty extra argument usage.
104 func (app *Application) Usage() string { return "<command> [command-flags] [command-args]" }
105
106 // ShortHelp implements tool.Application returning the main binary help.
107 func (app *Application) ShortHelp() string {
108         return "The Go Language source tools."
109 }
110
111 // DetailedHelp implements tool.Application returning the main binary help.
112 // This includes the short help for all the sub commands.
113 func (app *Application) DetailedHelp(f *flag.FlagSet) {
114         fmt.Fprint(f.Output(), `
115 gopls is a Go language server. It is typically used with an editor to provide
116 language features. When no command is specified, gopls will default to the 'serve'
117 command. The language features can also be accessed via the gopls command-line interface.
118
119 Available commands are:
120 `)
121         fmt.Fprint(f.Output(), `
122 main:
123 `)
124         for _, c := range app.mainCommands() {
125                 fmt.Fprintf(f.Output(), "  %s : %v\n", c.Name(), c.ShortHelp())
126         }
127         fmt.Fprint(f.Output(), `
128 features:
129 `)
130         for _, c := range app.featureCommands() {
131                 fmt.Fprintf(f.Output(), "  %s : %v\n", c.Name(), c.ShortHelp())
132         }
133         fmt.Fprint(f.Output(), `
134 gopls flags are:
135 `)
136         f.PrintDefaults()
137 }
138
139 // Run takes the args after top level flag processing, and invokes the correct
140 // sub command as specified by the first argument.
141 // If no arguments are passed it will invoke the server sub command, as a
142 // temporary measure for compatibility.
143 func (app *Application) Run(ctx context.Context, args ...string) error {
144         ctx = debug.WithInstance(ctx, app.wd, app.OCAgent)
145         app.Serve.app = app
146         if len(args) == 0 {
147                 return tool.Run(ctx, &app.Serve, args)
148         }
149         command, args := args[0], args[1:]
150         for _, c := range app.commands() {
151                 if c.Name() == command {
152                         return tool.Run(ctx, c, args)
153                 }
154         }
155         return tool.CommandLineErrorf("Unknown command %v", command)
156 }
157
158 // commands returns the set of commands supported by the gopls tool on the
159 // command line.
160 // The command is specified by the first non flag argument.
161 func (app *Application) commands() []tool.Application {
162         var commands []tool.Application
163         commands = append(commands, app.mainCommands()...)
164         commands = append(commands, app.featureCommands()...)
165         return commands
166 }
167
168 func (app *Application) mainCommands() []tool.Application {
169         return []tool.Application{
170                 &app.Serve,
171                 &version{app: app},
172                 &bug{},
173                 &apiJSON{},
174         }
175 }
176
177 func (app *Application) featureCommands() []tool.Application {
178         return []tool.Application{
179                 &callHierarchy{app: app},
180                 &check{app: app},
181                 &definition{app: app},
182                 &foldingRanges{app: app},
183                 &format{app: app},
184                 &highlight{app: app},
185                 &implementation{app: app},
186                 &imports{app: app},
187                 &inspect{app: app},
188                 &links{app: app},
189                 &prepareRename{app: app},
190                 &references{app: app},
191                 &rename{app: app},
192                 &semtok{app: app},
193                 &signature{app: app},
194                 &suggestedFix{app: app},
195                 &symbols{app: app},
196                 &workspace{app: app},
197                 &workspaceSymbol{app: app},
198         }
199 }
200
201 var (
202         internalMu          sync.Mutex
203         internalConnections = make(map[string]*connection)
204 )
205
206 func (app *Application) connect(ctx context.Context) (*connection, error) {
207         switch {
208         case app.Remote == "":
209                 connection := newConnection(app)
210                 connection.Server = lsp.NewServer(cache.New(ctx, app.options).NewSession(ctx), connection.Client)
211                 ctx = protocol.WithClient(ctx, connection.Client)
212                 return connection, connection.initialize(ctx, app.options)
213         case strings.HasPrefix(app.Remote, "internal@"):
214                 internalMu.Lock()
215                 defer internalMu.Unlock()
216                 opts := source.DefaultOptions().Clone()
217                 if app.options != nil {
218                         app.options(opts)
219                 }
220                 key := fmt.Sprintf("%s %v", app.wd, opts)
221                 if c := internalConnections[key]; c != nil {
222                         return c, nil
223                 }
224                 remote := app.Remote[len("internal@"):]
225                 ctx := xcontext.Detach(ctx) //TODO:a way of shutting down the internal server
226                 connection, err := app.connectRemote(ctx, remote)
227                 if err != nil {
228                         return nil, err
229                 }
230                 internalConnections[key] = connection
231                 return connection, nil
232         default:
233                 return app.connectRemote(ctx, app.Remote)
234         }
235 }
236
237 // CloseTestConnections terminates shared connections used in command tests. It
238 // should only be called from tests.
239 func CloseTestConnections(ctx context.Context) {
240         for _, c := range internalConnections {
241                 c.Shutdown(ctx)
242                 c.Exit(ctx)
243         }
244 }
245
246 func (app *Application) connectRemote(ctx context.Context, remote string) (*connection, error) {
247         connection := newConnection(app)
248         network, addr := parseAddr(remote)
249         conn, err := lsprpc.ConnectToRemote(ctx, network, addr)
250         if err != nil {
251                 return nil, err
252         }
253         stream := jsonrpc2.NewHeaderStream(conn)
254         cc := jsonrpc2.NewConn(stream)
255         connection.Server = protocol.ServerDispatcher(cc)
256         ctx = protocol.WithClient(ctx, connection.Client)
257         cc.Go(ctx,
258                 protocol.Handlers(
259                         protocol.ClientHandler(connection.Client,
260                                 jsonrpc2.MethodNotFound)))
261         return connection, connection.initialize(ctx, app.options)
262 }
263
264 var matcherString = map[source.SymbolMatcher]string{
265         source.SymbolFuzzy:           "fuzzy",
266         source.SymbolCaseSensitive:   "caseSensitive",
267         source.SymbolCaseInsensitive: "caseInsensitive",
268 }
269
270 func (c *connection) initialize(ctx context.Context, options func(*source.Options)) error {
271         params := &protocol.ParamInitialize{}
272         params.RootURI = protocol.URIFromPath(c.Client.app.wd)
273         params.Capabilities.Workspace.Configuration = true
274
275         // Make sure to respect configured options when sending initialize request.
276         opts := source.DefaultOptions().Clone()
277         if options != nil {
278                 options(opts)
279         }
280         params.Capabilities.TextDocument.Hover = protocol.HoverClientCapabilities{
281                 ContentFormat: []protocol.MarkupKind{opts.PreferredContentFormat},
282         }
283         params.Capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = opts.HierarchicalDocumentSymbolSupport
284         params.Capabilities.TextDocument.SemanticTokens = &protocol.SemanticTokensClientCapabilities{}
285         params.Capabilities.TextDocument.SemanticTokens.Formats = []string{"relative"}
286         params.Capabilities.TextDocument.SemanticTokens.Requests.Range = true
287         params.Capabilities.TextDocument.SemanticTokens.Requests.Full.Delta = true
288         params.Capabilities.TextDocument.SemanticTokens.TokenTypes = lsp.SemanticTypes()
289         params.Capabilities.TextDocument.SemanticTokens.TokenModifiers = lsp.SemanticModifiers()
290         params.InitializationOptions = map[string]interface{}{
291                 "symbolMatcher": matcherString[opts.SymbolMatcher],
292         }
293         if _, err := c.Server.Initialize(ctx, params); err != nil {
294                 return err
295         }
296         if err := c.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
297                 return err
298         }
299         return nil
300 }
301
302 type connection struct {
303         protocol.Server
304         Client *cmdClient
305 }
306
307 type cmdClient struct {
308         protocol.Server
309         app  *Application
310         fset *token.FileSet
311
312         diagnosticsMu   sync.Mutex
313         diagnosticsDone chan struct{}
314
315         filesMu sync.Mutex
316         files   map[span.URI]*cmdFile
317 }
318
319 type cmdFile struct {
320         uri         span.URI
321         mapper      *protocol.ColumnMapper
322         err         error
323         added       bool
324         diagnostics []protocol.Diagnostic
325 }
326
327 func newConnection(app *Application) *connection {
328         return &connection{
329                 Client: &cmdClient{
330                         app:   app,
331                         fset:  token.NewFileSet(),
332                         files: make(map[span.URI]*cmdFile),
333                 },
334         }
335 }
336
337 // fileURI converts a DocumentURI to a file:// span.URI, panicking if it's not a file.
338 func fileURI(uri protocol.DocumentURI) span.URI {
339         sURI := uri.SpanURI()
340         if !sURI.IsFile() {
341                 panic(fmt.Sprintf("%q is not a file URI", uri))
342         }
343         return sURI
344 }
345
346 func (c *cmdClient) ShowMessage(ctx context.Context, p *protocol.ShowMessageParams) error { return nil }
347
348 func (c *cmdClient) ShowMessageRequest(ctx context.Context, p *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
349         return nil, nil
350 }
351
352 func (c *cmdClient) LogMessage(ctx context.Context, p *protocol.LogMessageParams) error {
353         switch p.Type {
354         case protocol.Error:
355                 log.Print("Error:", p.Message)
356         case protocol.Warning:
357                 log.Print("Warning:", p.Message)
358         case protocol.Info:
359                 if c.app.verbose() {
360                         log.Print("Info:", p.Message)
361                 }
362         case protocol.Log:
363                 if c.app.verbose() {
364                         log.Print("Log:", p.Message)
365                 }
366         default:
367                 if c.app.verbose() {
368                         log.Print(p.Message)
369                 }
370         }
371         return nil
372 }
373
374 func (c *cmdClient) Event(ctx context.Context, t *interface{}) error { return nil }
375
376 func (c *cmdClient) RegisterCapability(ctx context.Context, p *protocol.RegistrationParams) error {
377         return nil
378 }
379
380 func (c *cmdClient) UnregisterCapability(ctx context.Context, p *protocol.UnregistrationParams) error {
381         return nil
382 }
383
384 func (c *cmdClient) WorkspaceFolders(ctx context.Context) ([]protocol.WorkspaceFolder, error) {
385         return nil, nil
386 }
387
388 func (c *cmdClient) Configuration(ctx context.Context, p *protocol.ParamConfiguration) ([]interface{}, error) {
389         results := make([]interface{}, len(p.Items))
390         for i, item := range p.Items {
391                 if item.Section != "gopls" {
392                         continue
393                 }
394                 env := map[string]interface{}{}
395                 for _, value := range c.app.env {
396                         l := strings.SplitN(value, "=", 2)
397                         if len(l) != 2 {
398                                 continue
399                         }
400                         env[l[0]] = l[1]
401                 }
402                 m := map[string]interface{}{
403                         "env": env,
404                         "analyses": map[string]bool{
405                                 "fillreturns":    true,
406                                 "nonewvars":      true,
407                                 "noresultvalues": true,
408                                 "undeclaredname": true,
409                         },
410                 }
411                 if c.app.VeryVerbose {
412                         m["verboseOutput"] = true
413                 }
414                 results[i] = m
415         }
416         return results, nil
417 }
418
419 func (c *cmdClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResponse, error) {
420         return &protocol.ApplyWorkspaceEditResponse{Applied: false, FailureReason: "not implemented"}, nil
421 }
422
423 func (c *cmdClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishDiagnosticsParams) error {
424         if p.URI == "gopls://diagnostics-done" {
425                 close(c.diagnosticsDone)
426         }
427         // Don't worry about diagnostics without versions.
428         if p.Version == 0 {
429                 return nil
430         }
431
432         c.filesMu.Lock()
433         defer c.filesMu.Unlock()
434
435         file := c.getFile(ctx, fileURI(p.URI))
436         file.diagnostics = p.Diagnostics
437         return nil
438 }
439
440 func (c *cmdClient) Progress(context.Context, *protocol.ProgressParams) error {
441         return nil
442 }
443
444 func (c *cmdClient) WorkDoneProgressCreate(context.Context, *protocol.WorkDoneProgressCreateParams) error {
445         return nil
446 }
447
448 func (c *cmdClient) getFile(ctx context.Context, uri span.URI) *cmdFile {
449         file, found := c.files[uri]
450         if !found || file.err != nil {
451                 file = &cmdFile{
452                         uri: uri,
453                 }
454                 c.files[uri] = file
455         }
456         if file.mapper == nil {
457                 fname := uri.Filename()
458                 content, err := ioutil.ReadFile(fname)
459                 if err != nil {
460                         file.err = errors.Errorf("getFile: %v: %v", uri, err)
461                         return file
462                 }
463                 f := c.fset.AddFile(fname, -1, len(content))
464                 f.SetLinesForContent(content)
465                 converter := span.NewContentConverter(fname, content)
466                 file.mapper = &protocol.ColumnMapper{
467                         URI:       uri,
468                         Converter: converter,
469                         Content:   content,
470                 }
471         }
472         return file
473 }
474
475 func (c *connection) AddFile(ctx context.Context, uri span.URI) *cmdFile {
476         c.Client.filesMu.Lock()
477         defer c.Client.filesMu.Unlock()
478
479         file := c.Client.getFile(ctx, uri)
480         // This should never happen.
481         if file == nil {
482                 return &cmdFile{
483                         uri: uri,
484                         err: fmt.Errorf("no file found for %s", uri),
485                 }
486         }
487         if file.err != nil || file.added {
488                 return file
489         }
490         file.added = true
491         p := &protocol.DidOpenTextDocumentParams{
492                 TextDocument: protocol.TextDocumentItem{
493                         URI:        protocol.URIFromSpanURI(uri),
494                         LanguageID: source.DetectLanguage("", file.uri.Filename()).String(),
495                         Version:    1,
496                         Text:       string(file.mapper.Content),
497                 },
498         }
499         if err := c.Server.DidOpen(ctx, p); err != nil {
500                 file.err = errors.Errorf("%v: %v", uri, err)
501         }
502         return file
503 }
504
505 func (c *connection) semanticTokens(ctx context.Context, file span.URI) (*protocol.SemanticTokens, error) {
506         p := &protocol.SemanticTokensParams{
507                 TextDocument: protocol.TextDocumentIdentifier{
508                         URI: protocol.URIFromSpanURI(file),
509                 },
510         }
511         resp, err := c.Server.SemanticTokensFull(ctx, p)
512         if err != nil {
513                 return nil, err
514         }
515         return resp, nil
516 }
517
518 func (c *connection) diagnoseFiles(ctx context.Context, files []span.URI) error {
519         var untypedFiles []interface{}
520         for _, file := range files {
521                 untypedFiles = append(untypedFiles, string(file))
522         }
523         c.Client.diagnosticsMu.Lock()
524         defer c.Client.diagnosticsMu.Unlock()
525
526         c.Client.diagnosticsDone = make(chan struct{})
527         _, err := c.Server.NonstandardRequest(ctx, "gopls/diagnoseFiles", map[string]interface{}{"files": untypedFiles})
528         <-c.Client.diagnosticsDone
529         return err
530 }
531
532 func (c *connection) terminate(ctx context.Context) {
533         if strings.HasPrefix(c.Client.app.Remote, "internal@") {
534                 // internal connections need to be left alive for the next test
535                 return
536         }
537         //TODO: do we need to handle errors on these calls?
538         c.Shutdown(ctx)
539         //TODO: right now calling exit terminates the process, we should rethink that
540         //server.Exit(ctx)
541 }
542
543 // Implement io.Closer.
544 func (c *cmdClient) Close() error {
545         return nil
546 }