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