Skip to content

feat(editor): autocomplete output formats inside a FORMAT clause#35

Merged
BorisTyshkevich merged 4 commits into
mainfrom
feat/format-autocomplete
Jun 24, 2026
Merged

feat(editor): autocomplete output formats inside a FORMAT clause#35
BorisTyshkevich merged 4 commits into
mainfrom
feat/format-autocomplete

Conversation

@BorisTyshkevich

Copy link
Copy Markdown
Collaborator

What

Autocomplete ClickHouse output format names (Vertical, JSONEachRow, TSVWithNames, Pretty, CSV, …) — but only inside a FORMAT clause.

Why

Completion candidates came from system.keywords + system.functions + the schema. Format names live in a separate catalog (system.formats, 90+ output formats here), so FORMAT completed but the name after it never did. This matters now that a trailing FORMAT … clause runs the query raw and shows ClickHouse's response verbatim.

How

  • ch-client.jsloadReferenceData also fetches SELECT name FROM system.formats WHERE is_output (one bulk query per connection, like keywords/functions; best-effort → null on old/denied).
  • completions.jsassembleReferenceData carries formats (built-in fallback list when unavailable); buildCompletions emits a new format candidate kind; completionContext sets an afterFormat flag when the token before the caret is FORMAT; rankCompletions shows only formats there (prefix-first) and excludes formats from general completion so the 90+ names never clutter normal suggestions.
  • editor-complete.js — a glyph for the format kind in the dropdown.
  • app.js / editor.js unchanged — they already pass the whole ref/candidate list through.

Tests

completions.test.js (assemble fallback, build, afterFormat detection, context-aware ranking) + ch-client.test.js (formats query). 742 unit tests pass; completions.js and ch-client.js stay at 100%. CI also runs the Playwright e2e (Chromium + Firefox).

Behavior

  • SELECT … FORMAT → dropdown lists output formats; typing VerVertical.
  • Outside a FORMAT clause, completion is unchanged (no format noise).
  • Offline / pre-connect: a built-in fallback list is used until system.formats loads.

🤖 Generated with Claude Code

BorisTyshkevich and others added 4 commits June 24, 2026 22:26
…clause

Completion candidates were drawn only from system.keywords, system.functions
and the schema — so `FORMAT` completed but the format name after it never did,
even though a trailing FORMAT clause now drives the raw result view.

Load output formats once per connection from system.formats (is_output), assemble
them into the reference data (with a built-in fallback for offline/old servers),
and offer them as a new `format` completion kind — but only inside a FORMAT
clause: completionContext flags `afterFormat` when the preceding token is FORMAT,
and rankCompletions then shows only formats there (prefix-first) while excluding
them from general completion so 90+ names don't clutter normal suggestions.

app.js/editor.js need no changes (they pass the whole ref/candidate list through).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBS74oUsXarGkCRQKEFLu
…eturn shapes

PR review follow-up: the two JSDoc blocks still described the pre-format return
shapes. Add `formats` (and `keywordDocs`) to assembleReferenceData's documented
shape and `afterFormat` to completionContext's. Comment-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBS74oUsXarGkCRQKEFLu
…keyword wins its tie

Two completion UX fixes (PR review follow-up):

- Accepting a function inserted a lone `name(` — an unmatched paren, awkward and
  inconsistent with typing `(` (which auto-closes to `()`). Now functions insert
  `name()` and a new `caretBack` field (threaded through replaceRange) leaves the
  caret between the parens, so signature args are one keystroke away and there's
  never a stray `(`.
- `for`+Tab inserted the obscure `format()` function instead of the FORMAT clause.
  A tiny PREFER_KEYWORD set (just FORMAT) boosts that clause keyword above its
  function namesake (and formatDateTime, …) once ≥3 chars are typed — so `for` →
  FORMAT → format-name completion. Other keyword/function name clashes (min, max,
  replace, left, in, like, …) deliberately keep favoring the function.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBS74oUsXarGkCRQKEFLu
…AT is given)

EXPLAIN output is plan text; as a one-column table it loses its indentation and
reads poorly. Default EXPLAIN to the raw text view as plain TabSeparated (no
header noise) so the plan shows verbatim; an explicit trailing FORMAT clause
still wins (detectSqlFormat is checked first). runQuery now passes a raw format
name through as default_format (instead of always JSONCompact) — that's what
lets the implicit EXPLAIN format request plain TabSeparated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBS74oUsXarGkCRQKEFLu
@BorisTyshkevich BorisTyshkevich force-pushed the feat/format-autocomplete branch from 56f28fc to cb6f334 Compare June 24, 2026 21:19
@BorisTyshkevich BorisTyshkevich merged commit 987f56f into main Jun 24, 2026
4 checks passed
@BorisTyshkevich BorisTyshkevich deleted the feat/format-autocomplete branch June 24, 2026 21:23
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