.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / 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", app.wd, opts)
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         params.Capabilities.TextDocument.Hover = protocol.HoverClientCapabilities{
282                 ContentFormat: []protocol.MarkupKind{opts.PreferredContentFormat},
283         }
284         params.Capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = opts.HierarchicalDocumentSymbolSupport
285         params.Capabilities.TextDocument.SemanticTokens = protocol.SemanticTokensClientCapabilities{}
286         params.Capabilities.TextDocument.SemanticTokens.Formats = []string{"relative"}
287         params.Capabilities.TextDocument.SemanticTokens.Requests.Range = true
288         params.Capabilities.TextDocument.SemanticTokens.Requests.Full = true
289         params.Capabilities.TextDocument.SemanticTokens.TokenTypes = lsp.SemanticTypes()
290         params.Capabilities.TextDocument.SemanticTokens.TokenModifiers = lsp.SemanticModifiers()
291         params.InitializationOptions = map[string]interface{}{
292                 "symbolMatcher": matcherString[opts.SymbolMatcher],
293         }
294         if _, err := c.Server.Initialize(ctx, params); err != nil {
295                 return err
296         }
297         if err := c.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
298                 return err
299         }
300         return nil
301 }
302
303 type connection struct {
304         protocol.Server
305         Client *cmdClient
306 }
307
308 type cmdClient struct {
309         protocol.Server
310         app  *Application
311         fset *token.FileSet
312
313         diagnosticsMu   sync.Mutex
314         diagnosticsDone chan struct{}
315
316         filesMu sync.Mutex
317         files   map[span.URI]*cmdFile
318 }
319
320 type cmdFile struct {
321         uri         span.URI
322         mapper      *protocol.ColumnMapper
323         err         error
324         added       bool
325         diagnostics []protocol.Diagnostic
326 }
327
328 func newConnection(app *Application) *connection {
329         return &connection{
330                 Client: &cmdClient{
331                         app:   app,
332                         fset:  token.NewFileSet(),
333                         files: make(map[span.URI]*cmdFile),
334                 },
335         }
336 }
337
338 // fileURI converts a DocumentURI to a file:// span.URI, panicking if it's not a file.
339 func fileURI(uri protocol.DocumentURI) span.URI {
340         sURI := uri.SpanURI()
341         if !sURI.IsFile() {
342                 panic(fmt.Sprintf("%q is not a file URI", uri))
343         }
344         return sURI
345 }
346
347 func (c *cmdClient) ShowMessage(ctx context.Context, p *protocol.ShowMessageParams) error { return nil }
348
349 func (c *cmdClient) ShowMessageRequest(ctx context.Context, p *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
350         return nil, nil
351 }
352
353 func (c *cmdClient) LogMessage(ctx context.Context, p *protocol.LogMessageParams) error {
354         switch p.Type {
355         case protocol.Error:
356                 log.Print("Error:", p.Message)
357         case protocol.Warning:
358                 log.Print("Warning:", p.Message)
359         case protocol.Info:
360                 if c.app.verbose() {
361                         log.Print("Info:", p.Message)
362                 }
363         case protocol.Log:
364                 if c.app.verbose() {
365                         log.Print("Log:", p.Message)
366                 }
367         default:
368                 if c.app.verbose() {
369                         log.Print(p.Message)
370                 }
371         }
372         return nil
373 }
374
375 func (c *cmdClient) Event(ctx context.Context, t *interface{}) error { return nil }
376
377 func (c *cmdClient) RegisterCapability(ctx context.Context, p *protocol.RegistrationParams) error {
378         return nil
379 }
380
381 func (c *cmdClient) UnregisterCapability(ctx context.Context, p *protocol.UnregistrationParams) error {
382         return nil
383 }
384
385 func (c *cmdClient) WorkspaceFolders(ctx context.Context) ([]protocol.WorkspaceFolder, error) {
386         return nil, nil
387 }
388
389 func (c *cmdClient) Configuration(ctx context.Context, p *protocol.ParamConfiguration) ([]interface{}, error) {
390         results := make([]interface{}, len(p.Items))
391         for i, item := range p.Items {
392                 if item.Section != "gopls" {
393                         continue
394                 }
395                 env := map[string]interface{}{}
396                 for _, value := range c.app.env {
397                         l := strings.SplitN(value, "=", 2)
398                         if len(l) != 2 {
399                                 continue
400                         }
401                         env[l[0]] = l[1]
402                 }
403                 m := map[string]interface{}{
404                         "env": env,
405                         "analyses": map[string]bool{
406                                 "fillreturns":    true,
407                                 "nonewvars":      true,
408                                 "noresultvalues": true,
409                                 "undeclaredname": true,
410                         },
411                 }
412                 if c.app.VeryVerbose {
413                         m["verboseOutput"] = true
414                 }
415                 results[i] = m
416         }
417         return results, nil
418 }
419
420 func (c *cmdClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResponse, error) {
421         return &protocol.ApplyWorkspaceEditResponse{Applied: false, FailureReason: "not implemented"}, nil
422 }
423
424 func (c *cmdClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishDiagnosticsParams) error {
425         if p.URI == "gopls://diagnostics-done" {
426                 close(c.diagnosticsDone)
427         }
428         // Don't worry about diagnostics without versions.
429         if p.Version == 0 {
430                 return nil
431         }
432
433         c.filesMu.Lock()
434         defer c.filesMu.Unlock()
435
436         file := c.getFile(ctx, fileURI(p.URI))
437         file.diagnostics = p.Diagnostics
438         return nil
439 }
440
441 func (c *cmdClient) Progress(context.Context, *protocol.ProgressParams) error {
442         return nil
443 }
444
445 func (c *cmdClient) WorkDoneProgressCreate(context.Context, *protocol.WorkDoneProgressCreateParams) error {
446         return nil
447 }
448
449 func (c *cmdClient) getFile(ctx context.Context, uri span.URI) *cmdFile {
450         file, found := c.files[uri]
451         if !found || file.err != nil {
452                 file = &cmdFile{
453                         uri: uri,
454                 }
455                 c.files[uri] = file
456         }
457         if file.mapper == nil {
458                 fname := uri.Filename()
459                 content, err := ioutil.ReadFile(fname)
460                 if err != nil {
461                         file.err = errors.Errorf("getFile: %v: %v", uri, err)
462                         return file
463                 }
464                 f := c.fset.AddFile(fname, -1, len(content))
465                 f.SetLinesForContent(content)
466                 converter := span.NewContentConverter(fname, content)
467                 file.mapper = &protocol.ColumnMapper{
468                         URI:       uri,
469                         Converter: converter,
470                         Content:   content,
471                 }
472         }
473         return file
474 }
475
476 func (c *connection) AddFile(ctx context.Context, uri span.URI) *cmdFile {
477         c.Client.filesMu.Lock()
478         defer c.Client.filesMu.Unlock()
479
480         file := c.Client.getFile(ctx, uri)
481         // This should never happen.
482         if file == nil {
483                 return &cmdFile{
484                         uri: uri,
485                         err: fmt.Errorf("no file found for %s", uri),
486                 }
487         }
488         if file.err != nil || file.added {
489                 return file
490         }
491         file.added = true
492         p := &protocol.DidOpenTextDocumentParams{
493                 TextDocument: protocol.TextDocumentItem{
494                         URI:        protocol.URIFromSpanURI(uri),
495                         LanguageID: source.DetectLanguage("", file.uri.Filename()).String(),
496                         Version:    1,
497                         Text:       string(file.mapper.Content),
498                 },
499         }
500         if err := c.Server.DidOpen(ctx, p); err != nil {
501                 file.err = errors.Errorf("%v: %v", uri, err)
502         }
503         return file
504 }
505
506 func (c *connection) semanticTokens(ctx context.Context, file span.URI) (*protocol.SemanticTokens, error) {
507         p := &protocol.SemanticTokensParams{
508                 TextDocument: protocol.TextDocumentIdentifier{
509                         URI: protocol.URIFromSpanURI(file),
510                 },
511         }
512         resp, err := c.Server.SemanticTokensFull(ctx, p)
513         if err != nil {
514                 return nil, err
515         }
516         return resp, nil
517 }
518
519 func (c *connection) diagnoseFiles(ctx context.Context, files []span.URI) error {
520         var untypedFiles []interface{}
521         for _, file := range files {
522                 untypedFiles = append(untypedFiles, string(file))
523         }
524         c.Client.diagnosticsMu.Lock()
525         defer c.Client.diagnosticsMu.Unlock()
526
527         c.Client.diagnosticsDone = make(chan struct{})
528         _, err := c.Server.NonstandardRequest(ctx, "gopls/diagnoseFiles", map[string]interface{}{"files": untypedFiles})
529         <-c.Client.diagnosticsDone
530         return err
531 }
532
533 func (c *connection) terminate(ctx context.Context) {
534         if strings.HasPrefix(c.Client.app.Remote, "internal@") {
535                 // internal connections need to be left alive for the next test
536                 return
537         }
538         //TODO: do we need to handle errors on these calls?
539         c.Shutdown(ctx)
540         //TODO: right now calling exit terminates the process, we should rethink that
541         //server.Exit(ctx)
542 }
543
544 // Implement io.Closer.
545 func (c *cmdClient) Close() error {
546         return nil
547 }