From 72fe14e66ba67bf5cbea2aa1c5d89a2791b39a40 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 07:09:10 +0000 Subject: [PATCH] fix(lsp): never scan the filesystem root for an opened document's siblings Co-Authored-By: jason.han --- .../lsp-no-root-sibling-scan.fixed.md | 1 + internal/frontend/lsp/files.go | 8 +++++-- internal/frontend/lsp/files_test.go | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 changes/unreleased/lsp-no-root-sibling-scan.fixed.md diff --git a/changes/unreleased/lsp-no-root-sibling-scan.fixed.md b/changes/unreleased/lsp-no-root-sibling-scan.fixed.md new file mode 100644 index 000000000..b47c54950 --- /dev/null +++ b/changes/unreleased/lsp-no-root-sibling-scan.fixed.md @@ -0,0 +1 @@ +- The language server no longer scans the filesystem root for sibling files when a document at the root is opened outside every workspace folder; the walk could take minutes. diff --git a/internal/frontend/lsp/files.go b/internal/frontend/lsp/files.go index 8a468b338..dbd93ccdb 100644 --- a/internal/frontend/lsp/files.go +++ b/internal/frontend/lsp/files.go @@ -232,16 +232,20 @@ func (s *Server) DidChangeWorkspaceFolders(ctx context.Context, params *protocol // maxOpenedDirs bounds the directories the scan for an opened document's // siblings visits: the document's directory was not chosen as a workspace, and -// may be a home directory or the filesystem root. +// may be a home directory. const maxOpenedDirs = 2000 // indexOpenedDirectory indexes the directory of a document opened outside every -// folder, so sibling imports resolve; it is rescanned once all its documents close. +// folder, so sibling imports resolve; it is rescanned once all its documents +// close. The filesystem root is never indexed: walking it can take minutes. func (s *Server) indexOpenedDirectory(name string) { if !filepath.IsAbs(name) { return } dir := filepath.Dir(name) + if dir == filepath.VolumeName(name)+string(filepath.Separator) { + return + } s.mu.Lock() if underAnyFolder(name, s.folders) || s.openDirs[dir] { s.mu.Unlock() diff --git a/internal/frontend/lsp/files_test.go b/internal/frontend/lsp/files_test.go index fdc59b3bf..86b500ffd 100644 --- a/internal/frontend/lsp/files_test.go +++ b/internal/frontend/lsp/files_test.go @@ -709,6 +709,30 @@ func TestOpeningFileOutsideFoldersIndexesItsDirectory(t *testing.T) { } } +// A document at the filesystem root indexes no siblings: scanning it would +// walk the whole filesystem. +func TestOpeningFileAtFilesystemRootIndexesNothing(t *testing.T) { + s := NewServer(model.NewWorkspace()) + fc := &fakeClient{} + s.client = fc + ctx := context.Background() + if _, err := s.Initialize(ctx, &protocol.InitializeParams{}); err != nil { + t.Fatalf("Initialize err = %v", err) + } + if err := s.Initialized(ctx, &protocol.InitializedParams{}); err != nil { + t.Fatalf("Initialized err = %v", err) + } + + name := filepath.Join(string(filepath.Separator), "m.sysml") + openFile(t, s, name, "package M {\n part def A;\n}\n") + if len(s.openDirs) != 0 { + t.Errorf("openDirs = %v, want none", s.openDirs) + } + if msgs := diagnosticsFor(fc, name); len(msgs) != 0 { + t.Fatalf("diagnostics for %s = %v, want none", name, msgs) + } +} + // The directory scan an open document triggers never overwrites another open // buffer, whose text the editor owns. func TestOpeningFileOutsideFoldersKeepsOpenBuffers(t *testing.T) {