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 / general.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 lsp
6
7 import (
8         "bytes"
9         "context"
10         "fmt"
11         "io"
12         "os"
13         "path"
14         "path/filepath"
15         "strings"
16         "sync"
17
18         "golang.org/x/tools/internal/event"
19         "golang.org/x/tools/internal/jsonrpc2"
20         "golang.org/x/tools/internal/lsp/debug"
21         "golang.org/x/tools/internal/lsp/protocol"
22         "golang.org/x/tools/internal/lsp/source"
23         "golang.org/x/tools/internal/span"
24         errors "golang.org/x/xerrors"
25 )
26
27 func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
28         s.stateMu.Lock()
29         if s.state >= serverInitializing {
30                 defer s.stateMu.Unlock()
31                 return nil, errors.Errorf("%w: initialize called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
32         }
33         s.state = serverInitializing
34         s.stateMu.Unlock()
35
36         s.progress.supportsWorkDoneProgress = params.Capabilities.Window.WorkDoneProgress
37
38         options := s.session.Options()
39         defer func() { s.session.SetOptions(options) }()
40
41         if err := s.handleOptionResults(ctx, source.SetOptions(options, params.InitializationOptions)); err != nil {
42                 return nil, err
43         }
44         options.ForClientCapabilities(params.Capabilities)
45
46         folders := params.WorkspaceFolders
47         if len(folders) == 0 {
48                 if params.RootURI != "" {
49                         folders = []protocol.WorkspaceFolder{{
50                                 URI:  string(params.RootURI),
51                                 Name: path.Base(params.RootURI.SpanURI().Filename()),
52                         }}
53                 }
54         }
55         for _, folder := range folders {
56                 uri := span.URIFromURI(folder.URI)
57                 if !uri.IsFile() {
58                         continue
59                 }
60                 s.pendingFolders = append(s.pendingFolders, folder)
61         }
62         // gopls only supports URIs with a file:// scheme, so if we have no
63         // workspace folders with a supported scheme, fail to initialize.
64         if len(folders) > 0 && len(s.pendingFolders) == 0 {
65                 return nil, fmt.Errorf("unsupported URI schemes: %v (gopls only supports file URIs)", folders)
66         }
67
68         var codeActionProvider interface{} = true
69         if ca := params.Capabilities.TextDocument.CodeAction; len(ca.CodeActionLiteralSupport.CodeActionKind.ValueSet) > 0 {
70                 // If the client has specified CodeActionLiteralSupport,
71                 // send the code actions we support.
72                 //
73                 // Using CodeActionOptions is only valid if codeActionLiteralSupport is set.
74                 codeActionProvider = &protocol.CodeActionOptions{
75                         CodeActionKinds: s.getSupportedCodeActions(),
76                 }
77         }
78         var renameOpts interface{} = true
79         if r := params.Capabilities.TextDocument.Rename; r.PrepareSupport {
80                 renameOpts = protocol.RenameOptions{
81                         PrepareProvider: r.PrepareSupport,
82                 }
83         }
84
85         if st := params.Capabilities.TextDocument.SemanticTokens; st != nil {
86                 rememberToks(st.TokenTypes, st.TokenModifiers)
87         }
88
89         goplsVer := &bytes.Buffer{}
90         debug.PrintVersionInfo(ctx, goplsVer, true, debug.PlainText)
91
92         return &protocol.InitializeResult{
93                 Capabilities: protocol.ServerCapabilities{
94                         CallHierarchyProvider: true,
95                         CodeActionProvider:    codeActionProvider,
96                         CompletionProvider: protocol.CompletionOptions{
97                                 TriggerCharacters: []string{"."},
98                         },
99                         DefinitionProvider:         true,
100                         TypeDefinitionProvider:     true,
101                         ImplementationProvider:     true,
102                         DocumentFormattingProvider: true,
103                         DocumentSymbolProvider:     true,
104                         WorkspaceSymbolProvider:    true,
105                         ExecuteCommandProvider: protocol.ExecuteCommandOptions{
106                                 Commands: options.SupportedCommands,
107                         },
108                         FoldingRangeProvider:      true,
109                         HoverProvider:             true,
110                         DocumentHighlightProvider: true,
111                         DocumentLinkProvider:      protocol.DocumentLinkOptions{},
112                         ReferencesProvider:        true,
113                         RenameProvider:            renameOpts,
114                         SignatureHelpProvider: protocol.SignatureHelpOptions{
115                                 TriggerCharacters: []string{"(", ","},
116                         },
117                         TextDocumentSync: &protocol.TextDocumentSyncOptions{
118                                 Change:    protocol.Incremental,
119                                 OpenClose: true,
120                                 Save: protocol.SaveOptions{
121                                         IncludeText: false,
122                                 },
123                         },
124                         Workspace: protocol.WorkspaceGn{
125                                 WorkspaceFolders: protocol.WorkspaceFoldersGn{
126                                         Supported:           true,
127                                         ChangeNotifications: "workspace/didChangeWorkspaceFolders",
128                                 },
129                         },
130                 },
131                 ServerInfo: struct {
132                         Name    string `json:"name"`
133                         Version string `json:"version,omitempty"`
134                 }{
135                         Name:    "gopls",
136                         Version: goplsVer.String(),
137                 },
138         }, nil
139 }
140
141 func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
142         s.stateMu.Lock()
143         if s.state >= serverInitialized {
144                 defer s.stateMu.Unlock()
145                 return errors.Errorf("%w: initalized called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
146         }
147         s.state = serverInitialized
148         s.stateMu.Unlock()
149
150         for _, not := range s.notifications {
151                 s.client.ShowMessage(ctx, not)
152         }
153         s.notifications = nil
154
155         options := s.session.Options()
156         defer func() { s.session.SetOptions(options) }()
157
158         if err := s.addFolders(ctx, s.pendingFolders); err != nil {
159                 return err
160         }
161         s.pendingFolders = nil
162
163         if options.ConfigurationSupported && options.DynamicConfigurationSupported {
164                 registrations := []protocol.Registration{
165                         {
166                                 ID:     "workspace/didChangeConfiguration",
167                                 Method: "workspace/didChangeConfiguration",
168                         },
169                         {
170                                 ID:     "workspace/didChangeWorkspaceFolders",
171                                 Method: "workspace/didChangeWorkspaceFolders",
172                         },
173                 }
174                 if options.SemanticTokens {
175                         registrations = append(registrations, semanticTokenRegistrations()...)
176
177                 }
178                 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
179                         Registrations: registrations,
180                 }); err != nil {
181                         return err
182                 }
183         }
184         return nil
185 }
186
187 func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) error {
188         originalViews := len(s.session.Views())
189         viewErrors := make(map[span.URI]error)
190
191         var wg sync.WaitGroup
192         if s.session.Options().VerboseWorkDoneProgress {
193                 work := s.progress.start(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil, nil)
194                 defer func() {
195                         go func() {
196                                 wg.Wait()
197                                 work.end("Done.")
198                         }()
199                 }()
200         }
201         dirsToWatch := map[span.URI]struct{}{}
202         for _, folder := range folders {
203                 uri := span.URIFromURI(folder.URI)
204                 // Ignore non-file URIs.
205                 if !uri.IsFile() {
206                         continue
207                 }
208                 work := s.progress.start(ctx, "Setting up workspace", "Loading packages...", nil, nil)
209                 snapshot, release, err := s.addView(ctx, folder.Name, uri)
210                 if err != nil {
211                         viewErrors[uri] = err
212                         work.end(fmt.Sprintf("Error loading packages: %s", err))
213                         continue
214                 }
215                 var swg sync.WaitGroup
216                 swg.Add(1)
217                 go func() {
218                         defer swg.Done()
219                         snapshot.AwaitInitialized(ctx)
220                         work.end("Finished loading packages.")
221                 }()
222
223                 for _, dir := range snapshot.WorkspaceDirectories(ctx) {
224                         dirsToWatch[dir] = struct{}{}
225                 }
226
227                 // Print each view's environment.
228                 buf := &bytes.Buffer{}
229                 if err := snapshot.WriteEnv(ctx, buf); err != nil {
230                         viewErrors[uri] = err
231                         continue
232                 }
233                 event.Log(ctx, buf.String())
234
235                 // Diagnose the newly created view.
236                 wg.Add(1)
237                 go func() {
238                         s.diagnoseDetached(snapshot)
239                         swg.Wait()
240                         release()
241                         wg.Done()
242                 }()
243         }
244         // Register for file watching notifications, if they are supported.
245         s.watchedDirectoriesMu.Lock()
246         err := s.registerWatchedDirectoriesLocked(ctx, dirsToWatch)
247         s.watchedDirectoriesMu.Unlock()
248         if err != nil {
249                 return err
250         }
251         if len(viewErrors) > 0 {
252                 errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews)
253                 for uri, err := range viewErrors {
254                         errMsg += fmt.Sprintf("failed to load view for %s: %v\n", uri, err)
255                 }
256                 return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
257                         Type:    protocol.Error,
258                         Message: errMsg,
259                 })
260         }
261         return nil
262 }
263
264 // updateWatchedDirectories compares the current set of directories to watch
265 // with the previously registered set of directories. If the set of directories
266 // has changed, we unregister and re-register for file watching notifications.
267 // updatedSnapshots is the set of snapshots that have been updated.
268 func (s *Server) updateWatchedDirectories(ctx context.Context, updatedSnapshots map[source.View]source.Snapshot) error {
269         dirsToWatch := map[span.URI]struct{}{}
270         seenViews := map[source.View]struct{}{}
271
272         // Collect all of the workspace directories from the updated snapshots.
273         for _, snapshot := range updatedSnapshots {
274                 seenViews[snapshot.View()] = struct{}{}
275                 for _, dir := range snapshot.WorkspaceDirectories(ctx) {
276                         dirsToWatch[dir] = struct{}{}
277                 }
278         }
279         // Not all views were necessarily updated, so check the remaining views.
280         for _, view := range s.session.Views() {
281                 if _, ok := seenViews[view]; ok {
282                         continue
283                 }
284                 snapshot, release := view.Snapshot(ctx)
285                 for _, dir := range snapshot.WorkspaceDirectories(ctx) {
286                         dirsToWatch[dir] = struct{}{}
287                 }
288                 release()
289         }
290
291         s.watchedDirectoriesMu.Lock()
292         defer s.watchedDirectoriesMu.Unlock()
293
294         // Nothing to do if the set of workspace directories is unchanged.
295         if equalURISet(s.watchedDirectories, dirsToWatch) {
296                 return nil
297         }
298
299         // If the set of directories to watch has changed, register the updates and
300         // unregister the previously watched directories. This ordering avoids a
301         // period where no files are being watched. Still, if a user makes on-disk
302         // changes before these updates are complete, we may miss them for the new
303         // directories.
304         if s.watchRegistrationCount > 0 {
305                 prevID := s.watchRegistrationCount - 1
306                 if err := s.registerWatchedDirectoriesLocked(ctx, dirsToWatch); err != nil {
307                         return err
308                 }
309                 return s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{
310                         Unregisterations: []protocol.Unregistration{{
311                                 ID:     watchedFilesCapabilityID(prevID),
312                                 Method: "workspace/didChangeWatchedFiles",
313                         }},
314                 })
315         }
316         return nil
317 }
318
319 func watchedFilesCapabilityID(id uint64) string {
320         return fmt.Sprintf("workspace/didChangeWatchedFiles-%d", id)
321 }
322
323 func equalURISet(m1, m2 map[span.URI]struct{}) bool {
324         if len(m1) != len(m2) {
325                 return false
326         }
327         for k := range m1 {
328                 _, ok := m2[k]
329                 if !ok {
330                         return false
331                 }
332         }
333         return true
334 }
335
336 // registerWatchedDirectoriesLocked sends the workspace/didChangeWatchedFiles
337 // registrations to the client and updates s.watchedDirectories.
338 func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, dirs map[span.URI]struct{}) error {
339         if !s.session.Options().DynamicWatchedFilesSupported {
340                 return nil
341         }
342         for k := range s.watchedDirectories {
343                 delete(s.watchedDirectories, k)
344         }
345         // Work-around microsoft/vscode#100870 by making sure that we are,
346         // at least, watching the user's entire workspace. This will still be
347         // applied to every folder in the workspace.
348         watchers := []protocol.FileSystemWatcher{{
349                 GlobPattern: "**/*.{go,mod,sum}",
350                 Kind:        float64(protocol.WatchChange + protocol.WatchDelete + protocol.WatchCreate),
351         }}
352         for dir := range dirs {
353                 filename := dir.Filename()
354                 // If the directory is within a workspace folder, we're already
355                 // watching it via the relative path above.
356                 for _, view := range s.session.Views() {
357                         if isSubdirectory(view.Folder().Filename(), filename) {
358                                 continue
359                         }
360                 }
361                 // If microsoft/vscode#100870 is resolved before
362                 // microsoft/vscode#104387, we will need a work-around for Windows
363                 // drive letter casing.
364                 watchers = append(watchers, protocol.FileSystemWatcher{
365                         GlobPattern: fmt.Sprintf("%s/**/*.{go,mod,sum}", filename),
366                         Kind:        float64(protocol.WatchChange + protocol.WatchDelete + protocol.WatchCreate),
367                 })
368         }
369         if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
370                 Registrations: []protocol.Registration{{
371                         ID:     watchedFilesCapabilityID(s.watchRegistrationCount),
372                         Method: "workspace/didChangeWatchedFiles",
373                         RegisterOptions: protocol.DidChangeWatchedFilesRegistrationOptions{
374                                 Watchers: watchers,
375                         },
376                 }},
377         }); err != nil {
378                 return err
379         }
380         s.watchRegistrationCount++
381
382         for dir := range dirs {
383                 s.watchedDirectories[dir] = struct{}{}
384         }
385         return nil
386 }
387
388 func isSubdirectory(root, leaf string) bool {
389         rel, err := filepath.Rel(root, leaf)
390         return err == nil && !strings.HasPrefix(rel, "..")
391 }
392
393 func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI, o *source.Options) error {
394         if !s.session.Options().ConfigurationSupported {
395                 return nil
396         }
397         v := protocol.ParamConfiguration{
398                 ConfigurationParams: protocol.ConfigurationParams{
399                         Items: []protocol.ConfigurationItem{{
400                                 ScopeURI: string(folder),
401                                 Section:  "gopls",
402                         }, {
403                                 ScopeURI: string(folder),
404                                 Section:  fmt.Sprintf("gopls-%s", name),
405                         }},
406                 },
407         }
408         configs, err := s.client.Configuration(ctx, &v)
409         if err != nil {
410                 return fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
411         }
412         for _, config := range configs {
413                 if err := s.handleOptionResults(ctx, source.SetOptions(o, config)); err != nil {
414                         return err
415                 }
416         }
417         return nil
418 }
419
420 func (s *Server) eventuallyShowMessage(ctx context.Context, msg *protocol.ShowMessageParams) error {
421         s.stateMu.Lock()
422         defer s.stateMu.Unlock()
423         if s.state == serverInitialized {
424                 return s.client.ShowMessage(ctx, msg)
425         }
426         s.notifications = append(s.notifications, msg)
427         return nil
428 }
429
430 func (s *Server) handleOptionResults(ctx context.Context, results source.OptionResults) error {
431         for _, result := range results {
432                 if result.Error != nil {
433                         msg := &protocol.ShowMessageParams{
434                                 Type:    protocol.Error,
435                                 Message: result.Error.Error(),
436                         }
437                         if err := s.eventuallyShowMessage(ctx, msg); err != nil {
438                                 return err
439                         }
440                 }
441                 switch result.State {
442                 case source.OptionUnexpected:
443                         msg := &protocol.ShowMessageParams{
444                                 Type:    protocol.Error,
445                                 Message: fmt.Sprintf("unexpected gopls setting %q", result.Name),
446                         }
447                         if err := s.eventuallyShowMessage(ctx, msg); err != nil {
448                                 return err
449                         }
450                 case source.OptionDeprecated:
451                         msg := fmt.Sprintf("gopls setting %q is deprecated", result.Name)
452                         if result.Replacement != "" {
453                                 msg = fmt.Sprintf("%s, use %q instead", msg, result.Replacement)
454                         }
455                         if err := s.eventuallyShowMessage(ctx, &protocol.ShowMessageParams{
456                                 Type:    protocol.Warning,
457                                 Message: msg,
458                         }); err != nil {
459                                 return err
460                         }
461                 }
462         }
463         return nil
464 }
465
466 // beginFileRequest checks preconditions for a file-oriented request and routes
467 // it to a snapshot.
468 // We don't want to return errors for benign conditions like wrong file type,
469 // so callers should do if !ok { return err } rather than if err != nil.
470 func (s *Server) beginFileRequest(ctx context.Context, pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.VersionedFileHandle, bool, func(), error) {
471         uri := pURI.SpanURI()
472         if !uri.IsFile() {
473                 // Not a file URI. Stop processing the request, but don't return an error.
474                 return nil, nil, false, func() {}, nil
475         }
476         view, err := s.session.ViewOf(uri)
477         if err != nil {
478                 return nil, nil, false, func() {}, err
479         }
480         snapshot, release := view.Snapshot(ctx)
481         fh, err := snapshot.GetVersionedFile(ctx, uri)
482         if err != nil {
483                 release()
484                 return nil, nil, false, func() {}, err
485         }
486         if expectKind != source.UnknownKind && fh.Kind() != expectKind {
487                 // Wrong kind of file. Nothing to do.
488                 release()
489                 return nil, nil, false, func() {}, nil
490         }
491         return snapshot, fh, true, release, nil
492 }
493
494 func (s *Server) shutdown(ctx context.Context) error {
495         s.stateMu.Lock()
496         defer s.stateMu.Unlock()
497         if s.state < serverInitialized {
498                 event.Log(ctx, "server shutdown without initialization")
499         }
500         if s.state != serverShutDown {
501                 // drop all the active views
502                 s.session.Shutdown(ctx)
503                 s.state = serverShutDown
504         }
505         return nil
506 }
507
508 func (s *Server) exit(ctx context.Context) error {
509         s.stateMu.Lock()
510         defer s.stateMu.Unlock()
511
512         // TODO: We need a better way to find the conn close method.
513         s.client.(io.Closer).Close()
514
515         if s.state != serverShutDown {
516                 // TODO: We should be able to do better than this.
517                 os.Exit(1)
518         }
519         // we don't terminate the process on a normal exit, we just allow it to
520         // close naturally if needed after the connection is closed.
521         return nil
522 }