internal/lsp: fix the logic to avoid duplicate file watching
The logic that checked if a file was already being watched by the
default glob pattern was incorrect. Fix it, and use the newly added
InDir function.
Change-Id: Ia7e3851ab5b9fa1fa7590cae3b370676201a9141
Reviewed-on: https://go-review.googlesource.com/c/tools/+/267119
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index d916de3..15b9855 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -694,7 +694,7 @@
func (s *snapshot) GoModForFile(ctx context.Context, uri span.URI) span.URI {
var match span.URI
for modURI := range s.workspace.activeModFiles() {
- if !inDir(dirURI(modURI).Filename(), uri.Filename()) {
+ if !source.InDir(dirURI(modURI).Filename(), uri.Filename()) {
continue
}
if len(modURI) > len(match) {
@@ -1377,7 +1377,7 @@
}
// If a go.mod in the workspace has been changed, invalidate metadata.
if kind := originalFH.Kind(); kind == source.Mod {
- return inDir(filepath.Dir(s.view.rootURI.Filename()), filepath.Dir(originalFH.URI().Filename()))
+ return source.InDir(filepath.Dir(s.view.rootURI.Filename()), filepath.Dir(originalFH.URI().Filename()))
}
// Get the original and current parsed files in order to check package name
// and imports. Use the new snapshot to parse to avoid modifying the
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index f27d8ca..598908a 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -698,87 +698,13 @@
// The user may have a multiple directories in their GOPATH.
// Check if the workspace is within any of them.
for _, gp := range filepath.SplitList(ws.gopath) {
- if inDir(filepath.Join(gp, "src"), folder.Filename()) {
+ if source.InDir(filepath.Join(gp, "src"), folder.Filename()) {
return true
}
}
return false
}
-// Copied and slightly adjusted from go/src/cmd/go/internal/search/search.go.
-//
-// inDir checks whether path is in the file tree rooted at dir.
-// If so, InDir returns an equivalent path relative to dir.
-// If not, InDir returns an empty string.
-// InDir makes some effort to succeed even in the presence of symbolic links.
-func inDir(dir, path string) bool {
- if rel := inDirLex(path, dir); rel != "" {
- return true
- }
- xpath, err := filepath.EvalSymlinks(path)
- if err != nil || xpath == path {
- xpath = ""
- } else {
- if rel := inDirLex(xpath, dir); rel != "" {
- return true
- }
- }
-
- xdir, err := filepath.EvalSymlinks(dir)
- if err == nil && xdir != dir {
- if rel := inDirLex(path, xdir); rel != "" {
- return true
- }
- if xpath != "" {
- if rel := inDirLex(xpath, xdir); rel != "" {
- return true
- }
- }
- }
- return false
-}
-
-// Copied from go/src/cmd/go/internal/search/search.go.
-//
-// inDirLex is like inDir but only checks the lexical form of the file names.
-// It does not consider symbolic links.
-// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
-// return the suffix. Most uses of str.HasFilePathPrefix should probably
-// be calling InDir instead.
-func inDirLex(path, dir string) string {
- pv := strings.ToUpper(filepath.VolumeName(path))
- dv := strings.ToUpper(filepath.VolumeName(dir))
- path = path[len(pv):]
- dir = dir[len(dv):]
- switch {
- default:
- return ""
- case pv != dv:
- return ""
- case len(path) == len(dir):
- if path == dir {
- return "."
- }
- return ""
- case dir == "":
- return path
- case len(path) > len(dir):
- if dir[len(dir)-1] == filepath.Separator {
- if path[:len(dir)] == dir {
- return path[len(dir):]
- }
- return ""
- }
- if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
- if len(path) == len(dir)+1 {
- return "."
- }
- return path[len(dir)+1:]
- }
- return ""
- }
-}
-
// getGoEnv gets the view's various GO* values.
func (s *Session) getGoEnv(ctx context.Context, folder string, configEnv []string) (environmentVariables, map[string]string, error) {
envVars := environmentVariables{}
diff --git a/internal/lsp/cache/workspace.go b/internal/lsp/cache/workspace.go
index 673843a..4d05adc 100644
--- a/internal/lsp/cache/workspace.go
+++ b/internal/lsp/cache/workspace.go
@@ -258,7 +258,7 @@
// Legacy mode only considers a module a workspace root.
continue
}
- if !inDir(wm.root.Filename(), uri.Filename()) {
+ if !source.InDir(wm.root.Filename(), uri.Filename()) {
// Otherwise, the module must be contained within the workspace root.
continue
}
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index 0dd0ad2..1c2a8bd 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -348,13 +348,20 @@
}}
for dir := range dirs {
filename := dir.Filename()
+
// If the directory is within a workspace folder, we're already
// watching it via the relative path above.
+ var matched bool
for _, view := range s.session.Views() {
- if isSubdirectory(view.Folder().Filename(), filename) {
- continue
+ if source.InDir(view.Folder().Filename(), filename) {
+ matched = true
+ break
}
}
+ if matched {
+ continue
+ }
+
// If microsoft/vscode#100870 is resolved before
// microsoft/vscode#104387, we will need a work-around for Windows
// drive letter casing.
diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go
index 4e01099..b3d87f9 100644
--- a/internal/lsp/source/util.go
+++ b/internal/lsp/source/util.go
@@ -441,3 +441,77 @@
}
return true
}
+
+// InDir checks whether path is in the file tree rooted at dir.
+// If so, InDir returns an equivalent path relative to dir.
+// If not, InDir returns an empty string.
+// InDir makes some effort to succeed even in the presence of symbolic links.
+//
+// Copied and slightly adjusted from go/src/cmd/go/internal/search/search.go.
+func InDir(dir, path string) bool {
+ if rel := inDirLex(path, dir); rel != "" {
+ return true
+ }
+ xpath, err := filepath.EvalSymlinks(path)
+ if err != nil || xpath == path {
+ xpath = ""
+ } else {
+ if rel := inDirLex(xpath, dir); rel != "" {
+ return true
+ }
+ }
+
+ xdir, err := filepath.EvalSymlinks(dir)
+ if err == nil && xdir != dir {
+ if rel := inDirLex(path, xdir); rel != "" {
+ return true
+ }
+ if xpath != "" {
+ if rel := inDirLex(xpath, xdir); rel != "" {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// Copied from go/src/cmd/go/internal/search/search.go.
+//
+// inDirLex is like inDir but only checks the lexical form of the file names.
+// It does not consider symbolic links.
+// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
+// return the suffix. Most uses of str.HasFilePathPrefix should probably
+// be calling InDir instead.
+func inDirLex(path, dir string) string {
+ pv := strings.ToUpper(filepath.VolumeName(path))
+ dv := strings.ToUpper(filepath.VolumeName(dir))
+ path = path[len(pv):]
+ dir = dir[len(dv):]
+ switch {
+ default:
+ return ""
+ case pv != dv:
+ return ""
+ case len(path) == len(dir):
+ if path == dir {
+ return "."
+ }
+ return ""
+ case dir == "":
+ return path
+ case len(path) > len(dir):
+ if dir[len(dir)-1] == filepath.Separator {
+ if path[:len(dir)] == dir {
+ return path[len(dir):]
+ }
+ return ""
+ }
+ if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
+ if len(path) == len(dir)+1 {
+ return "."
+ }
+ return path[len(dir)+1:]
+ }
+ return ""
+ }
+}