blob: 87a86f481b71940fe97ea1dc3f766d28a1703bec [file] [log] [blame]
Robert Findleyb15dac22022-08-30 14:40:12 -04001// 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
5package lsp
6
7import (
Robert Findleyb15dac22022-08-30 14:40:12 -04008 "context"
9 "encoding/json"
10 "fmt"
11 "log"
12 "os"
13 "path"
14 "path/filepath"
Robert Findley89b43352022-10-07 13:43:27 -040015 "sort"
16 "strings"
Robert Findleyb15dac22022-08-30 14:40:12 -040017 "sync"
18
Robert Findleyb15dac22022-08-30 14:40:12 -040019 "golang.org/x/tools/gopls/internal/lsp/debug"
20 "golang.org/x/tools/gopls/internal/lsp/protocol"
21 "golang.org/x/tools/gopls/internal/lsp/source"
Alan Donovan26a95e62022-10-07 10:40:32 -040022 "golang.org/x/tools/gopls/internal/span"
Peter Weinberger9250e222022-08-16 11:12:59 -040023 "golang.org/x/tools/internal/bug"
24 "golang.org/x/tools/internal/event"
25 "golang.org/x/tools/internal/jsonrpc2"
Robert Findleyb15dac22022-08-30 14:40:12 -040026)
27
28func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
29 s.stateMu.Lock()
30 if s.state >= serverInitializing {
31 defer s.stateMu.Unlock()
32 return nil, fmt.Errorf("%w: initialize called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
33 }
34 s.state = serverInitializing
35 s.stateMu.Unlock()
36
37 // For uniqueness, use the gopls PID rather than params.ProcessID (the client
38 // pid). Some clients might start multiple gopls servers, though they
39 // probably shouldn't.
40 pid := os.Getpid()
41 s.tempDir = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.%s", pid, s.session.ID()))
42 err := os.Mkdir(s.tempDir, 0700)
43 if err != nil {
44 // MkdirTemp could fail due to permissions issues. This is a problem with
45 // the user's environment, but should not block gopls otherwise behaving.
46 // All usage of s.tempDir should be predicated on having a non-empty
47 // s.tempDir.
48 event.Error(ctx, "creating temp dir", err)
49 s.tempDir = ""
50 }
51 s.progress.SetSupportsWorkDoneProgress(params.Capabilities.Window.WorkDoneProgress)
52
53 options := s.session.Options()
54 defer func() { s.session.SetOptions(options) }()
55
56 if err := s.handleOptionResults(ctx, source.SetOptions(options, params.InitializationOptions)); err != nil {
57 return nil, err
58 }
59 options.ForClientCapabilities(params.Capabilities)
60
61 if options.ShowBugReports {
62 // Report the next bug that occurs on the server.
63 bugCh := bug.Notify()
64 go func() {
65 b := <-bugCh
66 msg := &protocol.ShowMessageParams{
67 Type: protocol.Error,
68 Message: fmt.Sprintf("A bug occurred on the server: %s\nLocation:%s", b.Description, b.Key),
69 }
70 if err := s.eventuallyShowMessage(context.Background(), msg); err != nil {
71 log.Printf("error showing bug: %v", err)
72 }
73 }()
74 }
75
76 folders := params.WorkspaceFolders
77 if len(folders) == 0 {
78 if params.RootURI != "" {
79 folders = []protocol.WorkspaceFolder{{
80 URI: string(params.RootURI),
81 Name: path.Base(params.RootURI.SpanURI().Filename()),
82 }}
83 }
84 }
85 for _, folder := range folders {
86 uri := span.URIFromURI(folder.URI)
87 if !uri.IsFile() {
88 continue
89 }
90 s.pendingFolders = append(s.pendingFolders, folder)
91 }
92 // gopls only supports URIs with a file:// scheme, so if we have no
93 // workspace folders with a supported scheme, fail to initialize.
94 if len(folders) > 0 && len(s.pendingFolders) == 0 {
95 return nil, fmt.Errorf("unsupported URI schemes: %v (gopls only supports file URIs)", folders)
96 }
97
98 var codeActionProvider interface{} = true
99 if ca := params.Capabilities.TextDocument.CodeAction; len(ca.CodeActionLiteralSupport.CodeActionKind.ValueSet) > 0 {
100 // If the client has specified CodeActionLiteralSupport,
101 // send the code actions we support.
102 //
103 // Using CodeActionOptions is only valid if codeActionLiteralSupport is set.
104 codeActionProvider = &protocol.CodeActionOptions{
105 CodeActionKinds: s.getSupportedCodeActions(),
106 }
107 }
108 var renameOpts interface{} = true
109 if r := params.Capabilities.TextDocument.Rename; r.PrepareSupport {
110 renameOpts = protocol.RenameOptions{
111 PrepareProvider: r.PrepareSupport,
112 }
113 }
114
115 versionInfo := debug.VersionInfo()
116
117 // golang/go#45732: Warn users who've installed sergi/go-diff@v1.2.0, since
118 // it will corrupt the formatting of their files.
119 for _, dep := range versionInfo.Deps {
120 if dep.Path == "github.com/sergi/go-diff" && dep.Version == "v1.2.0" {
121 if err := s.eventuallyShowMessage(ctx, &protocol.ShowMessageParams{
122 Message: `It looks like you have a bad gopls installation.
123Please reinstall gopls by running 'GO111MODULE=on go install golang.org/x/tools/gopls@latest'.
124See https://github.com/golang/go/issues/45732 for more information.`,
125 Type: protocol.Error,
126 }); err != nil {
127 return nil, err
128 }
129 }
130 }
131
132 goplsVersion, err := json.Marshal(versionInfo)
133 if err != nil {
134 return nil, err
135 }
136
137 return &protocol.InitializeResult{
138 Capabilities: protocol.ServerCapabilities{
139 CallHierarchyProvider: true,
140 CodeActionProvider: codeActionProvider,
Suzy Mueller15782442022-09-19 14:13:01 -0400141 CodeLensProvider: &protocol.CodeLensOptions{}, // must be non-nil to enable the code lens capability
Robert Findleyb15dac22022-08-30 14:40:12 -0400142 CompletionProvider: protocol.CompletionOptions{
143 TriggerCharacters: []string{"."},
144 },
145 DefinitionProvider: true,
146 TypeDefinitionProvider: true,
147 ImplementationProvider: true,
148 DocumentFormattingProvider: true,
149 DocumentSymbolProvider: true,
150 WorkspaceSymbolProvider: true,
151 ExecuteCommandProvider: protocol.ExecuteCommandOptions{
152 Commands: options.SupportedCommands,
153 },
154 FoldingRangeProvider: true,
155 HoverProvider: true,
156 DocumentHighlightProvider: true,
157 DocumentLinkProvider: protocol.DocumentLinkOptions{},
158 InlayHintProvider: protocol.InlayHintOptions{},
159 ReferencesProvider: true,
160 RenameProvider: renameOpts,
Hana (Hyang-Ah) Kim1270fd72022-12-16 16:02:25 -0500161 SelectionRangeProvider: protocol.SelectionRangeRegistrationOptions{},
Robert Findleyb15dac22022-08-30 14:40:12 -0400162 SignatureHelpProvider: protocol.SignatureHelpOptions{
163 TriggerCharacters: []string{"(", ","},
164 },
165 TextDocumentSync: &protocol.TextDocumentSyncOptions{
166 Change: protocol.Incremental,
167 OpenClose: true,
168 Save: protocol.SaveOptions{
169 IncludeText: false,
170 },
171 },
172 Workspace: protocol.Workspace6Gn{
173 WorkspaceFolders: protocol.WorkspaceFolders5Gn{
174 Supported: true,
175 ChangeNotifications: "workspace/didChangeWorkspaceFolders",
176 },
177 },
178 },
Peter Weinberger9250e222022-08-16 11:12:59 -0400179 ServerInfo: protocol.PServerInfoMsg_initialize{
Robert Findleyb15dac22022-08-30 14:40:12 -0400180 Name: "gopls",
181 Version: string(goplsVersion),
182 },
183 }, nil
184}
185
186func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
187 s.stateMu.Lock()
188 if s.state >= serverInitialized {
189 defer s.stateMu.Unlock()
190 return fmt.Errorf("%w: initialized called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
191 }
192 s.state = serverInitialized
193 s.stateMu.Unlock()
194
195 for _, not := range s.notifications {
196 s.client.ShowMessage(ctx, not)
197 }
198 s.notifications = nil
199
200 options := s.session.Options()
201 defer func() { s.session.SetOptions(options) }()
202
203 if err := s.addFolders(ctx, s.pendingFolders); err != nil {
204 return err
205 }
206 s.pendingFolders = nil
Robert Findley20c1ee72022-09-28 13:10:58 -0400207 s.checkViewGoVersions()
Robert Findleyb15dac22022-08-30 14:40:12 -0400208
209 var registrations []protocol.Registration
210 if options.ConfigurationSupported && options.DynamicConfigurationSupported {
211 registrations = append(registrations, protocol.Registration{
212 ID: "workspace/didChangeConfiguration",
213 Method: "workspace/didChangeConfiguration",
214 })
215 }
216 if options.SemanticTokens && options.DynamicRegistrationSemanticTokensSupported {
217 registrations = append(registrations, semanticTokenRegistration(options.SemanticTypes, options.SemanticMods))
218 }
219 if len(registrations) > 0 {
220 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
221 Registrations: registrations,
222 }); err != nil {
223 return err
224 }
225 }
226 return nil
227}
228
Robert Findley42cb7be2022-10-26 18:57:08 -0400229// GoVersionTable maps Go versions to the gopls version in which support will
230// be deprecated, and the final gopls version supporting them without warnings.
231// Keep this in sync with gopls/README.md
Robert Findley20c1ee72022-09-28 13:10:58 -0400232//
Robert Findley42cb7be2022-10-26 18:57:08 -0400233// Must be sorted in ascending order of Go version.
234//
235// Mutable for testing.
236var GoVersionTable = []GoVersionSupport{
237 {12, "", "v0.7.5"},
238 {15, "v0.11.0", "v0.9.5"},
239}
240
241// GoVersionSupport holds information about end-of-life Go version support.
242type GoVersionSupport struct {
243 GoVersion int
244 DeprecatedVersion string // if unset, the version is already deprecated
245 InstallGoplsVersion string
246}
247
248// OldestSupportedGoVersion is the last X in Go 1.X that this version of gopls
249// supports.
250func OldestSupportedGoVersion() int {
251 return GoVersionTable[len(GoVersionTable)-1].GoVersion + 1
252}
253
Robert Findleye074ef82022-10-28 09:11:20 -0400254// versionMessage returns the warning/error message to display if the user is
255// on the given Go version, if any. The goVersion variable is the X in Go 1.X.
256//
257// If goVersion is invalid (< 0), it returns "", 0.
258func versionMessage(goVersion int) (string, protocol.MessageType) {
259 if goVersion < 0 {
260 return "", 0
261 }
262
Robert Findley42cb7be2022-10-26 18:57:08 -0400263 for _, v := range GoVersionTable {
Robert Findleye074ef82022-10-28 09:11:20 -0400264 if goVersion <= v.GoVersion {
Robert Findley42cb7be2022-10-26 18:57:08 -0400265 var msgBuilder strings.Builder
266
267 mType := protocol.Error
Robert Findleye074ef82022-10-28 09:11:20 -0400268 fmt.Fprintf(&msgBuilder, "Found Go version 1.%d", goVersion)
Robert Findley42cb7be2022-10-26 18:57:08 -0400269 if v.DeprecatedVersion != "" {
270 // not deprecated yet, just a warning
271 fmt.Fprintf(&msgBuilder, ", which will be unsupported by gopls %s. ", v.DeprecatedVersion)
272 mType = protocol.Warning
273 } else {
274 fmt.Fprint(&msgBuilder, ", which is not supported by this version of gopls. ")
275 }
276 fmt.Fprintf(&msgBuilder, "Please upgrade to Go 1.%d or later and reinstall gopls. ", OldestSupportedGoVersion())
277 fmt.Fprintf(&msgBuilder, "If you can't upgrade and want this message to go away, please install gopls %s. ", v.InstallGoplsVersion)
278 fmt.Fprint(&msgBuilder, "See https://go.dev/s/gopls-support-policy for more details.")
279
280 return msgBuilder.String(), mType
281 }
282 }
283 return "", 0
284}
Robert Findley20c1ee72022-09-28 13:10:58 -0400285
286// checkViewGoVersions checks whether any Go version used by a view is too old,
287// raising a showMessage notification if so.
288//
289// It should be called after views change.
290func (s *Server) checkViewGoVersions() {
291 oldestVersion := -1
292 for _, view := range s.session.Views() {
293 viewVersion := view.GoVersion()
294 if oldestVersion == -1 || viewVersion < oldestVersion {
295 oldestVersion = viewVersion
296 }
297 }
298
Robert Findley42cb7be2022-10-26 18:57:08 -0400299 if msg, mType := versionMessage(oldestVersion); msg != "" {
Robert Findley20c1ee72022-09-28 13:10:58 -0400300 s.eventuallyShowMessage(context.Background(), &protocol.ShowMessageParams{
Robert Findley42cb7be2022-10-26 18:57:08 -0400301 Type: mType,
Robert Findley20c1ee72022-09-28 13:10:58 -0400302 Message: msg,
303 })
304 }
305}
306
Robert Findleyb15dac22022-08-30 14:40:12 -0400307func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) error {
308 originalViews := len(s.session.Views())
309 viewErrors := make(map[span.URI]error)
310
Alan Donovan32e1cb72022-10-27 12:24:51 -0400311 var ndiagnose sync.WaitGroup // number of unfinished diagnose calls
Robert Findleyb15dac22022-08-30 14:40:12 -0400312 if s.session.Options().VerboseWorkDoneProgress {
313 work := s.progress.Start(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil, nil)
314 defer func() {
315 go func() {
Alan Donovan32e1cb72022-10-27 12:24:51 -0400316 ndiagnose.Wait()
Robert Findleyb15dac22022-08-30 14:40:12 -0400317 work.End(ctx, "Done.")
318 }()
319 }()
320 }
321 // Only one view gets to have a workspace.
Alan Donovan32e1cb72022-10-27 12:24:51 -0400322 var nsnapshots sync.WaitGroup // number of unfinished snapshot initializations
Robert Findleyb15dac22022-08-30 14:40:12 -0400323 for _, folder := range folders {
324 uri := span.URIFromURI(folder.URI)
325 // Ignore non-file URIs.
326 if !uri.IsFile() {
327 continue
328 }
329 work := s.progress.Start(ctx, "Setting up workspace", "Loading packages...", nil, nil)
330 snapshot, release, err := s.addView(ctx, folder.Name, uri)
331 if err != nil {
332 if err == source.ErrViewExists {
333 continue
334 }
335 viewErrors[uri] = err
336 work.End(ctx, fmt.Sprintf("Error loading packages: %s", err))
337 continue
338 }
339 // Inv: release() must be called once.
340
Alan Donovan32e1cb72022-10-27 12:24:51 -0400341 // Initialize snapshot asynchronously.
342 initialized := make(chan struct{})
343 nsnapshots.Add(1)
344 go func() {
345 snapshot.AwaitInitialized(ctx)
346 work.End(ctx, "Finished loading packages.")
347 nsnapshots.Done()
348 close(initialized) // signal
349 }()
350
351 // Diagnose the newly created view asynchronously.
352 ndiagnose.Add(1)
Robert Findleyb15dac22022-08-30 14:40:12 -0400353 go func() {
354 s.diagnoseDetached(snapshot)
Alan Donovan32e1cb72022-10-27 12:24:51 -0400355 <-initialized
Robert Findleyb15dac22022-08-30 14:40:12 -0400356 release()
Alan Donovan32e1cb72022-10-27 12:24:51 -0400357 ndiagnose.Done()
Robert Findleyb15dac22022-08-30 14:40:12 -0400358 }()
359 }
360
Alan Donovan32e1cb72022-10-27 12:24:51 -0400361 // Wait for snapshots to be initialized so that all files are known.
362 // (We don't need to wait for diagnosis to finish.)
363 nsnapshots.Wait()
364
Robert Findleyb15dac22022-08-30 14:40:12 -0400365 // Register for file watching notifications, if they are supported.
Robert Findleyb15dac22022-08-30 14:40:12 -0400366 if err := s.updateWatchedDirectories(ctx); err != nil {
367 event.Error(ctx, "failed to register for file watching notifications", err)
368 }
369
370 if len(viewErrors) > 0 {
371 errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews)
372 for uri, err := range viewErrors {
373 errMsg += fmt.Sprintf("failed to load view for %s: %v\n", uri, err)
374 }
375 return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
376 Type: protocol.Error,
377 Message: errMsg,
378 })
379 }
380 return nil
381}
382
383// updateWatchedDirectories compares the current set of directories to watch
384// with the previously registered set of directories. If the set of directories
385// has changed, we unregister and re-register for file watching notifications.
386// updatedSnapshots is the set of snapshots that have been updated.
387func (s *Server) updateWatchedDirectories(ctx context.Context) error {
388 patterns := s.session.FileWatchingGlobPatterns(ctx)
389
390 s.watchedGlobPatternsMu.Lock()
391 defer s.watchedGlobPatternsMu.Unlock()
392
393 // Nothing to do if the set of workspace directories is unchanged.
394 if equalURISet(s.watchedGlobPatterns, patterns) {
395 return nil
396 }
397
398 // If the set of directories to watch has changed, register the updates and
399 // unregister the previously watched directories. This ordering avoids a
400 // period where no files are being watched. Still, if a user makes on-disk
401 // changes before these updates are complete, we may miss them for the new
402 // directories.
403 prevID := s.watchRegistrationCount - 1
404 if err := s.registerWatchedDirectoriesLocked(ctx, patterns); err != nil {
405 return err
406 }
407 if prevID >= 0 {
408 return s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{
409 Unregisterations: []protocol.Unregistration{{
410 ID: watchedFilesCapabilityID(prevID),
411 Method: "workspace/didChangeWatchedFiles",
412 }},
413 })
414 }
415 return nil
416}
417
418func watchedFilesCapabilityID(id int) string {
419 return fmt.Sprintf("workspace/didChangeWatchedFiles-%d", id)
420}
421
422func equalURISet(m1, m2 map[string]struct{}) bool {
423 if len(m1) != len(m2) {
424 return false
425 }
426 for k := range m1 {
427 _, ok := m2[k]
428 if !ok {
429 return false
430 }
431 }
432 return true
433}
434
435// registerWatchedDirectoriesLocked sends the workspace/didChangeWatchedFiles
436// registrations to the client and updates s.watchedDirectories.
437func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, patterns map[string]struct{}) error {
438 if !s.session.Options().DynamicWatchedFilesSupported {
439 return nil
440 }
441 for k := range s.watchedGlobPatterns {
442 delete(s.watchedGlobPatterns, k)
443 }
444 var watchers []protocol.FileSystemWatcher
445 for pattern := range patterns {
446 watchers = append(watchers, protocol.FileSystemWatcher{
447 GlobPattern: pattern,
448 Kind: uint32(protocol.WatchChange + protocol.WatchDelete + protocol.WatchCreate),
449 })
450 }
451
452 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
453 Registrations: []protocol.Registration{{
454 ID: watchedFilesCapabilityID(s.watchRegistrationCount),
455 Method: "workspace/didChangeWatchedFiles",
456 RegisterOptions: protocol.DidChangeWatchedFilesRegistrationOptions{
457 Watchers: watchers,
458 },
459 }},
460 }); err != nil {
461 return err
462 }
463 s.watchRegistrationCount++
464
465 for k, v := range patterns {
466 s.watchedGlobPatterns[k] = v
467 }
468 return nil
469}
470
471func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI, o *source.Options) error {
472 if !s.session.Options().ConfigurationSupported {
473 return nil
474 }
475 configs, err := s.client.Configuration(ctx, &protocol.ParamConfiguration{
Peter Weinberger9250e222022-08-16 11:12:59 -0400476 Items: []protocol.ConfigurationItem{{
477 ScopeURI: string(folder),
478 Section: "gopls",
479 }},
480 },
481 )
Robert Findleyb15dac22022-08-30 14:40:12 -0400482 if err != nil {
483 return fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
484 }
485 for _, config := range configs {
486 if err := s.handleOptionResults(ctx, source.SetOptions(o, config)); err != nil {
487 return err
488 }
489 }
490 return nil
491}
492
493func (s *Server) eventuallyShowMessage(ctx context.Context, msg *protocol.ShowMessageParams) error {
494 s.stateMu.Lock()
495 defer s.stateMu.Unlock()
496 if s.state == serverInitialized {
497 return s.client.ShowMessage(ctx, msg)
498 }
499 s.notifications = append(s.notifications, msg)
500 return nil
501}
502
503func (s *Server) handleOptionResults(ctx context.Context, results source.OptionResults) error {
Robert Findley89b43352022-10-07 13:43:27 -0400504 var warnings, errors []string
Robert Findleyb15dac22022-08-30 14:40:12 -0400505 for _, result := range results {
Robert Findleyb15dac22022-08-30 14:40:12 -0400506 switch result.Error.(type) {
507 case nil:
508 // nothing to do
509 case *source.SoftError:
Robert Findley89b43352022-10-07 13:43:27 -0400510 warnings = append(warnings, result.Error.Error())
Robert Findleyb15dac22022-08-30 14:40:12 -0400511 default:
Robert Findley89b43352022-10-07 13:43:27 -0400512 errors = append(errors, result.Error.Error())
Robert Findleyb15dac22022-08-30 14:40:12 -0400513 }
514 }
Robert Findley89b43352022-10-07 13:43:27 -0400515
516 // Sort messages, but put errors first.
517 //
518 // Having stable content for the message allows clients to de-duplicate. This
519 // matters because we may send duplicate warnings for clients that support
520 // dynamic configuration: one for the initial settings, and then more for the
521 // individual view settings.
522 var msgs []string
523 msgType := protocol.Warning
524 if len(errors) > 0 {
525 msgType = protocol.Error
526 sort.Strings(errors)
527 msgs = append(msgs, errors...)
528 }
529 if len(warnings) > 0 {
530 sort.Strings(warnings)
531 msgs = append(msgs, warnings...)
532 }
533
534 if len(msgs) > 0 {
535 // Settings
536 combined := "Invalid settings: " + strings.Join(msgs, "; ")
537 params := &protocol.ShowMessageParams{
538 Type: msgType,
539 Message: combined,
540 }
541 return s.eventuallyShowMessage(ctx, params)
542 }
543
Robert Findleyb15dac22022-08-30 14:40:12 -0400544 return nil
545}
546
547// beginFileRequest checks preconditions for a file-oriented request and routes
548// it to a snapshot.
549// We don't want to return errors for benign conditions like wrong file type,
550// so callers should do if !ok { return err } rather than if err != nil.
551// The returned cleanup function is non-nil even in case of false/error result.
Robert Findleya7f033a2023-01-19 18:12:18 -0500552func (s *Server) beginFileRequest(ctx context.Context, pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.FileHandle, bool, func(), error) {
Robert Findleyb15dac22022-08-30 14:40:12 -0400553 uri := pURI.SpanURI()
554 if !uri.IsFile() {
555 // Not a file URI. Stop processing the request, but don't return an error.
556 return nil, nil, false, func() {}, nil
557 }
558 view, err := s.session.ViewOf(uri)
559 if err != nil {
560 return nil, nil, false, func() {}, err
561 }
562 snapshot, release := view.Snapshot(ctx)
Robert Findleya7f033a2023-01-19 18:12:18 -0500563 fh, err := snapshot.GetFile(ctx, uri)
Robert Findleyb15dac22022-08-30 14:40:12 -0400564 if err != nil {
565 release()
566 return nil, nil, false, func() {}, err
567 }
568 if expectKind != source.UnknownKind && view.FileKind(fh) != expectKind {
569 // Wrong kind of file. Nothing to do.
570 release()
571 return nil, nil, false, func() {}, nil
572 }
573 return snapshot, fh, true, release, nil
574}
575
576// shutdown implements the 'shutdown' LSP handler. It releases resources
577// associated with the server and waits for all ongoing work to complete.
578func (s *Server) shutdown(ctx context.Context) error {
579 s.stateMu.Lock()
580 defer s.stateMu.Unlock()
581 if s.state < serverInitialized {
582 event.Log(ctx, "server shutdown without initialization")
583 }
584 if s.state != serverShutDown {
585 // drop all the active views
586 s.session.Shutdown(ctx)
587 s.state = serverShutDown
588 if s.tempDir != "" {
589 if err := os.RemoveAll(s.tempDir); err != nil {
590 event.Error(ctx, "removing temp dir", err)
591 }
592 }
593 }
594 return nil
595}
596
597func (s *Server) exit(ctx context.Context) error {
598 s.stateMu.Lock()
599 defer s.stateMu.Unlock()
600
601 s.client.Close()
602
603 if s.state != serverShutDown {
604 // TODO: We should be able to do better than this.
605 os.Exit(1)
606 }
607 // we don't terminate the process on a normal exit, we just allow it to
608 // close naturally if needed after the connection is closed.
609 return nil
610}