.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / 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                 &licenses{app: app},
175         }
176 }
177
178 func (app *Application) featureCommands() []tool.Application {
179         return []tool.Application{
180                 &callHierarchy{app: app},
181                 &check{app: app},
182                 &definition{app: app},
183                 &foldingRanges{app: app},
184                 &format{app: app},
185                 &highlight{app: app},
186                 &implementation{app: app},
187                 &imports{app: app},
188                 &inspect{app: app},
189                 &links{app: app},
190                 &prepareRename{app: app},
191                 &references{app: app},
192                 &rename{app: app},
193                 &semtok{app: app},
194                 &signature{app: app},
195                 &suggestedFix{app: app},
196                 &symbols{app: app},
197                 &workspace{app: app},
198                 &workspaceSymbol{app: app},
199         }
200 }
201
202 var (
203         internalMu          sync.Mutex
204         internalConnections = make(map[string]*connection)
205 )
206
207 func (app *Application) connect(ctx context.Context) (*connection, error) {
208         switch {
209         case app.Remote == "":
210                 connection := newConnection(app)
211                 connection.Server = lsp.NewServer(cache.New(ctx, app.options).NewSession(ctx), connection.Client)
212                 ctx = protocol.WithClient(ctx, connection.Client)
213                 return connection, connection.initialize(ctx, app.options)
214         case strings.HasPrefix(app.Remote, "internal@"):
215                 internalMu.Lock()
216                 defer internalMu.Unlock()
217                 opts := source.DefaultOptions().Clone()
218                 if app.options != nil {
219                         app.options(opts)
220                 }
221                 key := fmt.Sprintf("%s %v %v %v", app.wd, opts.PreferredContentFormat, opts.HierarchicalDocumentSymbolSupport, opts.SymbolMatcher)
222                 if c := internalConnections[key]; c != nil {
223                         return c, nil
224                 }
225                 remote := app.Remote[len("internal@"):]
226                 ctx := xcontext.Detach(ctx) //TODO:a way of shutting down the internal server
227                 connection, err := app.connectRemote(ctx, remote)
228                 if err != nil {
229                         return nil, err
230                 }
231                 internalConnections[key] = connection
232                 return connection, nil
233         default:
234                 return app.connectRemote(ctx, app.Remote)
235         }
236 }
237
238 // CloseTestConnections terminates shared connections used in command tests. It
239 // should only be called from tests.
240 func CloseTestConnections(ctx context.Context) {
241         for _, c := range internalConnections {
242                 c.Shutdown(ctx)
243                 c.Exit(ctx)
244         }
245 }
246
247 func (app *Application) connectRemote(ctx context.Context, remote string) (*connection, error) {
248         connection := newConnection(app)
249         network, addr := parseAddr(remote)
250         conn, err := lsprpc.ConnectToRemote(ctx, network, addr)
251         if err != nil {
252                 return nil, err
253         }
254         stream := jsonrpc2.NewHeaderStream(conn)
255         cc := jsonrpc2.NewConn(stream)
256         connection.Server = protocol.ServerDispatcher(cc)
257         ctx = protocol.WithClient(ctx, connection.Client)
258         cc.Go(ctx,
259                 protocol.Handlers(
260                         protocol.ClientHandler(connection.Client,
261                                 jsonrpc2.MethodNotFound)))
262         return connection, connection.initialize(ctx, app.options)
263 }
264
265 var matcherString = map[source.SymbolMatcher]string{
266         source.SymbolFuzzy:           "fuzzy",
267         source.SymbolCaseSensitive:   "caseSensitive",
268         source.SymbolCaseInsensitive: "caseInsensitive",
269 }
270
271 func (c *connection) initialize(ctx context.Context, options func(*source.Options)) error {
272         params := &protocol.ParamInitialize{}
273         params.RootURI = protocol.URIFromPath(c.Client.app.wd)
274         params.Capabilities.Workspace.Configuration = true
275
276         // Make sure to respect configured options when sending initialize request.
277         opts := source.DefaultOptions().Clone()
278         if options != nil {
279                 options(opts)
280         }
281         // If you add an additional option here, you must update the map key in connect.
282         params.Capabilities.TextDocument.Hover = protocol.HoverClientCapabilities{
283                 ContentFormat: []protocol.MarkupKind{opts.PreferredContentFormat},
284         }
285         params.Capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = opts.HierarchicalDocumentSymbolSupport
286         params.Capabilities.TextDocument.SemanticTokens = protocol.SemanticTokensClientCapabilities{}
287         params.Capabilities.TextDocument.SemanticTokens.Formats = []string{"relative"}
288         params.Capabilities.TextDocument.SemanticTokens.Requests.Range = true
289         params.Capabilities.TextDocument.SemanticTokens.Requests.Full = true
290         params.Capabilities.TextDocument.SemanticTokens.TokenTypes = lsp.SemanticTypes()
291         params.Capabilities.TextDocument.SemanticTokens.TokenModifiers = lsp.SemanticModifiers()
292         params.InitializationOptions = map[string]interface{}{
293                 "symbolMatcher": matcherString[opts.SymbolMatcher],
294         }
295         if _, err := c.Server.Initialize(ctx, params); err != nil {
296                 return err
297         }
298         if err := c.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
299                 return err
300         }
301         return nil
302 }
303
304 type connection struct {
305         protocol.Server
306         Client *cmdClient
307 }
308
309 type cmdClient struct {
310         protocol.Server
311         app  *Application
312         fset *token.FileSet
313
314         diagnosticsMu   sync.Mutex
315         diagnosticsDone chan struct{}
316
317         filesMu sync.Mutex
318         files   map[span.URI]*cmdFile
319 }
320
321 type cmdFile struct {
322         uri         span.URI
323         mapper      *protocol.ColumnMapper
324         err         error
325         added       bool
326         diagnostics []protocol.Diagnostic
327 }
328
329 func newConnection(app *Application) *connection {
330         return &connection{
331                 Client: &cmdClient{
332                         app:   app,
333                         fset:  token.NewFileSet(),
334                         files: make(map[span.URI]*cmdFile),
335                 },
336         }
337 }
338
339 // fileURI converts a DocumentURI to a file:// span.URI, panicking if it's not a file.
340 func fileURI(uri protocol.DocumentURI) span.URI {
341         sURI := uri.SpanURI()
342         if !sURI.IsFile() {
343                 panic(fmt.Sprintf("%q is not a file URI", uri))
344         }
345         return sURI
346 }
347
348 func (c *cmdClient) ShowMessage(ctx context.Context, p *protocol.ShowMessageParams) error { return nil }
349
350 func (c *cmdClient) ShowMessageRequest(ctx context.Context, p *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
351         return nil, nil
352 }
353
354 func (c *cmdClient) LogMessage(ctx context.Context, p *protocol.LogMessageParams) error {
355         switch p.Type {
356         case protocol.Error:
357                 log.Print("Error:", p.Message)
358         case protocol.Warning:
359                 log.Print("Warning:", p.Message)
360         case protocol.Info:
361                 if c.app.verbose() {
362                         log.Print("Info:", p.Message)
363                 }
364         case protocol.Log:
365                 if c.app.verbose() {
366                         log.Print("Log:", p.Message)
367                 }
368         default:
369                 if c.app.verbose() {
370                         log.Print(p.Message)
371                 }
372         }
373         return nil
374 }
375
376 func (c *cmdClient) Event(ctx context.Context, t *interface{}) error { return nil }
377
378 func (c *cmdClient) RegisterCapability(ctx context.Context, p *protocol.RegistrationParams) error {
379         return nil
380 }
381
382 func (c *cmdClient) UnregisterCapability(ctx context.Context, p *protocol.UnregistrationParams) error {
383         return nil
384 }
385
386 func (c *cmdClient) WorkspaceFolders(ctx context.Context) ([]protocol.WorkspaceFolder, error) {
387         return nil, nil
388 }
389
390 func (c *cmdClient) Configuration(ctx context.Context, p *protocol.ParamConfiguration) ([]interface{}, error) {
391         results := make([]interface{}, len(p.Items))
392         for i, item := range p.Items {
393                 if item.Section != "gopls" {
394                         continue
395                 }
396                 env := map[string]interface{}{}
397                 for _, value := range c.app.env {
398                         l := strings.SplitN(value, "=", 2)
399                         if len(l) != 2 {
400                                 continue
401                         }
402                         env[l[0]] = l[1]
403                 }
404                 m := map[string]interface{}{
405                         "env": env,
406                         "analyses": map[string]bool{
407                                 "fillreturns":    true,
408                                 "nonewvars":      true,
409                                 "noresultvalues": true,
410                                 "undeclaredname": true,
411                         },
412                 }
413                 if c.app.VeryVerbose {
414                         m["verboseOutput"] = true
415                 }
416                 results[i] = m
417         }
418         return results, nil
419 }
420
421 func (c *cmdClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResponse, error) {
422         return &protocol.ApplyWorkspaceEditResponse{Applied: false, FailureReason: "not implemented"}, nil
423 }
424
425 func (c *cmdClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishDiagnosticsParams) error {
426         if p.URI == "gopls://diagnostics-done" {
427                 close(c.diagnosticsDone)
428         }
429         // Don't worry about diagnostics without versions.
430         if p.Version == 0 {
431                 return nil
432         }
433
434         c.filesMu.Lock()
435         defer c.filesMu.Unlock()
436
437         file := c.getFile(ctx, fileURI(p.URI))
438         file.diagnostics = p.Diagnostics
439         return nil
440 }
441
442 func (c *cmdClient) Progress(context.Context, *protocol.ProgressParams) error {
443         return nil
444 }
445
446 func (c *cmdClient) WorkDoneProgressCreate(context.Context, *protocol.WorkDoneProgressCreateParams) error {
447         return nil
448 }
449
450 func (c *cmdClient) getFile(ctx context.Context, uri span.URI) *cmdFile {
451         file, found := c.files[uri]
452         if !found || file.err != nil {
453                 file = &cmdFile{
454                         uri: uri,
455                 }
456                 c.files[uri] = file
457         }
458         if file.mapper == nil {
459                 fname := uri.Filename()
460                 content, err := ioutil.ReadFile(fname)
461                 if err != nil {
462                         file.err = errors.Errorf("getFile: %v: %v", uri, err)
463                         return file
464                 }
465                 f := c.fset.AddFile(fname, -1, len(content))
466                 f.SetLinesForContent(content)
467                 converter := span.NewContentConverter(fname, content)
468                 file.mapper = &protocol.ColumnMapper{
469                         URI:       uri,
470                         Converter: converter,
471                         Content:   content,
472                 }
473         }
474         return file
475 }
476
477 func (c *connection) AddFile(ctx context.Context, uri span.URI) *cmdFile {
478         c.Client.filesMu.Lock()
479         defer c.Client.filesMu.Unlock()
480
481         file := c.Client.getFile(ctx, uri)
482         // This should never happen.
483         if file == nil {
484                 return &cmdFile{
485                         uri: uri,
486                         err: fmt.Errorf("no file found for %s", uri),
487                 }
488         }
489         if file.err != nil || file.added {
490                 return file
491         }
492         file.added = true
493         p := &protocol.DidOpenTextDocumentParams{
494                 TextDocument: protocol.TextDocumentItem{
495                         URI:        protocol.URIFromSpanURI(uri),
496                         LanguageID: source.DetectLanguage("", file.uri.Filename()).String(),
497                         Version:    1,
498                         Text:       string(file.mapper.Content),
499                 },
500         }
501         if err := c.Server.DidOpen(ctx, p); err != nil {
502                 file.err = errors.Errorf("%v: %v", uri, err)
503         }
504         return file
505 }
506
507 func (c *connection) semanticTokens(ctx context.Context, file span.URI) (*protocol.SemanticTokens, error) {
508         p := &protocol.SemanticTokensParams{
509                 TextDocument: protocol.TextDocumentIdentifier{
510                         URI: protocol.URIFromSpanURI(file),
511                 },
512         }
513         resp, err := c.Server.SemanticTokensFull(ctx, p)
514         if err != nil {
515                 return nil, err
516         }
517         return resp, nil
518 }
519
520 func (c *connection) diagnoseFiles(ctx context.Context, files []span.URI) error {
521         var untypedFiles []interface{}
522         for _, file := range files {
523                 untypedFiles = append(untypedFiles, string(file))
524         }
525         c.Client.diagnosticsMu.Lock()
526         defer c.Client.diagnosticsMu.Unlock()
527
528         c.Client.diagnosticsDone = make(chan struct{})
529         _, err := c.Server.NonstandardRequest(ctx, "gopls/diagnoseFiles", map[string]interface{}{"files": untypedFiles})
530         <-c.Client.diagnosticsDone
531         return err
532 }
533
534 func (c *connection) terminate(ctx context.Context) {
535         if strings.HasPrefix(c.Client.app.Remote, "internal@") {
536                 // internal connections need to be left alive for the next test
537                 return
538         }
539         //TODO: do we need to handle errors on these calls?
540         c.Shutdown(ctx)
541         //TODO: right now calling exit terminates the process, we should rethink that
542         //server.Exit(ctx)
543 }
544
545 // Implement io.Closer.
546 func (c *cmdClient) Close() error {
547         return nil
548 }