diff --git a/internal/lsp/general.go b/internal/lsp/general.go index ee2adda695..6661eb1bc0 100644 --- a/internal/lsp/general.go +++ b/internal/lsp/general.go @@ -11,6 +11,8 @@ import ( "io" "os" "path" + "path/filepath" + "strings" "sync" "golang.org/x/tools/internal/event" @@ -46,6 +48,10 @@ func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitializ if params.RootURI != "" && !params.RootURI.SpanURI().IsFile() { return nil, fmt.Errorf("unsupported URI scheme: %v (gopls only supports file URIs)", params.RootURI) } + if params.RootURI != "" { + s.rootURI = params.RootURI.SpanURI() + } + for _, folder := range params.WorkspaceFolders { uri := span.URIFromURI(folder.URI) if !uri.IsFile() { @@ -144,20 +150,6 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa options := s.session.Options() defer func() { s.session.SetOptions(options) }() - var registrations []protocol.Registration - if options.ConfigurationSupported && options.DynamicConfigurationSupported { - registrations = append(registrations, - protocol.Registration{ - ID: "workspace/didChangeConfiguration", - Method: "workspace/didChangeConfiguration", - }, - protocol.Registration{ - ID: "workspace/didChangeWorkspaceFolders", - Method: "workspace/didChangeWorkspaceFolders", - }, - ) - } - // TODO: this event logging may be unnecessary. // The version info is included in the initialize response. buf := &bytes.Buffer{} @@ -169,9 +161,18 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa } s.pendingFolders = nil - if len(registrations) > 0 { + if options.ConfigurationSupported && options.DynamicConfigurationSupported { if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{ - Registrations: registrations, + Registrations: []protocol.Registration{ + { + ID: "workspace/didChangeConfiguration", + Method: "workspace/didChangeConfiguration", + }, + { + ID: "workspace/didChangeWorkspaceFolders", + Method: "workspace/didChangeWorkspaceFolders", + }, + }, }); err != nil { return err } @@ -322,10 +323,25 @@ func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, dirs map[ for k := range s.watchedDirectories { delete(s.watchedDirectories, k) } - var watchers []protocol.FileSystemWatcher + // Work-around microsoft/vscode#100870 by making sure that we are, + // at least, watching the user's entire workspace. This will still be + // applied to every folder in the workspace. + watchers := []protocol.FileSystemWatcher{{ + GlobPattern: "**/*.{go,mod,sum}", + Kind: float64(protocol.WatchChange + protocol.WatchDelete + protocol.WatchCreate), + }} for dir := range dirs { + filename := dir.Filename() + // If the directory is within the root URI, we're already watching it + // via the relative path above. + if isSubdirectory(s.rootURI.Filename(), filename) { + continue + } + // If microsoft/vscode#100870 is resolved before + // microsoft/vscode#104387, we will need a work-around for Windows + // drive letter casing. watchers = append(watchers, protocol.FileSystemWatcher{ - GlobPattern: fmt.Sprintf("%s/**/*.{go,mod,sum}", dir), + GlobPattern: fmt.Sprintf("%s/**/*.{go,mod,sum}", filename), Kind: float64(protocol.WatchChange + protocol.WatchDelete + protocol.WatchCreate), }) } @@ -348,6 +364,11 @@ func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, dirs map[ return nil } +func isSubdirectory(root, leaf string) bool { + rel, err := filepath.Rel(root, leaf) + return err == nil && !strings.HasPrefix(rel, "..") +} + func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI, o *source.Options) error { if !s.session.Options().ConfigurationSupported { return nil diff --git a/internal/lsp/server.go b/internal/lsp/server.go index cfcc72ea90..20568c9d80 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -64,6 +64,9 @@ type Server struct { session source.Session + // rootURI is the root of the workspace opened in the editor (if any). + rootURI span.URI + // changedFiles tracks files for which there has been a textDocument/didChange. changedFilesMu sync.Mutex changedFiles map[span.URI]struct{} diff --git a/internal/span/uri.go b/internal/span/uri.go index 78e71fe451..2504921356 100644 --- a/internal/span/uri.go +++ b/internal/span/uri.go @@ -160,7 +160,7 @@ func isWindowsDrivePath(path string) bool { // isWindowsDriveURI returns true if the file URI is of the format used by // Windows URIs. The url.Parse package does not specially handle Windows paths -// (see golang/go#6027). We check if the URI path has a drive prefix (e.g. "/C:"). +// (see golang/go#6027), so we check if the URI path has a drive prefix (e.g. "/C:"). func isWindowsDriveURIPath(uri string) bool { if len(uri) < 4 { return false