internal/lsp: lowercase drive letters on Windows to fix file watching
This is a work-around for
https://github.com/microsoft/vscode/issues/104387. We now always
lowercase the drive letter on Windows.
This CL also fixes a bug introduced by CL 245327, which caused URIs
to be used instead of paths in the GlobPattern.
We really need VS Code integration tests for this
(golang/vscode-go#404).
Updates golang/go#40661
Change-Id: I21be6d929288cfe41168cea34001fc2f41ac6c8b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/247684
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index ee2adda..6661eb1 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -11,6 +11,8 @@
"io"
"os"
"path"
+ "path/filepath"
+ "strings"
"sync"
"golang.org/x/tools/internal/event"
@@ -46,6 +48,10 @@
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 @@
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 @@
}
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 @@
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 @@
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 cfcc72e..20568c9 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -64,6 +64,9 @@
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 78e71fe..2504921 100644
--- a/internal/span/uri.go
+++ b/internal/span/uri.go
@@ -160,7 +160,7 @@
// 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