Skip to content

Add custom completions to CodeMirror#5986

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

Add custom completions to CodeMirror#5986
Jepson2k wants to merge 2 commits intozauberzeug:mainfrom
Jepson2k:cm-custom-completions

Conversation

@Jepson2k
Copy link
Copy Markdown

@Jepson2k Jepson2k commented Apr 23, 2026

Motivation

Embedding ui.codemirror as an in-app code editor often needs to surface a list of host-application-specific identifiers (functions, variables, keywords) in the autocomplete dropdown — e.g. an in-app scripting environment that wants to expose its own API to the user. CodeMirror 6's @codemirror/autocomplete package supports this via autocompletion({override: ...}), 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):

  • CompletionItem TypedDict with required label and NotRequired optional fields mirroring CM6's Completion surface (camelCase to round-trip verbatim): displayLabel, detail, info, apply (the text actually inserted on accept; defaults to label), type, boost (sort weight, -99..99), commitCharacters, section.
  • type is constrained to COMPLETION_ICON_TYPE, a Literal over CM6's 12 built-in icon names (class, constant, enum, function, interface, keyword, method, namespace, property, text, type, variable), matching the existing SUPPORTED_LANGUAGES / SUPPORTED_THEMES precedent in the same file.
  • custom_completions: list[CompletionItem] | None = DEFAULT_PROP | None constructor kwarg, custom_completions property + setter, set_custom_completions(...) method. The kwarg uses the DEFAULT_PROP | None pattern so per-instance defaults compose with the rest of the constructor.

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

  • New customCompletions: Array prop with a Vue watcher that delegates to setCustomCompletions(...) so changes from the Python side propagate without mounting a new editor.
  • setCustomCompletions(...) builds an autocompletion({override: [source]}) extension that filters entries by label-prefix against the word before the cursor, then dispatches it through a CM6 Compartment so the source can be replaced after mount. Only fields the user supplied are forwarded to CM6 — no forced type: 'function' default and no padded empty detail/info strings.
  • New completionsConfig Compartment slot in the editor's extension list. mounted() initializes the Compartment and applies any initial customCompletions once the editor exists.

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

  • Re-export @codemirror/autocomplete from src/index.mjs so autocompletion ends up in the dist bundle. @codemirror/autocomplete 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_custom_completions types foo into an editor whose initial custom_completions includes two foo_* entries (with different boost values, one with displayLabel) plus an unrelated qux, then asserts: (a) only the two matching entries render, (b) the higher-boost entry sorts first, (c) displayLabel overrides the visible label while label is still used for matching.
  • test_set_custom_completions_replaces_initial verifies that calling set_custom_completions after mount replaces the initial source (typing matches the new entries, not the original).

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

  • A numpy-flavored demo whose entries exercise apply, detail, type, boost, displayLabel, section, and commitCharacters, plus a {'label': 'TODO'} entry to show the no-icon rendering.

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:32
`custom_completions` constructor kwarg + property + `set_custom_completions(...)`
setter add a programmatic completion source to the editor's autocomplete
dropdown. Each entry is a `CompletionItem` TypedDict with required `label`
and optional `detail`, `info`, `apply`, and `type` (`'function'`,
`'variable'`, `'class'`, `'keyword'`, etc., controlling the icon shown
next to the entry).

The kwarg uses the `DEFAULT_PROP | None` pattern so per-instance defaults
compose correctly with the rest of the constructor.

JS side installs `CM.autocompletion({override: ...})` lazily through a
Compartment so the source can be reconfigured after mount when
`set_custom_completions` (or the `customCompletions` prop) changes.

Adds `@codemirror/autocomplete` to the dist bundle (already a transitive
dep of `codemirror`, just re-exported from `src/index.mjs`). Bundle
rebuilt.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Make `label` required on CompletionItem (was incorrectly optional under
  total=False); switch the rest to NotRequired.
- Constrain `type` to a Literal of CM6's 12 built-in icon names, matching
  SUPPORTED_LANGUAGES/SUPPORTED_THEMES precedent.
- Add `displayLabel`, `boost`, `commitCharacters`, and `section` (string
  form) to cover the full public CM6 Completion surface; field names are
  kept in CM6's camelCase so the dict round-trips verbatim.
- Stop forcing `type: function` and stop padding empty detail/info strings
  in the JS source — only forward keys the user supplied.
- Drop the customCompletions add_rename — add_rename is a deprecation
  shim, not a wire mapper, and isn't needed for a brand-new prop.
- Add version markers, drop a dead nonlocal in test_custom_completions,
  and replace the brittle screen.wait(0.3) sleeps in
  test_set_custom_completions_replaces_initial.
- Extend the autocomplete test to assert boost ordering and displayLabel
  rendering. Refresh the docs demo to exercise the new fields.
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