microsoft / microsoft/vscode-mypy
Template Sync: Add Jupyter notebook cell support via LSP 3.17 Notebook Document Sync
- Dominant language
- Python
- Stars
- 150
- Forks
- 39
- Avg merge
- 21h 45m
- Merged PRs (30d)
- 8
Description
### 🔄 Template Sync Required
Changes from the upstream [vscode-python-tools-extension-template](https://github.com/microsoft/vscode-python-tools-extension-template) have not yet been incorporated into this repository.
#### Source PR
- microsoft/vscode-python-tools-extension-template#265 — Add Jupyter notebook cell support via LSP 3.17 Notebook Document Sync
#### Summary
The template now ships with built-in support for linting Python cells inside Jupyter notebooks (`.ipynb`) and the VS Code Interactive Window via the LSP 3.17 Notebook Document Sync specification. This adds `NOTEBOOK_SYNC_OPTIONS` to the server initialization, a `_get_document_path` helper for resolving `vscode-notebook-cell:` URIs to the parent notebook's filesystem path, and four notebook lifecycle handlers (`notebookDocument/didOpen`, `/didChange`, `/didSave`, `/didClose`).
vscode-mypy currently **explicitly skips** notebook cells in `_linting_helper` with a "Not Supported" log warning. Adopting the template's pattern would enable mypy type checking in Jupyter notebook Python cells.
#### Files with missing changes
- **`bundled/tool/lsp_server.py`** — Missing the notebook infrastructure:
- `import urllib.parse` not present
- `NOTEBOOK_SYNC_OPTIONS` constant not present
- `LanguageServer` constructor does not include `notebook_document_sync`
- `_get_document_path` helper not present
- No `notebook_did_open`, `notebook_did_change`, `notebook_did_save`, or `notebook_did_close` handlers
- `_linting_helper` explicitly rejects `vscode-notebook-cell:` URIs (lines 213–216) rather than handling them
#### Suggested fix
Below are the recommended additions to `bundled/tool/lsp_server.py`. Mypy-specific logic (e.g., `reportingScope`, daemon handling) should be preserved inside each new handler, mirroring how `_linting_helper` works for regular text documents.
**1. Add the `urllib.parse` import** (near the top with other stdlib imports):
````diff
+import urllib.parse
````
**2. Add `NOTEBOOK_SYNC_OPTIONS`** (before or after `MAX_WORKERS`):
````diff
+import lsprotocol.types as lsp
+
+NOTEBOOK_SYNC_OPTIONS = lsp.NotebookDocumentSyncOptions(
+ notebook_selector=[
+ lsp.NotebookDocumentFilterWithNotebook(
+ notebook="jupyter-notebook",
+ cells=[lsp.NotebookCellLanguage(language="python")],
+ ),
+ lsp.NotebookDocumentFilterWithNotebook(
+ notebook="interactive",
+ cells=[lsp.NotebookCellLanguage(language="python")],
+ ),
+ ],
+ save=True,
+)
````
**3. Add `notebook_document_sync` to the `LanguageServer` constructor**:
````diff
-LSP_SERVER = LanguageServer(name="Mypy", version="v0.1.0", max_workers=MAX_WORKERS)
+LSP_SERVER = LanguageServer(
+ name="Mypy",
+ version="v0.1.0",
+ max_workers=MAX_WORKERS,
+ notebook_document_sync=NOTEBOOK_SYNC_OPTIONS,
+)
````
**4. Add the `_get_document_path` helper** (near other document helpers):
````diff
+def _get_document_path(cell_uri: str) -> str:
+ """Resolve a vscode-notebook-cell: URI to the parent notebook's filesystem path."""
+ parsed = urllib.parse.urlparse(cell_uri)
+ if parsed.scheme == "vscode-notebook-cell":
+ nb_uri = parsed._replace(scheme="file", fragment="").geturl()
+ return LSP_SERVER.workspace.get_text_document(nb_uri).path
+ return LSP_SERVER.workspace.get_text_document(cell_uri).path
````
**5. Add notebook lifecycle handlers** (alongside the existing `did_open`, `did_save`, `did_close` handlers):
````diff
+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_OPEN)
+def notebook_did_open(params: lsp.DidOpenNotebookDocumentParams) -> None:
+ """LSP handler for notebookDocument/didOpen request."""
+ nb = LSP_SERVER.workspace.get_notebook_document(
+ notebook_uri=params.notebook_document.uri
+ )
+ if nb is None:
+ return
+ for cell in nb.cells:
+ if cell.kind == lsp.NotebookCellKind.Code:
+ doc = LSP_SERVER.workspace.get_text_document(cell.document)
+ _linting_helper(doc)
+
+
+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_SAVE)
+def notebook_did_save(params: lsp.DidSaveNotebookDocumentParams) -> None:
+ """LSP handler for notebookDocument/didSave request."""
+ nb = LSP_SERVER.workspace.get_notebook_document(
+ notebook_uri=params.notebook_document.uri
+ )
+ if nb is None:
+ return
+ for cell in nb.cells:
+ if cell.kind == lsp.NotebookCellKind.Code:
+ doc = LSP_SERVER.workspace.get_text_document(cell.document)
+ _linting_helper(doc)
+
+
+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_CLOSE)
+def notebook_did_close(params: lsp.DidCloseNotebookDocumentParams) -> None:
+ """LSP handler for notebookDocument/didClose request."""
+ for cell_doc in params.cell_text_documents:
+ doc = LSP_SERVER.workspace.get_text_document(cell_doc.uri)
+ _clear_diagnostics(doc)
+
+
+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_CHANGE)
+def notebook_did_change(params: lsp.DidChangeNotebookDocumentParams) -> None:
+ """LSP handler for notebookDocument/didChange request."""
+ if params.change.cells is None:
+ return
+ cell_changes = params.change.cells
+ if cell_changes.text_content:
+ for changed in cell_changes.text_content:
+ doc = LSP_SERVER.workspace.get_text_document(changed.document.uri)
+ _linting_helper(doc)
+ if cell_changes.structure:
+ for removed in (cell_changes.structure.did_close or []):
+ doc = LSP_SERVER.workspace.get_text_document(removed.uri)
+ _clear_diagnostics(doc)
````
**6. Remove the "Not Supported" skip** from `_linting_helper` (lines 213–216):
````diff
- if str(document.uri).startswith("vscode-notebook-cell"):
- # We don't support running mypy on notebook cells.
- log_warning(f"Skipping notebook cells [Not Supported]: {str(document.uri)}")
- _clear_diagnostics(document)
- return None
-
````
> **Note:** The mypy tool runs against a file path. For notebook cells, `_get_document_path` resolves the cell URI to the parent notebook's `.ipynb` path. You may want to verify how mypy behaves when given a `.ipynb` file path and adjust the implementation accordingly.
#### Files skipped
- `README.md` — Template-specific documentation; vscode-mypy has its own README.
- `src/test/python_tests/test_notebook.py` — New test file added by the template. vscode-mypy should add its own notebook integration tests covering mypy-specific behavior.
---
🤖 This issue was auto-generated by the [`extension-template-sync`](.github/workflows/extension-template-sync.md) workflow.
> Generated by [Extension Template Sync](https://github.com/microsoft/vscode-mypy/actions/runs/22928519858)
Contributor guide
Research direction
Start in bundled/tool/lsp_server.py, compare its existing document handlers and _linting_helper with upstream PR #265, and inspect the skipped notebook test path src/test/python_tests/test_notebook.py for relevant coverage. Verify notebook lifecycle handling, parent-path resolution, and mypy-specific behavior for Python cells; done means notebook cells are linted without the existing Not Supported skip and integration tests cover that behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- jupyter-notebook, python, vscode
- Domain
- devtools, testing-qa
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100