Add custom completions to CodeMirror#5986
Draft
Jepson2k wants to merge 2 commits intozauberzeug:mainfrom
Draft
Conversation
`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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Embedding
ui.codemirroras 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/autocompletepackage supports this viaautocompletion({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):CompletionItemTypedDictwith requiredlabelandNotRequiredoptional fields mirroring CM6'sCompletionsurface (camelCase to round-trip verbatim):displayLabel,detail,info,apply(the text actually inserted on accept; defaults tolabel),type,boost(sort weight, -99..99),commitCharacters,section.typeis constrained toCOMPLETION_ICON_TYPE, aLiteralover CM6's 12 built-in icon names (class,constant,enum,function,interface,keyword,method,namespace,property,text,type,variable), matching the existingSUPPORTED_LANGUAGES/SUPPORTED_THEMESprecedent in the same file.custom_completions: list[CompletionItem] | None = DEFAULT_PROP | Noneconstructor kwarg,custom_completionsproperty + setter,set_custom_completions(...)method. The kwarg uses theDEFAULT_PROP | Nonepattern so per-instance defaults compose with the rest of the constructor.JavaScript (
nicegui/elements/codemirror/codemirror.js):customCompletions: Arrayprop with a Vue watcher that delegates tosetCustomCompletions(...)so changes from the Python side propagate without mounting a new editor.setCustomCompletions(...)builds anautocompletion({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 forcedtype: 'function'default and no padded emptydetail/infostrings.completionsConfigCompartment slot in the editor's extension list.mounted()initializes the Compartment and applies any initialcustomCompletionsonce the editor exists.Bundle (
nicegui/elements/codemirror/src/index.mjs,nicegui/elements/codemirror/dist/):@codemirror/autocompletefromsrc/index.mjssoautocompletionends up in the dist bundle.@codemirror/autocompletewas already a transitive dep of thecodemirrormeta-package, sopackage.jsondoesn't change.npm run clean && npm run build.Tests (
tests/test_codemirror.py):test_custom_completionstypesfoointo an editor whose initialcustom_completionsincludes twofoo_*entries (with differentboostvalues, one withdisplayLabel) plus an unrelatedqux, then asserts: (a) only the two matching entries render, (b) the higher-boostentry sorts first, (c)displayLabeloverrides the visible label whilelabelis still used for matching.test_set_custom_completions_replaces_initialverifies that callingset_custom_completionsafter mount replaces the initial source (typing matches the new entries, not the original).Documentation (
website/documentation/content/codemirror_documentation.py):apply,detail,type,boost,displayLabel,section, andcommitCharacters, plus a{'label': 'TODO'}entry to show the no-icon rendering.Progress