Skip to content

languages: Fix Python LSP workspace folder detection in uv workspaces#53781

Merged
osiewicz merged 3 commits intozed-industries:mainfrom
space192:fix/python-workspace-manifest-detection
Apr 20, 2026
Merged

languages: Fix Python LSP workspace folder detection in uv workspaces#53781
osiewicz merged 3 commits intozed-industries:mainfrom
space192:fix/python-workspace-manifest-detection

Conversation

@space192
Copy link
Copy Markdown
Contributor

@space192 space192 commented Apr 13, 2026

Closes #47926

Summary

When using a uv (or Poetry/PDM) workspace with multiple subprojects, Python LSP servers (Pyright, Ruff, ty, etc.) are initialized with the subproject directory as their workspace folder instead of the workspace root. This happens because PyprojectTomlManifestProvider::search() returns the first (innermost)
pyproject.toml found walking up the directory tree.

For example, in a uv workspace like:

main-project/
├── pyproject.toml # workspace root with [tool.uv.workspace]
├── uv.lock
├── packages/
│ └── project-api/
│ └── pyproject.toml # subpackage
└── projects/
└── project-a/
└── pyproject.toml # subpackage

Opening a file in packages/project-api/ would register packages/project-api/ as the LSP workspace folder instead of main-project/.

Approach

The fix uses lockfile existence as a heuristic to detect workspace roots. The updated search() method walks all ancestors (similar to CargoManifestProvider) and:

  • Tracks the innermost pyproject.toml as a fallback
  • Tracks the outermost pyproject.toml that has a sibling lockfile (uv.lock, poetry.lock, pdm.lock, or Pipfile.lock)
  • Returns the outermost workspace root if found, otherwise falls back to the innermost

This works within the existing ManifestDelegate interface (existence checks only, no file content reading).

Scenario Result
uv workspace (root pyproject.toml + uv.lock) Returns workspace root
Poetry workspace (root pyproject.toml + poetry.lock) Returns workspace root
Simple project (single pyproject.toml, no lockfile) Returns project dir (unchanged)
Independent subprojects (no lockfile at any level) Returns each project's own dir (unchanged)

Since the manifest provider is set at the Python language level, this fix applies to all Python LSP servers (Pyright, Ruff, ty, etc.).

Test plan

  • Added unit tests for PyprojectTomlManifestProvider covering all scenarios above
  • Existing integration test test_running_multiple_instances_of_a_single_server_in_one_worktree passes (independent subprojects without lockfiles)
  • cargo check -p languages compiles cleanly
  • Manual testing with a real uv workspace (Pyright and Ruff both receive correct workspace root)

Release Notes:

  • Fixed Python LSP servers (Pyright, Ruff, etc.) using the wrong workspace folder in uv/Poetry/PDM workspaces with multiple subprojects.
@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Apr 13, 2026
@zed-codeowner-coordinator zed-codeowner-coordinator Bot requested review from a team, rtfeldman and smitbarmase and removed request for a team April 13, 2026 08:40
@zed-community-bot zed-community-bot Bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Apr 13, 2026
@dinocosta dinocosta added the area:languages/python Python programming language support label Apr 13, 2026
@MrSubidubi MrSubidubi requested review from MrSubidubi and osiewicz and removed request for MrSubidubi April 17, 2026 10:43
Copy link
Copy Markdown
Member

@osiewicz osiewicz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me. Thanks!

@osiewicz osiewicz merged commit 03414d8 into zed-industries:main Apr 20, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:languages/python Python programming language support cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions

5 participants