Skip to content

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

Description

@BorisTyshkevich

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