Skip to content

Add lint diagnostics API to CodeMirror#5985

Draft
Jepson2k wants to merge 2 commits intozauberzeug:mainfrom
Jepson2k:cm-diagnostics
Draft

Add lint diagnostics API to CodeMirror#5985
Jepson2k wants to merge 2 commits intozauberzeug:mainfrom
Jepson2k:cm-diagnostics

Conversation

@Jepson2k
Copy link
Copy Markdown

Motivation

Embedding ui.codemirror as an in-app code editor often needs to surface programmatically computed diagnostics — type-check results, lint output, runtime errors mapped back to source — onto specific lines, with hover tooltips so the user can read the message without leaving the editor. CodeMirror 6's @codemirror/lint package provides exactly this, but it isn't bundled in the current dist and there's no Python API for it. This PR adds both.

This is one of several small slices being carved out of #5965 per the splitting discussion on that thread.

Implementation

Python (nicegui/elements/codemirror/codemirror.py):

  • Diagnostic TypedDict(total=False) with required line (1-indexed) and message, optional severity ('error' | 'warning' | 'info' | 'hint', default 'error') and source (label shown next to the message).
  • set_diagnostics(list[Diagnostic]) and clear_diagnostics() methods. Both run a JS setDiagnostics method via run_method.

JavaScript (nicegui/elements/codemirror/codemirror.js):

  • New setDiagnostics method maps each Diagnostic to a CM6 diagnostic spanning the affected line, then dispatches CM.setDiagnostics(state, ...).
  • The editor's extensions now include CM.linter(() => []). The empty source disables auto-linting; the linter() extension just installs lintState so setDiagnostics() works and inline error marks render. We do NOT use CM.lintGutter(): it pulls in lintGutterTooltip, a StateField that registers itself via showTooltip.from(field) and returns null on most transactions. That null provider sits in the showTooltip facet and silently suppresses the autocomplete popup outside of paren contexts. CM.linter()'s only tooltip is a hoverTooltip that fires on mouseover, not on every keystroke.

Bundle (nicegui/elements/codemirror/src/index.mjs, nicegui/elements/codemirror/dist/):

  • Re-export @codemirror/lint from src/index.mjs so linter, setDiagnostics, and lintState end up in the dist bundle. @codemirror/lint was already a transitive dep of the codemirror meta-package, so package.json doesn't change.
  • Bundle rebuilt via npm run clean && npm run build.

Tests (tests/test_codemirror.py):

  • test_set_and_clear_diagnostics calls set_diagnostics with two entries (different severities, different lines) and asserts two .cm-lintRange elements appear in the DOM, then calls clear_diagnostics and asserts they're gone.

Documentation (website/documentation/content/codemirror_documentation.py):

  • Small demo with a Python snippet that sets a single error diagnostic on a line and a button to clear it.

Progress

  • The PR title is a short phrase starting with a verb like "Add ...", "Fix ...", "Update ...", "Remove ...", etc.
  • The implementation is complete. (Otherwise, open a draft PR.)
  • This PR does not address a security issue. (Security fixes must be coordinated via the security advisory process before opening a PR.)
  • Pytests have been added.
  • Documentation has been added.
  • No breaking changes to the public API.

Jepson2k and others added 2 commits April 23, 2026 18:27
`set_diagnostics(list[Diagnostic])` and `clear_diagnostics()` render
inline error/warning marks with hover tooltips on user-supplied lines,
backed by CodeMirror's built-in lint system. Each `Diagnostic` is a
small TypedDict with required `line` (1-indexed) and `message`, plus
optional `severity` (`'error'` | `'warning'` | `'info'` | `'hint'`,
default `'error'`) and `source` (label shown next to the message).

The editor installs `CM.linter(() => [])` so that `setDiagnostics()`
works and inline error marks render. **Important:** we deliberately
do NOT use `CM.lintGutter()` — it pulls in `lintGutterTooltip`, a
StateField that registers itself via `showTooltip.from(field)` and
returns null on most transactions. That null provider sits in the
`showTooltip` facet and silently suppresses the autocomplete popup
outside of paren contexts. `CM.linter()`'s only tooltip is a
`hoverTooltip` that fires on mouseover, not on every keystroke.

Adds `@codemirror/lint` to the dist bundle (already a transitive
dependency of `codemirror`, just needs to be re-exported from
`src/index.mjs` so it ends up in `dist/`). Bundle rebuilt.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Diagnostic TypedDict: make `line`/`message` required and use
  `NotRequired` for `severity`/`source`, matching the docstring.
- `set_diagnostics` docstring: drop the inaccurate gutter-underline
  claim; we deliberately don't install `lintGutter()`.
- JS: drop the dead empty-string fallback for `message` (now required),
  and `console.warn` + skip non-integer or out-of-range line numbers
  instead of silently clamping them onto the wrong line.
- Test: drop the slack `screen.wait(0.3)`; assert that severity-suffixed
  classes (`.cm-lintRange-error`, `.cm-lintRange-info`) appear; add a
  case exercising the default-`error` severity branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant