Skip to content

Add workspace/didChangeWorkspaceFolders handler with graph-based project unloading#83090

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/add-unload-support-for-workspace-folders
Draft

Add workspace/didChangeWorkspaceFolders handler with graph-based project unloading#83090
Copilot wants to merge 3 commits intomainfrom
copilot/add-unload-support-for-workspace-folders

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

Roslyn LSP had no handler for workspace/didChangeWorkspaceFolders, so projects loaded from removed workspace roots were never unloaded. Critically, projects outside workspace folders that are still transitively referenced by in-scope projects must be retained.

Core algorithm (LanguageServerProjectLoader)

New UnloadProjectsNotReachableFromWorkspaceFoldersAsync performs a graph-based BFS from retained roots:

  1. Seed reachable set: tracked projects whose paths are under an active workspace folder (PathUtilities.IsChildPath)
  2. BFS expand: follow Project.ProjectReferences from each reachable project, retaining any transitively referenced tracked project
  3. Unload everything not in the reachable set via existing TryUnloadProject_NoLockAsync

Non-absolute paths are skipped (containment undefined). Empty workspace folder set → unload all.

Workspace folder state (LanguageServerProjectSystem)

  • _currentWorkspaceFolderPaths tracks the active set (guarded by _folderPathsLock)
  • SetInitialWorkspaceFolderPaths seeds state from initializeParams.WorkspaceFolders at startup (called from AutoLoadProjectsInitializer)
  • OnWorkspaceFoldersChangedAsync(added, removed, ct) applies deltas using PathUtilities.Comparer, then calls the unload algorithm

New LSP handler (DidChangeWorkspaceFoldersHandler)

Handles workspace/didChangeWorkspaceFolders, extracts file: URI folder paths, and delegates to LanguageServerProjectSystem.OnWorkspaceFoldersChangedAsync. Non-file URIs are skipped with a warning.

Capability advertisement (DefaultCapabilitiesProvider)

Sets workspace.workspaceFolders = { supported: true, changeNotifications: true } so clients know to send folder-change notifications.

Tests (WorkspaceFolderChangeTests)

Seven focused unit tests using a TestAccessor to inject fake Primordial project state without running design-time builds:

  • Project inside workspace folder → retained
  • Project outside workspace folder → unloaded
  • Project outside but directly referenced by in-scope project → retained
  • Transitively referenced chain (App → Core → Util, only App in folder) → all retained
  • Unreferenced external project → unloaded
  • Path prefix safety (/repo folder does not match /repo2/proj.csproj)
  • All workspace folders removed → all projects unloaded
Original prompt

Implement support in dotnet/roslyn for unloading Roslyn LSP projects when workspace/didChangeWorkspaceFolders removes the workspace-folder roots that keep those projects in scope.

Repository: dotnet/roslyn
Base branch: main

Context and current state:

  • The project loading/unloading logic lives in src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectLoader.cs.
  • LanguageServerProjectLoader already supports explicit unloads via TryUnloadProjectAsync(...) and UnloadAllProjectsAsync(), and it tracks loaded projects in _loadedProjects keyed by project path.
  • LanguageServerProjectSystem in src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs handles project loading through OpenProjectsAsync(...) and OpenSolutionAsync(...).
  • Roslyn’s LSP protocol layer already contains protocol types/constants for workspace folders, including DidChangeWorkspaceFoldersParams, WorkspaceFoldersChangeEvent, and Methods.WorkspaceDidChangeWorkspaceFoldersName.
  • There does not appear to be an existing Roslyn LSP handler wiring workspace/didChangeWorkspaceFolders to project unloading.

Important additional requirement:

  • Do NOT unload projects merely because their own project path is outside the current workspace folders if they are still referenced (transitively) by a project that remains in-scope.
  • In other words, when workspace folders change, retained projects should be:
    1. tracked projects whose project path is under one of the active workspace folders, plus
    2. any tracked projects transitively referenced by those retained root projects.
  • Only unload tracked projects that are outside the active workspace folders AND unreachable from any retained root project.

Implementation goals:

  1. Add support for handling workspace/didChangeWorkspaceFolders in the Roslyn LSP server.

    • Create an appropriate notification handler in the HostWorkspace area.
    • Route the notification to LanguageServerProjectSystem or another appropriate abstraction.
    • Follow existing Roslyn MEF/LSP handler conventions.
  2. Track workspace folders in the project system.

    • Add APIs/state to LanguageServerProjectSystem for setting/updating the active workspace folder paths.
    • Normalize file paths appropriately.
    • On initialization/startup, ensure the initial workspace folder state is available if needed.
  3. Extend LanguageServerProjectLoader with a graph-based unload operation.

    • Add an internal API with a name like UnloadProjectsNotReachableFromWorkspaceFoldersAsync(...).
    • Safely snapshot tracked projects under _gate.
    • Treat tracked project path as the node identity.
    • Determine retained root tracked projects by path containment under active workspace folders.
    • Build a reverse map from workspace ProjectId to tracked project path.
    • Use Roslyn workspace Project.ProjectReferences to compute the transitive closure from retained roots.
    • Be conservative around multi-targeting: if any backing ProjectId for a tracked path is reachable, retain that tracked path.
    • Be conservative around primordial projects / partially loaded states if needed to avoid unloading dependencies too aggressively while loads are in flight.
    • Use existing unload primitives (TryUnloadProject_NoLockAsync, LoadedProject.Dispose(), primordial project removal) rather than inventing new removal mechanics.
  4. Keep race behavior safe.

    • ReloadProjectAsync(...) already checks _loadedProjects before proceeding; preserve or extend this model so that folder-change-driven unloads do not leave inconsistent state.
    • Avoid holding _gate during expensive work unless already part of the surrounding design.
  5. Add logging where useful for diagnosis.

    • For example, log when workspace folders change and when projects are unloaded because they are no longer reachable from active workspace folders.
  6. Add or update tests in src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/.
    Please add focused tests covering at least:

    • A project inside the workspace folders remains loaded.
    • A project outside all workspace folders is unloaded after a workspace folder change.
    • A project outside all workspace folders but referenced by an in-scope project remains loaded.
    • Transitive references are retained.
    • A no-longer-referenced external project is unloaded.
    • Prefix/nesting path safety if relevant.

Helpful files:

  • src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectLoader.cs
  • src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs
  • src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenProjectsHandler.cs
  • src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs
  • `src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Host...

This pull request was created from Copilot chat.

…ect unloading

Agent-Logs-Url: https://github.com/dotnet/roslyn/sessions/b0d7a6c1-3998-4646-91f4-c1f637c574f5

Co-authored-by: JoeRobich <611219+JoeRobich@users.noreply.github.com>
…der disposal in tests

Agent-Logs-Url: https://github.com/dotnet/roslyn/sessions/b0d7a6c1-3998-4646-91f4-c1f637c574f5

Co-authored-by: JoeRobich <611219+JoeRobich@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for unloading Roslyn LSP projects on workspace folder removal Add workspace/didChangeWorkspaceFolders handler with graph-based project unloading Apr 8, 2026
Copilot AI requested a review from JoeRobich April 8, 2026 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants