2019-04-17 13:37:20 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package lsp
|
|
|
|
|
|
|
|
import (
|
2019-05-03 12:31:03 -06:00
|
|
|
"bytes"
|
2019-04-17 13:37:20 -06:00
|
|
|
"context"
|
2019-09-11 11:13:44 -06:00
|
|
|
"fmt"
|
2020-04-30 13:59:37 -06:00
|
|
|
"io"
|
2019-04-17 13:37:20 -06:00
|
|
|
"os"
|
|
|
|
"path"
|
2020-07-07 12:50:57 -06:00
|
|
|
"sync"
|
2019-04-17 13:37:20 -06:00
|
|
|
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
2019-04-17 13:37:20 -06:00
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
2019-05-29 17:58:27 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/debug"
|
2020-03-23 20:47:52 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/debug/tag"
|
2019-04-17 13:37:20 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2019-05-14 21:04:23 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
2019-04-17 13:37:20 -06:00
|
|
|
"golang.org/x/tools/internal/span"
|
|
|
|
)
|
|
|
|
|
2019-11-17 12:29:15 -07:00
|
|
|
func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
|
2019-07-29 21:48:11 -06:00
|
|
|
s.stateMu.Lock()
|
2020-03-09 12:17:49 -06:00
|
|
|
if s.state >= serverInitializing {
|
|
|
|
defer s.stateMu.Unlock()
|
2020-04-09 20:37:52 -06:00
|
|
|
return nil, fmt.Errorf("%w: initialize called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
|
2019-04-17 13:37:20 -06:00
|
|
|
}
|
2019-07-27 12:59:14 -06:00
|
|
|
s.state = serverInitializing
|
2019-07-29 21:48:11 -06:00
|
|
|
s.stateMu.Unlock()
|
2019-04-17 13:37:20 -06:00
|
|
|
|
2020-03-12 14:31:00 -06:00
|
|
|
s.supportsWorkDoneProgress = params.Capabilities.Window.WorkDoneProgress
|
2020-04-22 12:41:03 -06:00
|
|
|
s.inProgress = map[string]*WorkDone{}
|
2020-03-12 14:31:00 -06:00
|
|
|
|
2019-09-05 22:17:36 -06:00
|
|
|
options := s.session.Options()
|
|
|
|
defer func() { s.session.SetOptions(options) }()
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
// TODO: Handle results here.
|
2019-09-09 07:28:25 -06:00
|
|
|
source.SetOptions(&options, params.InitializationOptions)
|
|
|
|
options.ForClientCapabilities(params.Capabilities)
|
2019-04-17 13:37:20 -06:00
|
|
|
|
2020-06-25 23:34:55 -06:00
|
|
|
if params.RootURI != "" && !params.RootURI.SpanURI().IsFile() {
|
2020-04-02 13:32:48 -06:00
|
|
|
return nil, fmt.Errorf("unsupported URI scheme: %v (gopls only supports file URIs)", params.RootURI)
|
|
|
|
}
|
2019-09-09 11:04:12 -06:00
|
|
|
s.pendingFolders = params.WorkspaceFolders
|
|
|
|
if len(s.pendingFolders) == 0 {
|
2019-04-17 13:37:20 -06:00
|
|
|
if params.RootURI != "" {
|
2019-09-09 11:04:12 -06:00
|
|
|
s.pendingFolders = []protocol.WorkspaceFolder{{
|
2020-02-12 14:36:46 -07:00
|
|
|
URI: string(params.RootURI),
|
|
|
|
Name: path.Base(params.RootURI.SpanURI().Filename()),
|
2019-04-17 13:37:20 -06:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
}
|
2019-05-06 14:44:43 -06:00
|
|
|
|
2019-11-21 21:42:27 -07:00
|
|
|
var codeActionProvider interface{} = true
|
2019-11-17 12:29:15 -07:00
|
|
|
if ca := params.Capabilities.TextDocument.CodeAction; len(ca.CodeActionLiteralSupport.CodeActionKind.ValueSet) > 0 {
|
2019-08-14 13:24:21 -06:00
|
|
|
// If the client has specified CodeActionLiteralSupport,
|
|
|
|
// send the code actions we support.
|
|
|
|
//
|
|
|
|
// Using CodeActionOptions is only valid if codeActionLiteralSupport is set.
|
|
|
|
codeActionProvider = &protocol.CodeActionOptions{
|
|
|
|
CodeActionKinds: s.getSupportedCodeActions(),
|
|
|
|
}
|
|
|
|
}
|
2019-11-21 21:42:27 -07:00
|
|
|
var renameOpts interface{} = true
|
|
|
|
if r := params.Capabilities.TextDocument.Rename; r.PrepareSupport {
|
|
|
|
renameOpts = protocol.RenameOptions{
|
|
|
|
PrepareProvider: r.PrepareSupport,
|
|
|
|
}
|
2019-08-22 11:31:03 -06:00
|
|
|
}
|
2020-04-15 13:59:30 -06:00
|
|
|
|
|
|
|
goplsVer := &bytes.Buffer{}
|
|
|
|
debug.PrintVersionInfo(ctx, goplsVer, true, debug.PlainText)
|
|
|
|
|
2019-04-17 13:37:20 -06:00
|
|
|
return &protocol.InitializeResult{
|
|
|
|
Capabilities: protocol.ServerCapabilities{
|
2019-08-14 13:24:21 -06:00
|
|
|
CodeActionProvider: codeActionProvider,
|
2019-11-17 12:29:15 -07:00
|
|
|
CompletionProvider: protocol.CompletionOptions{
|
2019-04-17 13:37:20 -06:00
|
|
|
TriggerCharacters: []string{"."},
|
|
|
|
},
|
2019-05-08 03:44:01 -06:00
|
|
|
DefinitionProvider: true,
|
2019-10-31 16:19:51 -06:00
|
|
|
TypeDefinitionProvider: true,
|
|
|
|
ImplementationProvider: true,
|
2019-05-08 03:44:01 -06:00
|
|
|
DocumentFormattingProvider: true,
|
|
|
|
DocumentSymbolProvider: true,
|
2020-01-05 03:46:20 -07:00
|
|
|
WorkspaceSymbolProvider: true,
|
2019-11-17 12:29:15 -07:00
|
|
|
ExecuteCommandProvider: protocol.ExecuteCommandOptions{
|
2019-09-18 23:21:54 -06:00
|
|
|
Commands: options.SupportedCommands,
|
|
|
|
},
|
|
|
|
FoldingRangeProvider: true,
|
|
|
|
HoverProvider: true,
|
|
|
|
DocumentHighlightProvider: true,
|
2019-11-17 12:29:15 -07:00
|
|
|
DocumentLinkProvider: protocol.DocumentLinkOptions{},
|
2019-09-18 23:21:54 -06:00
|
|
|
ReferencesProvider: true,
|
|
|
|
RenameProvider: renameOpts,
|
2019-11-17 12:29:15 -07:00
|
|
|
SignatureHelpProvider: protocol.SignatureHelpOptions{
|
2019-04-17 13:37:20 -06:00
|
|
|
TriggerCharacters: []string{"(", ","},
|
|
|
|
},
|
|
|
|
TextDocumentSync: &protocol.TextDocumentSyncOptions{
|
2019-12-24 14:16:06 -07:00
|
|
|
Change: protocol.Incremental,
|
2019-04-17 13:37:20 -06:00
|
|
|
OpenClose: true,
|
2019-11-17 12:29:15 -07:00
|
|
|
Save: protocol.SaveOptions{
|
2019-06-18 10:41:22 -06:00
|
|
|
IncludeText: false,
|
|
|
|
},
|
2019-04-17 13:37:20 -06:00
|
|
|
},
|
2019-11-17 12:29:15 -07:00
|
|
|
Workspace: protocol.WorkspaceGn{
|
2019-11-20 14:38:43 -07:00
|
|
|
WorkspaceFolders: protocol.WorkspaceFoldersGn{
|
2019-05-06 14:44:43 -06:00
|
|
|
Supported: true,
|
|
|
|
ChangeNotifications: "workspace/didChangeWorkspaceFolders",
|
|
|
|
},
|
|
|
|
},
|
2019-04-17 13:37:20 -06:00
|
|
|
},
|
2020-04-15 13:59:30 -06:00
|
|
|
ServerInfo: struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Version string `json:"version,omitempty"`
|
|
|
|
}{
|
|
|
|
Name: "gopls",
|
|
|
|
Version: goplsVer.String(),
|
|
|
|
},
|
2019-04-17 13:37:20 -06:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
|
2019-07-29 21:48:11 -06:00
|
|
|
s.stateMu.Lock()
|
2020-03-09 12:17:49 -06:00
|
|
|
if s.state >= serverInitialized {
|
|
|
|
defer s.stateMu.Unlock()
|
2020-04-09 20:37:52 -06:00
|
|
|
return fmt.Errorf("%w: initalized called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
|
2020-03-09 12:17:49 -06:00
|
|
|
}
|
2019-07-29 21:48:11 -06:00
|
|
|
s.state = serverInitialized
|
|
|
|
s.stateMu.Unlock()
|
|
|
|
|
2019-09-05 22:17:36 -06:00
|
|
|
options := s.session.Options()
|
|
|
|
defer func() { s.session.SetOptions(options) }()
|
|
|
|
|
2019-08-19 10:53:51 -06:00
|
|
|
var registrations []protocol.Registration
|
2019-09-05 22:17:36 -06:00
|
|
|
if options.ConfigurationSupported && options.DynamicConfigurationSupported {
|
2019-08-19 10:53:51 -06:00
|
|
|
registrations = append(registrations,
|
|
|
|
protocol.Registration{
|
|
|
|
ID: "workspace/didChangeConfiguration",
|
|
|
|
Method: "workspace/didChangeConfiguration",
|
|
|
|
},
|
|
|
|
protocol.Registration{
|
|
|
|
ID: "workspace/didChangeWorkspaceFolders",
|
|
|
|
Method: "workspace/didChangeWorkspaceFolders",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:34:55 -06:00
|
|
|
// TODO: this event logging may be unnecessary.
|
|
|
|
// The version info is included in the initialize response.
|
2019-05-03 12:31:03 -06:00
|
|
|
buf := &bytes.Buffer{}
|
2020-03-23 20:47:52 -06:00
|
|
|
debug.PrintVersionInfo(ctx, buf, true, debug.PlainText)
|
2020-04-20 10:14:12 -06:00
|
|
|
event.Log(ctx, buf.String())
|
2019-09-09 11:04:12 -06:00
|
|
|
|
2020-06-25 23:34:55 -06:00
|
|
|
if err := s.addFolders(ctx, s.pendingFolders); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-11-21 16:55:49 -07:00
|
|
|
s.pendingFolders = nil
|
|
|
|
|
2020-06-20 14:11:01 -06:00
|
|
|
if options.DynamicWatchedFilesSupported {
|
|
|
|
for _, view := range s.session.Views() {
|
|
|
|
dirs, err := view.WorkspaceDirectories(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, dir := range dirs {
|
|
|
|
registrations = append(registrations, protocol.Registration{
|
|
|
|
ID: "workspace/didChangeWatchedFiles",
|
|
|
|
Method: "workspace/didChangeWatchedFiles",
|
|
|
|
RegisterOptions: protocol.DidChangeWatchedFilesRegistrationOptions{
|
|
|
|
Watchers: []protocol.FileSystemWatcher{{
|
|
|
|
GlobPattern: fmt.Sprintf("%s/**.go", dir),
|
|
|
|
Kind: float64(protocol.WatchChange + protocol.WatchDelete + protocol.WatchCreate),
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(registrations) > 0 {
|
|
|
|
s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
|
|
|
|
Registrations: registrations,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-11-21 16:55:49 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-25 23:34:55 -06:00
|
|
|
func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) error {
|
2019-11-21 16:55:49 -07:00
|
|
|
originalViews := len(s.session.Views())
|
2019-11-15 10:43:45 -07:00
|
|
|
viewErrors := make(map[span.URI]error)
|
2019-11-21 16:55:49 -07:00
|
|
|
|
2020-07-07 12:50:57 -06:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
if s.session.Options().VerboseWorkDoneProgress {
|
|
|
|
work := s.StartWork(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil)
|
|
|
|
defer func() {
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
work.End(ctx, "Done.")
|
|
|
|
}()
|
|
|
|
|
|
|
|
}()
|
|
|
|
}
|
2019-11-21 16:55:49 -07:00
|
|
|
for _, folder := range folders {
|
2020-02-12 14:36:46 -07:00
|
|
|
uri := span.URIFromURI(folder.URI)
|
2020-03-23 20:47:52 -06:00
|
|
|
view, snapshot, err := s.addView(ctx, folder.Name, uri)
|
2019-11-15 12:47:29 -07:00
|
|
|
if err != nil {
|
2019-11-15 10:43:45 -07:00
|
|
|
viewErrors[uri] = err
|
2019-11-15 12:47:29 -07:00
|
|
|
continue
|
2019-09-09 11:04:12 -06:00
|
|
|
}
|
2020-03-23 20:47:52 -06:00
|
|
|
// Print each view's environment.
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
if err := view.WriteEnv(ctx, buf); err != nil {
|
|
|
|
event.Error(ctx, "failed to write environment", err, tag.Directory.Of(view.Folder()))
|
|
|
|
continue
|
|
|
|
}
|
2020-04-20 10:14:12 -06:00
|
|
|
event.Log(ctx, buf.String())
|
2020-03-23 20:47:52 -06:00
|
|
|
|
|
|
|
// Diagnose the newly created view.
|
2020-07-07 12:50:57 -06:00
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
s.diagnoseDetached(snapshot)
|
|
|
|
wg.Done()
|
|
|
|
}()
|
2019-09-09 11:04:12 -06:00
|
|
|
}
|
2019-11-15 10:43:45 -07:00
|
|
|
if len(viewErrors) > 0 {
|
2019-11-21 16:55:49 -07:00
|
|
|
errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews)
|
2019-11-15 10:43:45 -07:00
|
|
|
for uri, err := range viewErrors {
|
|
|
|
errMsg += fmt.Sprintf("failed to load view for %s: %v\n", uri, err)
|
|
|
|
}
|
2020-06-25 23:34:55 -06:00
|
|
|
return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
|
2019-11-15 10:43:45 -07:00
|
|
|
Type: protocol.Error,
|
|
|
|
Message: errMsg,
|
|
|
|
})
|
|
|
|
}
|
2020-06-25 23:34:55 -06:00
|
|
|
return nil
|
2019-04-17 13:37:20 -06:00
|
|
|
}
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI, o *source.Options) error {
|
2019-09-09 07:28:25 -06:00
|
|
|
if !s.session.Options().ConfigurationSupported {
|
2019-09-09 11:04:12 -06:00
|
|
|
return nil
|
|
|
|
}
|
2019-11-17 12:29:15 -07:00
|
|
|
v := protocol.ParamConfiguration{
|
2019-09-16 22:38:16 -06:00
|
|
|
ConfigurationParams: protocol.ConfigurationParams{
|
2019-09-07 15:01:26 -06:00
|
|
|
Items: []protocol.ConfigurationItem{{
|
2020-02-12 14:36:46 -07:00
|
|
|
ScopeURI: string(folder),
|
2019-09-07 15:01:26 -06:00
|
|
|
Section: "gopls",
|
|
|
|
}, {
|
2020-02-12 14:36:46 -07:00
|
|
|
ScopeURI: string(folder),
|
2019-09-18 10:32:40 -06:00
|
|
|
Section: fmt.Sprintf("gopls-%s", name),
|
2019-09-16 22:38:16 -06:00
|
|
|
}},
|
|
|
|
},
|
2019-09-07 15:01:26 -06:00
|
|
|
}
|
|
|
|
configs, err := s.client.Configuration(ctx, &v)
|
2019-07-27 12:59:14 -06:00
|
|
|
if err != nil {
|
2020-06-30 15:55:40 -06:00
|
|
|
return fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
|
2019-07-27 12:59:14 -06:00
|
|
|
}
|
|
|
|
for _, config := range configs {
|
2019-09-11 11:13:44 -06:00
|
|
|
results := source.SetOptions(o, config)
|
|
|
|
for _, result := range results {
|
|
|
|
if result.Error != nil {
|
2020-06-30 15:55:40 -06:00
|
|
|
if err := s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
|
2019-09-11 11:13:44 -06:00
|
|
|
Type: protocol.Error,
|
|
|
|
Message: result.Error.Error(),
|
2020-06-30 15:55:40 -06:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-09-11 11:13:44 -06:00
|
|
|
}
|
|
|
|
switch result.State {
|
|
|
|
case source.OptionUnexpected:
|
2020-06-30 15:55:40 -06:00
|
|
|
if err := s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
|
2019-09-11 11:13:44 -06:00
|
|
|
Type: protocol.Error,
|
|
|
|
Message: fmt.Sprintf("unexpected config %s", result.Name),
|
2020-06-30 15:55:40 -06:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-09-11 11:13:44 -06:00
|
|
|
case source.OptionDeprecated:
|
|
|
|
msg := fmt.Sprintf("config %s is deprecated", result.Name)
|
|
|
|
if result.Replacement != "" {
|
|
|
|
msg = fmt.Sprintf("%s, use %s instead", msg, result.Replacement)
|
|
|
|
}
|
2020-06-30 15:55:40 -06:00
|
|
|
if err := s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
|
2019-09-11 11:13:44 -06:00
|
|
|
Type: protocol.Warning,
|
|
|
|
Message: msg,
|
2020-06-30 15:55:40 -06:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-09-11 11:13:44 -06:00
|
|
|
}
|
|
|
|
}
|
2019-07-27 12:59:14 -06:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-02-13 11:46:49 -07:00
|
|
|
// beginFileRequest checks preconditions for a file-oriented request and routes
|
|
|
|
// it to a snapshot.
|
|
|
|
// We don't want to return errors for benign conditions like wrong file type,
|
|
|
|
// so callers should do if !ok { return err } rather than if err != nil.
|
internal/lsp: read files eagerly
We use file identities pervasively throughout gopls. Prior to this
change, the identity is the modification date of an unopened file, or
the hash of an opened file. That means that opening a file changes its
identity, which causes unnecessary churn in the cache.
Unfortunately, there isn't an easy way to fix this. Changing the
cache key to something else, such as the modification time, means that
we won't unify cache entries if a change is made and then undone. The
approach here is to read files eagerly in GetFile, so that we know their
hashes immediately. That resolves the churn, but means that we do a ton
of file IO at startup.
Incidental changes:
Remove the FileSystem interface; there was only one implementation and
it added a fair amount of cruft. We have many other places that assume
os.Stat and such work.
Add direct accessors to FileHandle for URI, Kind, and Version. Most uses
of (FileHandle).Identity were for stuff that we derive solely from the
URI, and this helped me disentangle them. It is a *ton* of churn,
though. I can revert it if you want.
Change-Id: Ia2133bc527f71daf81c9d674951726a232ca5bc9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/237037
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-08 13:21:24 -06:00
|
|
|
func (s *Server) beginFileRequest(ctx context.Context, pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.FileHandle, bool, error) {
|
2020-02-13 11:46:49 -07:00
|
|
|
uri := pURI.SpanURI()
|
|
|
|
if !uri.IsFile() {
|
|
|
|
// Not a file URI. Stop processing the request, but don't return an error.
|
|
|
|
return nil, nil, false, nil
|
|
|
|
}
|
|
|
|
view, err := s.session.ViewOf(uri)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, false, err
|
|
|
|
}
|
|
|
|
snapshot := view.Snapshot()
|
internal/lsp: read files eagerly
We use file identities pervasively throughout gopls. Prior to this
change, the identity is the modification date of an unopened file, or
the hash of an opened file. That means that opening a file changes its
identity, which causes unnecessary churn in the cache.
Unfortunately, there isn't an easy way to fix this. Changing the
cache key to something else, such as the modification time, means that
we won't unify cache entries if a change is made and then undone. The
approach here is to read files eagerly in GetFile, so that we know their
hashes immediately. That resolves the churn, but means that we do a ton
of file IO at startup.
Incidental changes:
Remove the FileSystem interface; there was only one implementation and
it added a fair amount of cruft. We have many other places that assume
os.Stat and such work.
Add direct accessors to FileHandle for URI, Kind, and Version. Most uses
of (FileHandle).Identity were for stuff that we derive solely from the
URI, and this helped me disentangle them. It is a *ton* of churn,
though. I can revert it if you want.
Change-Id: Ia2133bc527f71daf81c9d674951726a232ca5bc9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/237037
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-08 13:21:24 -06:00
|
|
|
fh, err := snapshot.GetFile(ctx, uri)
|
2020-02-13 11:46:49 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, false, err
|
|
|
|
}
|
internal/lsp: read files eagerly
We use file identities pervasively throughout gopls. Prior to this
change, the identity is the modification date of an unopened file, or
the hash of an opened file. That means that opening a file changes its
identity, which causes unnecessary churn in the cache.
Unfortunately, there isn't an easy way to fix this. Changing the
cache key to something else, such as the modification time, means that
we won't unify cache entries if a change is made and then undone. The
approach here is to read files eagerly in GetFile, so that we know their
hashes immediately. That resolves the churn, but means that we do a ton
of file IO at startup.
Incidental changes:
Remove the FileSystem interface; there was only one implementation and
it added a fair amount of cruft. We have many other places that assume
os.Stat and such work.
Add direct accessors to FileHandle for URI, Kind, and Version. Most uses
of (FileHandle).Identity were for stuff that we derive solely from the
URI, and this helped me disentangle them. It is a *ton* of churn,
though. I can revert it if you want.
Change-Id: Ia2133bc527f71daf81c9d674951726a232ca5bc9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/237037
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-08 13:21:24 -06:00
|
|
|
if expectKind != source.UnknownKind && fh.Kind() != expectKind {
|
2020-02-13 11:46:49 -07:00
|
|
|
// Wrong kind of file. Nothing to do.
|
|
|
|
return nil, nil, false, nil
|
|
|
|
}
|
|
|
|
return snapshot, fh, true, nil
|
|
|
|
}
|
|
|
|
|
2019-04-17 13:37:20 -06:00
|
|
|
func (s *Server) shutdown(ctx context.Context) error {
|
2019-07-29 21:48:11 -06:00
|
|
|
s.stateMu.Lock()
|
|
|
|
defer s.stateMu.Unlock()
|
2019-07-27 12:59:14 -06:00
|
|
|
if s.state < serverInitialized {
|
2020-04-20 10:14:12 -06:00
|
|
|
event.Log(ctx, "server shutdown without initialization")
|
2019-04-17 13:37:20 -06:00
|
|
|
}
|
2020-02-21 15:31:45 -07:00
|
|
|
if s.state != serverShutDown {
|
|
|
|
// drop all the active views
|
|
|
|
s.session.Shutdown(ctx)
|
|
|
|
s.state = serverShutDown
|
|
|
|
}
|
2019-04-17 13:37:20 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) exit(ctx context.Context) error {
|
2019-07-29 21:48:11 -06:00
|
|
|
s.stateMu.Lock()
|
|
|
|
defer s.stateMu.Unlock()
|
2020-06-11 22:24:37 -06:00
|
|
|
|
|
|
|
// TODO: We need a better way to find the conn close method.
|
2020-04-30 13:59:37 -06:00
|
|
|
s.client.(io.Closer).Close()
|
2020-06-11 22:24:37 -06:00
|
|
|
|
2019-07-27 12:59:14 -06:00
|
|
|
if s.state != serverShutDown {
|
2020-06-11 22:24:37 -06:00
|
|
|
// TODO: We should be able to do better than this.
|
2020-04-30 13:59:37 -06:00
|
|
|
os.Exit(1)
|
2019-04-17 13:37:20 -06:00
|
|
|
}
|
2020-04-30 13:59:37 -06:00
|
|
|
// we don't terminate the process on a normal exit, we just allow it to
|
|
|
|
// close naturally if needed after the connection is closed.
|
2019-04-17 13:37:20 -06:00
|
|
|
return nil
|
|
|
|
}
|