From fadf93ffb22c3815fa9d5daace75e62a8035b519 Mon Sep 17 00:00:00 2001 From: Rebecca Stambler Date: Sat, 20 Jun 2020 16:11:01 -0400 Subject: [PATCH] internal/lsp: watch all files in the module and replace target Previously, our file watching only considered the root directory of the view, which may not include the entire module or its replaced dependencies. Now we expand our watching to include the whole module. As part of testing this, I noticed that VS Code's file watcher actually only sends updates for files in the workspace, even if we request notifications for all files in the module. I filed an issue to ask about this: https://github.com/microsoft/vscode-languageserver-node/issues/641. Change-Id: I9499d31aff273f69e9c117511e7985ff58b7fdc4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/239198 Run-TryBot: Rebecca Stambler TryBot-Result: Gobot Gobot Reviewed-by: Heschi Kreinick --- internal/lsp/cache/view.go | 30 +++++++++++++++++++++++++ internal/lsp/general.go | 44 +++++++++++++++++++++---------------- internal/lsp/source/view.go | 4 ++++ 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go index f78dbcdb6e..ee79f742dd 100644 --- a/internal/lsp/cache/view.go +++ b/internal/lsp/cache/view.go @@ -519,6 +519,36 @@ func basename(filename string) string { return strings.ToLower(filepath.Base(filename)) } +func (v *View) WorkspaceDirectories(ctx context.Context) ([]string, error) { + // If the view does not have a go.mod file, only the root directory + // is known. In GOPATH mode, we should really watch the entire GOPATH, + // but that's probably too expensive. + // TODO(rstambler): Figure out a better approach in the future. + if v.modURI == "" { + return []string{v.folder.Filename()}, nil + } + // Anything inside of the module root is known. + dirs := []string{filepath.Dir(v.modURI.Filename())} + + // Keep track of any directories mentioned in replace targets. + fh, err := v.session.GetFile(ctx, v.modURI) + if err != nil { + return nil, err + } + pmh, err := v.Snapshot().ParseModHandle(ctx, fh) + if err != nil { + return nil, err + } + parsed, _, _, err := pmh.Parse(ctx) + if err != nil { + return nil, err + } + for _, replace := range parsed.Replace { + dirs = append(dirs, replace.New.Path) + } + return dirs, nil +} + func (v *View) relevantChange(c source.FileModification) bool { // If the file is known to the view, the change is relevant. known := v.knownFile(c.URI) diff --git a/internal/lsp/general.go b/internal/lsp/general.go index c2051ce0ab..16cee522a2 100644 --- a/internal/lsp/general.go +++ b/internal/lsp/general.go @@ -152,25 +152,6 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa ) } - if options.DynamicWatchedFilesSupported { - registrations = append(registrations, protocol.Registration{ - ID: "workspace/didChangeWatchedFiles", - Method: "workspace/didChangeWatchedFiles", - RegisterOptions: protocol.DidChangeWatchedFilesRegistrationOptions{ - Watchers: []protocol.FileSystemWatcher{{ - GlobPattern: "**/*.go", - Kind: float64(protocol.WatchChange + protocol.WatchDelete + protocol.WatchCreate), - }}, - }, - }) - } - - if len(registrations) > 0 { - s.client.RegisterCapability(ctx, &protocol.RegistrationParams{ - Registrations: registrations, - }) - } - // TODO: this event logging may be unnecessary. The version info is included in the initialize response. buf := &bytes.Buffer{} debug.PrintVersionInfo(ctx, buf, true, debug.PlainText) @@ -179,6 +160,31 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa s.addFolders(ctx, s.pendingFolders) s.pendingFolders = nil + 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, + }) + } + } return nil } diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go index 1854bc8bd4..89bed4b207 100644 --- a/internal/lsp/source/view.go +++ b/internal/lsp/source/view.go @@ -177,6 +177,10 @@ type View interface { // IgnoredFile reports if a file would be ignored by a `go list` of the whole // workspace. IgnoredFile(uri span.URI) bool + + // WorkspaceDirectories returns any directory known by the view. For views + // within a module, this is the module root and any replace targets. + WorkspaceDirectories(ctx context.Context) ([]string, error) } type BuiltinPackage interface {