Skip to content

fix: LSP workspace root detection and absolute URI generation#8938

Draft
daizutabi wants to merge 3 commits intomarimo-team:mainfrom
daizutabi:fix-pyright-options
Draft

fix: LSP workspace root detection and absolute URI generation#8938
daizutabi wants to merge 3 commits intomarimo-team:mainfrom
daizutabi:fix-pyright-options

Conversation

@daizutabi
Copy link
Copy Markdown
Contributor

📝 Summary

This PR addresses Issue #8662, where basedpyright failed to apply project-level configurations (e.g., pyproject.toml).

The root cause was two-fold:

  1. Relative Document URIs: marimo was providing document URIs using relative paths (e.g., file://notebook.py).
  2. Missing Absolute Workspace Root: marimo was not providing a valid absolute path for the rootUri or workspaceFolders.

Without an absolute workspace root, LSP servers cannot determine the project's boundaries. Consequently, they fail to discover and load configuration files like pyproject.toml or pyrightconfig.json that reside in the root directory, falling back to default global settings instead.

Closes #8662

Changes

  • getWorkspaceRoot() utility: Introduced a new function in frontend/src/core/codemirror/lsp/utils.ts that calculates the absolute workspace root.
  • Absolute Path Support: Updated getLSPDocument() and getLSPDocumentRootUri() to return proper absolute file:/// URIs.
  • Explicit Workspace Configuration: Updated LSP initialization (specifically for basedpyright) to use these absolute URIs and explicitly define workspaceFolders, ensuring the server correctly identifies the project root and applies local configurations.

Verification (Screenshots)

Verified using a root pyproject.toml with:

[tool.basedpyright]
reportUnusedCallResult = false

Before

before

The project configuration is ignored because the workspace root was not specified. basedpyright incorrectly reports a warning on line 4 (func()) despite it being disabled in pyproject.toml.

After

after

The configuration is now correctly applied. The warning on line 4 is suppressed, while a legitimate type error (line 2) is still reported.

📋 Pre-Review Checklist

  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Mar 31, 2026 2:08pm

Request Review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes LSP initialization for basedpyright by ensuring the client provides absolute file: URIs and a meaningful workspace root so the server can discover project configuration (e.g., pyproject.toml) as intended.

Changes:

  • Added workspace-root derivation in LSP utilities (using cwd + workspace-relative filename).
  • Updated LSP document/root URI generation to prefer absolute paths when possible.
  • Configured basedpyright initialization to set rootUri and workspaceFolders explicitly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
frontend/src/core/codemirror/lsp/utils.ts Adds workspace root detection and updates document/root URI generation to prefer absolute paths.
frontend/src/core/codemirror/lsp/tests/utils.test.ts Adds unit tests covering URI/root behavior across Unix/Windows and fallback cases.
frontend/src/core/codemirror/language/languages/python.ts Updates basedpyright client initialization to pass rootUri and workspaceFolders.

Comment on lines 30 to +36
export function getLSPDocument() {
return `file://${getFilenameFromDOM() ?? "/__marimo_notebook__.py"}`;
const root = getWorkspaceRoot();
const filename = getFilenameFromDOM();

if (root && filename) {
return `file://${PathBuilder.guessDeliminator(root).join(root, filename)}`;
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

getLSPDocument()/getLSPDocumentRootUri() are building file URIs by simple string concatenation with OS paths. This produces invalid file: URIs on Windows (backslashes + missing file:///C:/... form) and also fails to percent-encode characters like spaces or #, which can break URI parsing in LSP servers (e.g., pyright/basedpyright). Consider introducing a small toFileUri(absolutePath: string) helper that normalizes separators to /, handles Windows drive letters, and URL-encodes path segments, and use it for both the document URI and root URI.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +27
function getWorkspaceRoot() {
const cwd = store.get(cwdAtom);
const filename = getFilenameFromDOM();

if (!cwd || !filename) {
return null;
}

// Split the relative path from the workspace root
const numDirs = filename.split(/[/\\]/).filter(Boolean).length - 1;

let root = cwd;
for (let i = 0; i < numDirs; i++) {
root = Paths.dirname(root);
}
return root;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

getWorkspaceRoot() assumes filename is a workspace-relative path. When filename is absolute (non-directory mode), numDirs becomes very large and the loop walks cwd up to ""; the function then silently falls back to other branches. It would be safer (and cheaper) to bail out early when Paths.isAbsolute(filename) is true, and only attempt this calculation for workspace-relative display paths.

Copilot uses AI. Check for mistakes.
Comment on lines +227 to 232
const rootUri = getLSPDocumentRootUri();
const lspClientOpts = {
transport,
rootUri: getLSPDocumentRootUri(),
workspaceFolders: [],
rootUri,
workspaceFolders: [{ uri: rootUri, name: "marimo" }],
};
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

workspaceFolders is now always set for basedpyright using rootUri. In fallback cases getLSPDocumentRootUri() can return values like file:/// (no filename) or file://. (no cwd), which can cause basedpyright to treat the filesystem root/current directory as the workspace and potentially trigger very expensive scans or even fail URI parsing. Recommend only setting workspaceFolders when you have a real absolute project root (e.g., derived from directory-mode workspace root), otherwise keep it []/null and rely on the per-document URI.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@daizutabi daizutabi marked this pull request as draft March 31, 2026 14:07
@mscolnick mscolnick added the bug Something isn't working label Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Marimo does not apply Basedpyright configurations in ./pyproject.toml

3 participants