Skip to content

Consider CodeMirror 6 for the SQL editor (schema-aware autocomplete) #21

Description

@BorisTyshkevich

Reopened 2026-06-30 — the trigger this issue named ("adopt CM6 when autocomplete becomes a priority") is now met: #84 (schema-aware autocomplete) is a 1.0 feature, and #88 settled the foundation (signals-core + imperative adapters behind seams, no UI framework). CM6 is now scheduled, behind an EditorPort seam.

Goal

Replace the hand-rolled <textarea> editor (src/ui/editor.js + editor-* modules) with CodeMirror 6 behind an injected EditorPort seam — unlocking schema-aware autocomplete (#84), inline docs (#60), bracket matching, search and folding, without breaking the single self-contained artifact (CM6 bundles + inlines via esbuild, like Chart.js).

Sequencing

After the signals migration (#88); before #84/#60. See the #68 build order (Phase 3).

Key implementation — EditorPort + dual adapter (reversible swap)

Acceptance criteria

  • CM6 mounts behind EditorPort; the bundle delta is measured + recorded (CLAUDE.md rule 4 — the deliberate 4th runtime dep).
  • Selection / undo / redo, drag-drop schema identifiers, and the run / format / save / share wiring all work through the port.
  • SQL logic stays pure in core/ at 100%; the CM6 wrapper is gated like app.js.
  • Tri-engine (Chromium/Firefox/WebKit, Add WebKit/Safari to e2e + decide the supported-browser stance #69) e2e green; the textarea adapter is removed.

Re-evaluation trigger

If the measured bundle delta is unacceptable, fall back to the textarea + the "middle path" schema-driven completion dropdown (kept zero-dep) — see the survey below.

Tracking

Part of #68 (Phase 3). Enables #84, #60. Foundation: #88 / docs/ADR-0001-reactivity.md (rule 5).

Original survey + rationale (2026-06-23, when this issue recommended deferring)

Context

The SQL editor is written from scratch, zero-dependency (src/ui/editor.js, ~125 lines): a <textarea> overlaid on a syntax-highlighted <pre> with a line-number gutter, fed by a hand-rolled ~100-line pure tokenizer (src/core/sql-highlight.js). It's fully covered by the 100% test gate and adds nothing to the bundle.

It nails the 90% case — write, highlight, line numbers, Tab-indent, drag-drop schema identifiers, undo, save/share/run wiring. What it lacks: autocomplete (especially schema-aware table/column completion), bracket matching, auto-indent, in-editor search, folding, multi-cursor, error squiggles.

Options surveyed

Library Size Fit for this app
Monaco (VS Code editor) ~5–10 MB + web workers ❌ Disqualified. We ship one self-contained HTML file with zero third-party requests (served from ClickHouse user_files); Monaco's size + worker model can't be cleanly inlined and would bloat the ~316 KB artifact ~20×. Unusable on mobile.
Ace ~500 KB, older arch ⚠️ Works, but dated and no advantage over CM6.
CodeMirror 6 modular, ~50 KB core → ~150 KB with SQL + autocomplete (less gzipped) ✅ Only realistic candidate. Tree-shakeable, no workers, bundles+inlines with esbuild like Chart.js, good on mobile. Used by Prisma Studio, Observable, Replit, Firefox DevTools.

The case for CM6

One real reason: schema-aware autocomplete. It's the single capability that matters for a SQL browser and that the hand-rolled editor can't reasonably match. @codemirror/lang-sql's schemaCompletionSource turns the schema tree we already load into live table.column completion (plus keyword/function completion, bracket matching, search, folding for free). Reimplementing a decent completion engine on the textarea is most of the work of adopting CM6 anyway, with worse results.

Costs (these cut against the project's grain)

  • A second bundled runtime dependency — CLAUDE.md hard-rule feat(auth): multi-IdP config.json + login provider picker (phase 1) #4 makes this a deliberate decision (Chart.js was the first). ~+100 KB+ to the single served file.
  • The editor DOM wrapper drops below the 100% coverage gate → move it behind an injected seam (app.Editor, exactly the Chart.js precedent), keeping the SQL knowledge (keywords/funcs/schema) pure in core/.
  • CM6's transaction/extension API is a real integration surface — selection, undo, drag-drop, and the share/save wiring all get rewritten.

Recommendation

Don't migrate now. The current editor is well-matched to the constraints and migrating is a net loss unless we're buying a feature it structurally can't provide.

Adopt CM6 when autocomplete becomes a priority (or we want folding/search/multi-cursor as a set). At that point CM6 is clearly the right pick — not Monaco (size/worker/single-file killer) or Ace — bundled and inlined behind an injected seam like Chart.js, keeping the artifact self-contained.

Middle path (if we only want completion and want to avoid a second dep): keep the textarea and build a small schema-driven completion dropdown ourselves — we already have the schema + tokenizer. Stays zero-dep and on-gate, but won't match CM6's polish and is more maintenance.

Suggested next step

Prototype the CM6 route on a branch behind a seam (lang-sql + schemaCompletionSource wired to the live schema) to measure the real bundle delta and the autocomplete UX before committing.

References

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions