Add as-you-type autocompletion (inline hints) to the CLI#108070
Conversation
Bring the Web UI (play.html) autocompletion principle to clickhouse-client and clickhouse-local: show the best matching suggestion as inline "ghost" text as you type, instead of only on Tab. This reuses replxx's hint system, which ClickHouse never wired up. A single match renders inline after the cursor (gray); several matches render the selected one inline plus a navigable list (Ctrl-Up/Ctrl-Down). Tab accepts the selected hint, and Right at the end of the line accepts it too. Suggestions are prioritized like in the Web UI: identifiers used in queries run earlier this session first, then identifiers already present in the current query line, then the rest in the existing sorted order. The completion (Tab) and hint callbacks are routed through one shared `Suggest::getMatchingWords` so they always return the same words in the same order — required because replxx accepts a hint by indexing the completion list with the hint selection. Controlled by a new `--hints` option (default on); it requires `--highlight` because hints need color. Identifiers are extracted with the SQL lexer so that string literals, numbers, and comments are not mistaken for identifiers. Adds a PTY test covering ghost text, Tab/Right accept, --hints 0, and the in-query prioritization. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bind Up/Down (in addition to Ctrl-Up/Ctrl-Down) to navigate the as-you-type hint list: Down steps into and advances the list, Up moves back through it, and Right/Tab accept the selected hint. Up only navigates hints once the user has stepped into the list with Down (tracked by `hint_active`), so before that Up keeps recalling command history and the hints never shadow it. Hint navigation is limited to single-line input with the cursor at the end, so multi-line line movement is preserved. Esc is intentionally not bound to dismiss the hints: the bundled replxx reads a lone Esc by blocking for the next byte (there is no escape-key timeout on POSIX), so a standalone Esc cannot be intercepted reliably. The Up-recalls- history behavior covers the need to get back to history instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gments Previously the inline hints were shown only when the cursor was at the very end of the input. Now they also appear when the cursor is right after an identifier-like fragment in the middle of the query (e.g. editing a column name before `FROM ...`), mirroring the Web UI trigger. This bumps the `replxx` submodule to a fork commit that relaxes the end-of-buffer gate: mid-line the suggestions are shown as the list below the cursor (the inline "ghost" suffix is still only used at the end of the line, where it can be appended after the whole input without disturbing the text after the cursor). On the ClickHouse side, hint navigation with Up/Down now applies to single-line input regardless of the cursor position, so the mid-line suggestions are navigable too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix Up/Down not working for hints shown in the middle of the line: - Bump replxx so the mid-line hint list highlights the selected entry (the inline ghost is unavailable mid-line, and moving the selection into it made navigation invisible and hid the selected item). - In the middle of the line, Up navigates the hints directly instead of recalling history (which would clobber the in-progress edit); at the end of the line Up still recalls history until the user steps into the list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… line Two fixes for the as-you-type hints in multi-line queries: - Up/Down now navigate the hints whenever the hint list is shown, including on any line of a multi-line query (previously navigation was restricted to single-line input, so Up/Down kept moving between lines and the hints could not be navigated). - The hint list is shown right below the cursor's line (temporarily covering the lines below), instead of below the whole query, so it stays next to what is being completed. This bumps the `replxx` submodule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert the mid-line / multi-line hint work (the `replxx` submodule changes and the related navigation tweaks), going back to showing the as-you-type hints only when the cursor is at the end of the input. The submodule returns to its original upstream commit, so no `replxx` patch is needed. End-of-line behavior is unchanged: ghost text as you type, Up/Down (and Ctrl-Up/Ctrl-Down) navigation, Tab/Right to accept, the `--hints` option, and the priority ordering all remain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The as-you-type hints are shown at the end of the input, which for a multi-line query is the last line. Navigation was restricted to single-line input, so Up/Down moved between lines there instead of choosing a hint. Drop that restriction: since hints are only shown at the end of the buffer, Up/Down keep their normal line/history movement everywhere else and only navigate the hints where they are actually shown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Right now accepts the chosen hint (the single one shown, or the one selected by navigating) and never falls back to the old-style completion list — only Tab does that. When no hint is chosen, Right just moves the cursor. - Enter also accepts a chosen hint (like Tab and Right) instead of running the query / inserting a newline; when no hint is chosen it runs the query as before. To distinguish "a hint is chosen" from "several matches with nothing selected", the selected hint index and count are tracked in sync with replxx's internal selection. `hints_visible` now requires a hint with a non-empty suffix, so a fully-typed word (which matches itself with an empty suffix) does not capture Right/Enter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI showed an interactive expect test (02049_clickhouse_local_merge_tree) timing out: the as-you-type hints interfered with tests that drive the client in a PTY. Two fixes, keeping hints on by default: - `--disable_suggestion` now also disables the hints (it means "no autocompletion"). This covers all expect tests that use the standard CLICKHOUSE_CLIENT_EXPECT_OPT (which already passes --disable_suggestion and --highlight 0) as well as those that pass it directly. - Enter accepts a hint only when one is explicitly selected by navigating; a single ghost shown while typing no longer captures Enter, so "type a command and press Enter" keeps running the query. This was the actual cause of the 02049 timeout. The few interactive tests that test highlighting with --hilite/--highlight on (03566, 03591) now pass --hints 0, since they need color but not the hints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`Suggest::getHints` computed the matched/ranked word list via `getMatchingWords` and only then dropped it when the last word was empty. Because the hint callback runs on every repaint with a zero delay, an empty hint context (e.g. the cursor right after `SELECT `) made an empty last word match every suggestion and fold/stable-sort the whole dictionary on each keystroke, just to discard it. Determine that the last word is empty before doing any matching, mirroring the existing last-word computation in `getMatchingWords`, and return early. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`commit_action` accepts a hint with `Enter` only when one is explicitly selected (`hint_selection >= 0`); a single inline ghost hint leaves `hint_selection == -1`, so `Enter` runs the typed query as usual. `Tab` and `Right` accept the single inline hint. The `--hints` help text lumped all three keys together and overstated `Enter`. Qualify it to match the intentional behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The priority assertion compared the bare word `ReplicatedMergeTree`, which is also part of the typed input, so `rfind` could match the echoed input before the first rendered `ReplacingMergeTree` hint and pass even if the in-query prioritization were removed. Only the rendered hints are prefixed with the gray `HINT_COLOR` escape (the echoed input is syntax-highlighted differently), so anchor the comparison on the colored hint text. Now the test fails if `ReplicatedMergeTree` is not ranked before the alphabetically-earlier `ReplacingMergeTree` among the rendered hints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new `--hints` option was missing from the user-facing option lists. Document it next to `--highlight` in the `clickhouse-client` reference (`docs/en/interfaces/client.md`) and the `clickhouse-local` reference (`docs/en/operations/utilities/clickhouse-local.md`), including the default, the navigation/accept keys, and the `--highlight` / `--disable_suggestion` dependencies. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CLI as-you-type hints are accepted by routing through `replxx`'s `COMPLETE_LINE`, which indexes the *completion* list with the hint selection. Two paths (raised in review on `src/Client/ReplxxLineReader.cpp`) could make the accepted word differ from the displayed one: 1. The hint and completion callbacks recomputed the matches independently, so the background `Suggest::load` thread could insert a word that sorts before the displayed hint between display and acceptance, shifting the indexed completion. The matches are now computed once in the hint callback and cached (`hint_completions`), and the completion callback reuses that exact snapshot when the context matches, so the accepted word is always the one shown. The now-unused `Suggest::getHints` is removed. 2. `replxx` drops its internal hint selection on every line-regenerating action (any cursor movement) but only re-invokes the hint callback on text changes, so a plain cursor move (e.g. Left then Right back to the end of the line) left our `hint_selection` mirror stale — Right/Enter then treated a no-longer-shown hint as chosen and got swallowed by completion instead of running the query. A `replxx` modify callback (which runs on every dispatched action) now resets the mirror to track `replxx`; the hint-navigation keys re-apply the selection after invoking. Adds a PTY regression test for the cursor-movement path: after Down, Left, Right back to the end of the line, Enter must commit the query (a fresh prompt appears) rather than pop the completion list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@groeneai, please investigate this flaky integration test and provide a fix in a separate PR (or link an existing one):
It is the only failure on this PR, and it is unrelated to the change here (CLI autocompletion in |
|
Confirmed: this is an environmental flake, unrelated to the CLI autocompletion change here. Root cause: in CIDB evidence (30 days): this Fix (separate PR): #108860. It replaces the fixed-budget count poll with the existing |
The `stale_selection_enter_commits` subtest of `04402_clickhouse_client_autocomplete_inline_hints` was flaky on slow (sanitizer) builds: after the final `Enter` commits the query, the fresh `:)` prompt only appears once the query has executed server-side, which can take longer than the fixed 0.4s inter-keystroke drain. The test then collected the output before the prompt arrived and reported `FAIL`. Keep reading until the check predicate passes (bounded by `FINAL_WAIT_SECONDS`) instead of giving up after a single drain window. The wait is skipped entirely when the check already passed, so fast cases are unaffected, and a genuine logic regression (the prompt never appearing) still fails - it just waits the bound first - so this does not mask real bugs. CI report: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=108070&sha=09bef5015ac0b14eb241ac45cd259271e364c71f&name_0=PR Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the review finding on `LineReader.cpp`: the PR documents that identifiers already present in the current query (aliases, column names) and identifiers used earlier this session participate in the hints, but `getMatchingWords` only used `priority_words`/`recently_used` to re-rank entries that were already in the loaded suggestion dictionary. A query-only alias such as `SELECT veryUniqueAlias AS x, veryU` was therefore never offered unless it also existed in `system.completions`. Make those identifiers eligible candidates: after collecting the dictionary matches, also add the `priority_words` and recently-used words that match the typed prefix (same case-folding as the dictionary lookup), skipping the token currently being typed and deduplicating against the dictionary matches and each other. The existing tier ranking then applies, so they appear with the right priority for both `Tab` completion and the inline hints. Adds a PTY regression test (`in_query_alias_candidate`) where the expected hint comes from a query-local alias that is not in the suggestion dictionary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed two follow-up commits (
The remaining red on the previous run, |
|
CI completed for All remaining red checks are known, widespread flakes that are server-side and cannot be caused by this client-only (interactive
I re-triggered the failed jobs (the fuzzer/upgrade-check runs usually come back green on a clean rerun). The CH Inc sync is still in progress; its only failures so far are Cloud-only lanes ( No re-merge with |
LLVM Coverage Report
Changed lines: Changed C/C++ lines covered: 186/216 (86.11%) · Uncovered code |

Brings the Web UI (
play.html) autocompletion principle toclickhouse-clientandclickhouse-local: the best matching suggestion is shown as inline "ghost" text as you type, instead of only when pressingTab.How
This reuses
replxx's hint system, which ClickHouse had never wired up (no hint callback was registered).replxxalready implements the desired UX natively:Hints are shown when the cursor is at the end of the input (including the last line of a multi-line query). Mid-line hints were explored but dropped — see the note below.
Navigation & accept
Downsteps into and advances it;Upmoves back.Uponly navigates the hints once one is selected — before that it keeps recalling command history, so the hints never shadow it. This works on the last line of a multi-line query too.Enterstill runs the query. When no hint is chosen,Rightjust moves the cursor andEnterruns the query / inserts a newline as usual.replxxreads a loneEscby blocking for the next byte (there is no escape-key timeout on POSIX), so a standaloneEsccannot be intercepted reliably; theUp-recalls-history behavior covers getting back to history instead.Prioritization
Suggestions are ordered like in the Web UI:
The
Tabcompletion callback and the hint callback are routed through a single sharedSuggest::getMatchingWords, so they always return the same words in the same order — this is required becausereplxxaccepts a hint by indexing the completion list with the hint selection index. Identifiers are extracted with the SQL lexer (Parsers/Lexer.h) so that string literals, numbers, and comments are not mistaken for identifiers.Option
Controlled by a new
--hintsoption (default on). It requires--highlight(hints need color), so--highlight 0also disables hints;--hints 0disables them while keeping highlighting.Note: mid-line hints
Showing hints in the middle of a line / multi-line query was prototyped (it required patching the vendored
replxx), but dropped: the terminal rendering tradeoffs were not worth it. Noreplxxchange is included — the submodule is unchanged.Testing
tests/queries/0_stateless/04402_clickhouse_client_autocomplete_inline_hints(PTY-based, modeled on01676_clickhouse_client_autocomplete): ghost text appears as you type;Tab/Right/Enteraccept;Downsteps into the list; navigation works on the last line of a multi-line query;Rightdoes not pop the completion list when nothing is chosen;--hints 0shows no ghost; and an in-query identifier is hinted before alphabetically-earlier matches.Uprecalls history before stepping into the list, a fully-typed word does not blockEnter, andTabcompletion is unaffected with--highlight 0.Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
clickhouse-clientandclickhouse-localnow show as-you-type autocompletion hints (inline "ghost" text) for the best matching suggestion when the cursor is at the end of the input. The most relevant suggestions (recently used and identifiers already present in the query) are ranked first. Navigate with Up/Down (or Ctrl-Up/Ctrl-Down); accept a single or selected hint with Tab or Right, and accept a selected hint with Enter; Tab also opens the classic completion list. Controlled by the new--hintsoption (on by default; requires--highlight).Documentation entry for user-facing changes
Version info
26.7.1.332