golang.org/x/tools/gopls@v0.15.3/internal/server/general.go (about)

     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 server
     6  
     7  // This file defines server methods related to initialization,
     8  // options, shutdown, and exit.
     9  
    10  import (
    11  	"context"
    12  	"encoding/json"
    13  	"fmt"
    14  	"go/build"
    15  	"log"
    16  	"os"
    17  	"path"
    18  	"path/filepath"
    19  	"sort"
    20  	"strings"
    21  	"sync"
    22  
    23  	"golang.org/x/tools/gopls/internal/cache"
    24  	"golang.org/x/tools/gopls/internal/debug"
    25  	"golang.org/x/tools/gopls/internal/file"
    26  	"golang.org/x/tools/gopls/internal/protocol"
    27  	"golang.org/x/tools/gopls/internal/settings"
    28  	"golang.org/x/tools/gopls/internal/telemetry"
    29  	"golang.org/x/tools/gopls/internal/util/bug"
    30  	"golang.org/x/tools/gopls/internal/util/goversion"
    31  	"golang.org/x/tools/gopls/internal/util/maps"
    32  	"golang.org/x/tools/internal/event"
    33  	"golang.org/x/tools/internal/jsonrpc2"
    34  )
    35  
    36  func (s *server) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
    37  	ctx, done := event.Start(ctx, "lsp.Server.initialize")
    38  	defer done()
    39  
    40  	var clientName string
    41  	if params != nil && params.ClientInfo != nil {
    42  		clientName = params.ClientInfo.Name
    43  	}
    44  	telemetry.RecordClientInfo(clientName)
    45  
    46  	s.stateMu.Lock()
    47  	if s.state >= serverInitializing {
    48  		defer s.stateMu.Unlock()
    49  		return nil, fmt.Errorf("%w: initialize called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
    50  	}
    51  	s.state = serverInitializing
    52  	s.stateMu.Unlock()
    53  
    54  	// For uniqueness, use the gopls PID rather than params.ProcessID (the client
    55  	// pid). Some clients might start multiple gopls servers, though they
    56  	// probably shouldn't.
    57  	pid := os.Getpid()
    58  	s.tempDir = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.%s", pid, s.session.ID()))
    59  	err := os.Mkdir(s.tempDir, 0700)
    60  	if err != nil {
    61  		// MkdirTemp could fail due to permissions issues. This is a problem with
    62  		// the user's environment, but should not block gopls otherwise behaving.
    63  		// All usage of s.tempDir should be predicated on having a non-empty
    64  		// s.tempDir.
    65  		event.Error(ctx, "creating temp dir", err)
    66  		s.tempDir = ""
    67  	}
    68  	s.progress.SetSupportsWorkDoneProgress(params.Capabilities.Window.WorkDoneProgress)
    69  
    70  	options := s.Options().Clone()
    71  	// TODO(rfindley): remove the error return from handleOptionResults, and
    72  	// eliminate this defer.
    73  	defer func() { s.SetOptions(options) }()
    74  
    75  	if err := s.handleOptionResults(ctx, settings.SetOptions(options, params.InitializationOptions)); err != nil {
    76  		return nil, err
    77  	}
    78  	options.ForClientCapabilities(params.ClientInfo, params.Capabilities)
    79  
    80  	if options.ShowBugReports {
    81  		// Report the next bug that occurs on the server.
    82  		bug.Handle(func(b bug.Bug) {
    83  			msg := &protocol.ShowMessageParams{
    84  				Type:    protocol.Error,
    85  				Message: fmt.Sprintf("A bug occurred on the server: %s\nLocation:%s", b.Description, b.Key),
    86  			}
    87  			go func() {
    88  				if err := s.eventuallyShowMessage(context.Background(), msg); err != nil {
    89  					log.Printf("error showing bug: %v", err)
    90  				}
    91  			}()
    92  		})
    93  	}
    94  
    95  	folders := params.WorkspaceFolders
    96  	if len(folders) == 0 {
    97  		if params.RootURI != "" {
    98  			folders = []protocol.WorkspaceFolder{{
    99  				URI:  string(params.RootURI),
   100  				Name: path.Base(params.RootURI.Path()),
   101  			}}
   102  		}
   103  	}
   104  	for _, folder := range folders {
   105  		if folder.URI == "" {
   106  			return nil, fmt.Errorf("empty WorkspaceFolder.URI")
   107  		}
   108  		if _, err := protocol.ParseDocumentURI(folder.URI); err != nil {
   109  			return nil, fmt.Errorf("invalid WorkspaceFolder.URI: %v", err)
   110  		}
   111  		s.pendingFolders = append(s.pendingFolders, folder)
   112  	}
   113  
   114  	var codeActionProvider interface{} = true
   115  	if ca := params.Capabilities.TextDocument.CodeAction; len(ca.CodeActionLiteralSupport.CodeActionKind.ValueSet) > 0 {
   116  		// If the client has specified CodeActionLiteralSupport,
   117  		// send the code actions we support.
   118  		//
   119  		// Using CodeActionOptions is only valid if codeActionLiteralSupport is set.
   120  		codeActionProvider = &protocol.CodeActionOptions{
   121  			CodeActionKinds: s.getSupportedCodeActions(),
   122  			ResolveProvider: true,
   123  		}
   124  	}
   125  	var renameOpts interface{} = true
   126  	if r := params.Capabilities.TextDocument.Rename; r != nil && r.PrepareSupport {
   127  		renameOpts = protocol.RenameOptions{
   128  			PrepareProvider: r.PrepareSupport,
   129  		}
   130  	}
   131  
   132  	versionInfo := debug.VersionInfo()
   133  
   134  	goplsVersion, err := json.Marshal(versionInfo)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	return &protocol.InitializeResult{
   140  		Capabilities: protocol.ServerCapabilities{
   141  			CallHierarchyProvider: &protocol.Or_ServerCapabilities_callHierarchyProvider{Value: true},
   142  			CodeActionProvider:    codeActionProvider,
   143  			CodeLensProvider:      &protocol.CodeLensOptions{}, // must be non-nil to enable the code lens capability
   144  			CompletionProvider: &protocol.CompletionOptions{
   145  				TriggerCharacters: []string{"."},
   146  			},
   147  			DefinitionProvider:         &protocol.Or_ServerCapabilities_definitionProvider{Value: true},
   148  			TypeDefinitionProvider:     &protocol.Or_ServerCapabilities_typeDefinitionProvider{Value: true},
   149  			ImplementationProvider:     &protocol.Or_ServerCapabilities_implementationProvider{Value: true},
   150  			DocumentFormattingProvider: &protocol.Or_ServerCapabilities_documentFormattingProvider{Value: true},
   151  			DocumentSymbolProvider:     &protocol.Or_ServerCapabilities_documentSymbolProvider{Value: true},
   152  			WorkspaceSymbolProvider:    &protocol.Or_ServerCapabilities_workspaceSymbolProvider{Value: true},
   153  			ExecuteCommandProvider: &protocol.ExecuteCommandOptions{
   154  				Commands: protocol.NonNilSlice(options.SupportedCommands),
   155  			},
   156  			FoldingRangeProvider:      &protocol.Or_ServerCapabilities_foldingRangeProvider{Value: true},
   157  			HoverProvider:             &protocol.Or_ServerCapabilities_hoverProvider{Value: true},
   158  			DocumentHighlightProvider: &protocol.Or_ServerCapabilities_documentHighlightProvider{Value: true},
   159  			DocumentLinkProvider:      &protocol.DocumentLinkOptions{},
   160  			InlayHintProvider:         protocol.InlayHintOptions{},
   161  			ReferencesProvider:        &protocol.Or_ServerCapabilities_referencesProvider{Value: true},
   162  			RenameProvider:            renameOpts,
   163  			SelectionRangeProvider:    &protocol.Or_ServerCapabilities_selectionRangeProvider{Value: true},
   164  			SemanticTokensProvider: protocol.SemanticTokensOptions{
   165  				Range: &protocol.Or_SemanticTokensOptions_range{Value: true},
   166  				Full:  &protocol.Or_SemanticTokensOptions_full{Value: true},
   167  				Legend: protocol.SemanticTokensLegend{
   168  					TokenTypes:     protocol.NonNilSlice(options.SemanticTypes),
   169  					TokenModifiers: protocol.NonNilSlice(options.SemanticMods),
   170  				},
   171  			},
   172  			SignatureHelpProvider: &protocol.SignatureHelpOptions{
   173  				TriggerCharacters: []string{"(", ","},
   174  			},
   175  			TextDocumentSync: &protocol.TextDocumentSyncOptions{
   176  				Change:    protocol.Incremental,
   177  				OpenClose: true,
   178  				Save: &protocol.SaveOptions{
   179  					IncludeText: false,
   180  				},
   181  			},
   182  			Workspace: &protocol.WorkspaceOptions{
   183  				WorkspaceFolders: &protocol.WorkspaceFolders5Gn{
   184  					Supported:           true,
   185  					ChangeNotifications: "workspace/didChangeWorkspaceFolders",
   186  				},
   187  			},
   188  		},
   189  		ServerInfo: &protocol.ServerInfo{
   190  			Name:    "gopls",
   191  			Version: string(goplsVersion),
   192  		},
   193  	}, nil
   194  }
   195  
   196  func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
   197  	ctx, done := event.Start(ctx, "lsp.Server.initialized")
   198  	defer done()
   199  
   200  	s.stateMu.Lock()
   201  	if s.state >= serverInitialized {
   202  		defer s.stateMu.Unlock()
   203  		return fmt.Errorf("%w: initialized called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
   204  	}
   205  	s.state = serverInitialized
   206  	s.stateMu.Unlock()
   207  
   208  	for _, not := range s.notifications {
   209  		s.client.ShowMessage(ctx, not)
   210  	}
   211  	s.notifications = nil
   212  
   213  	s.addFolders(ctx, s.pendingFolders)
   214  
   215  	s.pendingFolders = nil
   216  	s.checkViewGoVersions()
   217  
   218  	var registrations []protocol.Registration
   219  	options := s.Options()
   220  	if options.ConfigurationSupported && options.DynamicConfigurationSupported {
   221  		registrations = append(registrations, protocol.Registration{
   222  			ID:     "workspace/didChangeConfiguration",
   223  			Method: "workspace/didChangeConfiguration",
   224  		})
   225  	}
   226  	if len(registrations) > 0 {
   227  		if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
   228  			Registrations: registrations,
   229  		}); err != nil {
   230  			return err
   231  		}
   232  	}
   233  
   234  	// Ask (maybe) about enabling telemetry. Do this asynchronously, as it's OK
   235  	// for users to ignore or dismiss the question.
   236  	go s.maybePromptForTelemetry(ctx, options.TelemetryPrompt)
   237  
   238  	return nil
   239  }
   240  
   241  // checkViewGoVersions checks whether any Go version used by a view is too old,
   242  // raising a showMessage notification if so.
   243  //
   244  // It should be called after views change.
   245  func (s *server) checkViewGoVersions() {
   246  	oldestVersion, fromBuild := go1Point(), true
   247  	for _, view := range s.session.Views() {
   248  		viewVersion := view.GoVersion()
   249  		if oldestVersion == -1 || viewVersion < oldestVersion {
   250  			oldestVersion, fromBuild = viewVersion, false
   251  		}
   252  		telemetry.RecordViewGoVersion(viewVersion)
   253  	}
   254  
   255  	if msg, isError := goversion.Message(oldestVersion, fromBuild); msg != "" {
   256  		mType := protocol.Warning
   257  		if isError {
   258  			mType = protocol.Error
   259  		}
   260  		s.eventuallyShowMessage(context.Background(), &protocol.ShowMessageParams{
   261  			Type:    mType,
   262  			Message: msg,
   263  		})
   264  	}
   265  }
   266  
   267  // go1Point returns the x in Go 1.x. If an error occurs extracting the go
   268  // version, it returns -1.
   269  //
   270  // Copied from the testenv package.
   271  func go1Point() int {
   272  	for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {
   273  		var version int
   274  		if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil {
   275  			continue
   276  		}
   277  		return version
   278  	}
   279  	return -1
   280  }
   281  
   282  // addFolders adds the specified list of "folders" (that's Windows for
   283  // directories) to the session. It does not return an error, though it
   284  // may report an error to the client over LSP if one or more folders
   285  // had problems.
   286  func (s *server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) {
   287  	originalViews := len(s.session.Views())
   288  	viewErrors := make(map[protocol.URI]error)
   289  
   290  	var ndiagnose sync.WaitGroup // number of unfinished diagnose calls
   291  	if s.Options().VerboseWorkDoneProgress {
   292  		work := s.progress.Start(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil, nil)
   293  		defer func() {
   294  			go func() {
   295  				ndiagnose.Wait()
   296  				work.End(ctx, "Done.")
   297  			}()
   298  		}()
   299  	}
   300  	// Only one view gets to have a workspace.
   301  	var nsnapshots sync.WaitGroup // number of unfinished snapshot initializations
   302  	for _, folder := range folders {
   303  		uri, err := protocol.ParseDocumentURI(folder.URI)
   304  		if err != nil {
   305  			viewErrors[folder.URI] = fmt.Errorf("invalid folder URI: %v", err)
   306  			continue
   307  		}
   308  		work := s.progress.Start(ctx, "Setting up workspace", "Loading packages...", nil, nil)
   309  		snapshot, release, err := s.addView(ctx, folder.Name, uri)
   310  		if err != nil {
   311  			if err == cache.ErrViewExists {
   312  				continue
   313  			}
   314  			viewErrors[folder.URI] = err
   315  			work.End(ctx, fmt.Sprintf("Error loading packages: %s", err))
   316  			continue
   317  		}
   318  		// Inv: release() must be called once.
   319  
   320  		// Initialize snapshot asynchronously.
   321  		initialized := make(chan struct{})
   322  		nsnapshots.Add(1)
   323  		go func() {
   324  			snapshot.AwaitInitialized(ctx)
   325  			work.End(ctx, "Finished loading packages.")
   326  			nsnapshots.Done()
   327  			close(initialized) // signal
   328  		}()
   329  
   330  		// Diagnose the newly created view asynchronously.
   331  		ndiagnose.Add(1)
   332  		go func() {
   333  			s.diagnoseSnapshot(snapshot, nil, 0)
   334  			<-initialized
   335  			release()
   336  			ndiagnose.Done()
   337  		}()
   338  	}
   339  
   340  	// Wait for snapshots to be initialized so that all files are known.
   341  	// (We don't need to wait for diagnosis to finish.)
   342  	nsnapshots.Wait()
   343  
   344  	// Register for file watching notifications, if they are supported.
   345  	if err := s.updateWatchedDirectories(ctx); err != nil {
   346  		event.Error(ctx, "failed to register for file watching notifications", err)
   347  	}
   348  
   349  	// Report any errors using the protocol.
   350  	if len(viewErrors) > 0 {
   351  		errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews)
   352  		for uri, err := range viewErrors {
   353  			errMsg += fmt.Sprintf("failed to load view for %s: %v\n", uri, err)
   354  		}
   355  		showMessage(ctx, s.client, protocol.Error, errMsg)
   356  	}
   357  }
   358  
   359  // updateWatchedDirectories compares the current set of directories to watch
   360  // with the previously registered set of directories. If the set of directories
   361  // has changed, we unregister and re-register for file watching notifications.
   362  // updatedSnapshots is the set of snapshots that have been updated.
   363  func (s *server) updateWatchedDirectories(ctx context.Context) error {
   364  	patterns := s.session.FileWatchingGlobPatterns(ctx)
   365  
   366  	s.watchedGlobPatternsMu.Lock()
   367  	defer s.watchedGlobPatternsMu.Unlock()
   368  
   369  	// Nothing to do if the set of workspace directories is unchanged.
   370  	if maps.SameKeys(s.watchedGlobPatterns, patterns) {
   371  		return nil
   372  	}
   373  
   374  	// If the set of directories to watch has changed, register the updates and
   375  	// unregister the previously watched directories. This ordering avoids a
   376  	// period where no files are being watched. Still, if a user makes on-disk
   377  	// changes before these updates are complete, we may miss them for the new
   378  	// directories.
   379  	prevID := s.watchRegistrationCount - 1
   380  	if err := s.registerWatchedDirectoriesLocked(ctx, patterns); err != nil {
   381  		return err
   382  	}
   383  	if prevID >= 0 {
   384  		return s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{
   385  			Unregisterations: []protocol.Unregistration{{
   386  				ID:     watchedFilesCapabilityID(prevID),
   387  				Method: "workspace/didChangeWatchedFiles",
   388  			}},
   389  		})
   390  	}
   391  	return nil
   392  }
   393  
   394  func watchedFilesCapabilityID(id int) string {
   395  	return fmt.Sprintf("workspace/didChangeWatchedFiles-%d", id)
   396  }
   397  
   398  // registerWatchedDirectoriesLocked sends the workspace/didChangeWatchedFiles
   399  // registrations to the client and updates s.watchedDirectories.
   400  // The caller must not subsequently mutate patterns.
   401  func (s *server) registerWatchedDirectoriesLocked(ctx context.Context, patterns map[protocol.RelativePattern]unit) error {
   402  	if !s.Options().DynamicWatchedFilesSupported {
   403  		return nil
   404  	}
   405  
   406  	supportsRelativePatterns := s.Options().RelativePatternsSupported
   407  
   408  	s.watchedGlobPatterns = patterns
   409  	watchers := make([]protocol.FileSystemWatcher, 0, len(patterns)) // must be a slice
   410  	val := protocol.WatchChange | protocol.WatchDelete | protocol.WatchCreate
   411  	for pattern := range patterns {
   412  		var value any
   413  		if supportsRelativePatterns && pattern.BaseURI != "" {
   414  			value = pattern
   415  		} else {
   416  			p := pattern.Pattern
   417  			if pattern.BaseURI != "" {
   418  				p = path.Join(filepath.ToSlash(pattern.BaseURI.Path()), p)
   419  			}
   420  			value = p
   421  		}
   422  		watchers = append(watchers, protocol.FileSystemWatcher{
   423  			GlobPattern: protocol.GlobPattern{Value: value},
   424  			Kind:        &val,
   425  		})
   426  	}
   427  
   428  	if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
   429  		Registrations: []protocol.Registration{{
   430  			ID:     watchedFilesCapabilityID(s.watchRegistrationCount),
   431  			Method: "workspace/didChangeWatchedFiles",
   432  			RegisterOptions: protocol.DidChangeWatchedFilesRegistrationOptions{
   433  				Watchers: watchers,
   434  			},
   435  		}},
   436  	}); err != nil {
   437  		return err
   438  	}
   439  	s.watchRegistrationCount++
   440  	return nil
   441  }
   442  
   443  // Options returns the current server options.
   444  //
   445  // The caller must not modify the result.
   446  func (s *server) Options() *settings.Options {
   447  	s.optionsMu.Lock()
   448  	defer s.optionsMu.Unlock()
   449  	return s.options
   450  }
   451  
   452  // SetOptions sets the current server options.
   453  //
   454  // The caller must not subsequently modify the options.
   455  func (s *server) SetOptions(opts *settings.Options) {
   456  	s.optionsMu.Lock()
   457  	defer s.optionsMu.Unlock()
   458  	s.options = opts
   459  }
   460  
   461  func (s *server) newFolder(ctx context.Context, folder protocol.DocumentURI, name string, opts *settings.Options) (*cache.Folder, error) {
   462  	env, err := cache.FetchGoEnv(ctx, folder, opts)
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  	return &cache.Folder{
   467  		Dir:     folder,
   468  		Name:    name,
   469  		Options: opts,
   470  		Env:     *env,
   471  	}, nil
   472  }
   473  
   474  // fetchFolderOptions makes a workspace/configuration request for the given
   475  // folder, and populates options with the result.
   476  //
   477  // If folder is "", fetchFolderOptions makes an unscoped request.
   478  func (s *server) fetchFolderOptions(ctx context.Context, folder protocol.DocumentURI) (*settings.Options, error) {
   479  	opts := s.Options()
   480  	if !opts.ConfigurationSupported {
   481  		return opts, nil
   482  	}
   483  	var scopeURI *string
   484  	if folder != "" {
   485  		scope := string(folder)
   486  		scopeURI = &scope
   487  	}
   488  	configs, err := s.client.Configuration(ctx, &protocol.ParamConfiguration{
   489  		Items: []protocol.ConfigurationItem{{
   490  			ScopeURI: scopeURI,
   491  			Section:  "gopls",
   492  		}},
   493  	},
   494  	)
   495  	if err != nil {
   496  		return nil, fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
   497  	}
   498  
   499  	opts = opts.Clone()
   500  	for _, config := range configs {
   501  		if err := s.handleOptionResults(ctx, settings.SetOptions(opts, config)); err != nil {
   502  			return nil, err
   503  		}
   504  	}
   505  	return opts, nil
   506  }
   507  
   508  func (s *server) eventuallyShowMessage(ctx context.Context, msg *protocol.ShowMessageParams) error {
   509  	s.stateMu.Lock()
   510  	defer s.stateMu.Unlock()
   511  	if s.state == serverInitialized {
   512  		return s.client.ShowMessage(ctx, msg)
   513  	}
   514  	s.notifications = append(s.notifications, msg)
   515  	return nil
   516  }
   517  
   518  func (s *server) handleOptionResults(ctx context.Context, results settings.OptionResults) error {
   519  	var warnings, errors []string
   520  	for _, result := range results {
   521  		switch result.Error.(type) {
   522  		case nil:
   523  			// nothing to do
   524  		case *settings.SoftError:
   525  			warnings = append(warnings, result.Error.Error())
   526  		default:
   527  			errors = append(errors, result.Error.Error())
   528  		}
   529  	}
   530  
   531  	// Sort messages, but put errors first.
   532  	//
   533  	// Having stable content for the message allows clients to de-duplicate. This
   534  	// matters because we may send duplicate warnings for clients that support
   535  	// dynamic configuration: one for the initial settings, and then more for the
   536  	// individual viewsettings.
   537  	var msgs []string
   538  	msgType := protocol.Warning
   539  	if len(errors) > 0 {
   540  		msgType = protocol.Error
   541  		sort.Strings(errors)
   542  		msgs = append(msgs, errors...)
   543  	}
   544  	if len(warnings) > 0 {
   545  		sort.Strings(warnings)
   546  		msgs = append(msgs, warnings...)
   547  	}
   548  
   549  	if len(msgs) > 0 {
   550  		// Settings
   551  		combined := "Invalid settings: " + strings.Join(msgs, "; ")
   552  		params := &protocol.ShowMessageParams{
   553  			Type:    msgType,
   554  			Message: combined,
   555  		}
   556  		return s.eventuallyShowMessage(ctx, params)
   557  	}
   558  
   559  	return nil
   560  }
   561  
   562  // fileOf returns the file for a given URI and its snapshot.
   563  // On success, the returned function must be called to release the snapshot.
   564  func (s *server) fileOf(ctx context.Context, uri protocol.DocumentURI) (file.Handle, *cache.Snapshot, func(), error) {
   565  	snapshot, release, err := s.session.SnapshotOf(ctx, uri)
   566  	if err != nil {
   567  		return nil, nil, nil, err
   568  	}
   569  	fh, err := snapshot.ReadFile(ctx, uri)
   570  	if err != nil {
   571  		release()
   572  		return nil, nil, nil, err
   573  	}
   574  	return fh, snapshot, release, nil
   575  }
   576  
   577  // shutdown implements the 'shutdown' LSP handler. It releases resources
   578  // associated with the server and waits for all ongoing work to complete.
   579  func (s *server) Shutdown(ctx context.Context) error {
   580  	ctx, done := event.Start(ctx, "lsp.Server.shutdown")
   581  	defer done()
   582  
   583  	s.stateMu.Lock()
   584  	defer s.stateMu.Unlock()
   585  	if s.state < serverInitialized {
   586  		event.Log(ctx, "server shutdown without initialization")
   587  	}
   588  	if s.state != serverShutDown {
   589  		// drop all the active views
   590  		s.session.Shutdown(ctx)
   591  		s.state = serverShutDown
   592  		if s.tempDir != "" {
   593  			if err := os.RemoveAll(s.tempDir); err != nil {
   594  				event.Error(ctx, "removing temp dir", err)
   595  			}
   596  		}
   597  	}
   598  	return nil
   599  }
   600  
   601  func (s *server) Exit(ctx context.Context) error {
   602  	ctx, done := event.Start(ctx, "lsp.Server.exit")
   603  	defer done()
   604  
   605  	s.stateMu.Lock()
   606  	defer s.stateMu.Unlock()
   607  
   608  	s.client.Close()
   609  
   610  	if s.state != serverShutDown {
   611  		// TODO: We should be able to do better than this.
   612  		os.Exit(1)
   613  	}
   614  	// We don't terminate the process on a normal exit, we just allow it to
   615  	// close naturally if needed after the connection is closed.
   616  	return nil
   617  }