ADR-0022 stage 8 follow-up: fixes from real-app testing

Three fixes from the user's testing run, plus an
investigation note on a fourth.

#4 Sticky hint during cycling. The previous code recomputed
candidates_at_cursor at the post-Tab cursor position, which
made the panel whiplash through "what comes next at the new
cursor" between cycles. ambient_hint now short-circuits to
the memo's stored candidate list while the memo is alive —
so Tab Tab Tab keeps showing the same list with the
selection moving, then snaps to the post-Tab ambient state
once any non-Tab key clears the memo.

#2 Candidate ordering and kind-coloured rendering. New
`Candidate { text, kind: Keyword|Identifier }` carries the
classification through completion, last-completion memo,
and ambient-hint payload. candidates_at_cursor now sorts
keywords first (alphabetical), identifiers second
(alphabetical), and the hint-panel renderer colours keywords
in `tok_keyword` and identifiers in `tok_identifier`.
Keyword-vs-identifier name collisions resolve in favour of
the keyword (rare; the user can still address their table
via different syntax).

#3 tok_identifier no longer matches theme.fg. Identifiers
in the input pane now render in a distinct cool grey-blue
(dark) / dark steel-blue (light), so they stand out from
prose-like default text without competing with keyword
purple. Same colour drives the identifier candidates in
the hint panel for visual consistency input ↔ hint.

Limitation worth knowing: "keywords first, alphabetical"
is not the same as grammatical order. For "add column "
the hint shows `table to` not `to table` — chumsky's
expected-set doesn't preserve combinator-source order, and
encoding it in the registry adds maintenance overhead the
fix doesn't cleanly justify. Marked for future revisit if
it bites.

#1 (Tab does nothing on "add column ") — not reproduced
through App::update. The internal logic works correctly:
"add column " + Tab inserts "Customers ", second Tab
cycles to "Orders ", third to "Thing ". The most likely
explanation is a stale binary or a terminal-level event
intercept (tmux focus, kitty-keyboard protocol differences,
etc.) — needs user verification with a fresh build.

Tests: 747 passing, 0 failing, 1 ignored (744 baseline →
+3: 2 new completion-ordering cases including the
keyword-wins-on-name-collision edge, plus 1 hint-mid-cycle
sticky test). Clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-11 22:12:16 +00:00
parent 8214e4136a
commit bd1cce672d
5 changed files with 237 additions and 82 deletions
+2 -2
View File
@@ -70,7 +70,7 @@ impl Theme {
// accent tones; keyword takes a cool accent tone
// distinct from the mode-banner blue.
tok_keyword: Color::Rgb(0xC7, 0x92, 0xEA), // muted purple
tok_identifier: Color::Rgb(0xE6, 0xE6, 0xE6), // == fg
tok_identifier: Color::Rgb(0xAB, 0xB2, 0xBF), // cool grey-blue (distinct from fg)
tok_number: Color::Rgb(0xF7, 0x8C, 0x6C), // warm orange
tok_string: Color::Rgb(0xC3, 0xE8, 0x8D), // soft green
tok_punct: Color::Rgb(0x8B, 0x90, 0x9A), // == muted
@@ -96,7 +96,7 @@ impl Theme {
// identifier/punct close to fg/muted; warm tones for
// literals + flags; cool accent for keyword.
tok_keyword: Color::Rgb(0x6F, 0x42, 0xC1), // royal purple
tok_identifier: Color::Rgb(0x1A, 0x1F, 0x2C), // == fg
tok_identifier: Color::Rgb(0x3F, 0x47, 0x57), // dark steel-blue (distinct from fg)
tok_number: Color::Rgb(0xBC, 0x4F, 0x1F), // burnt orange
tok_string: Color::Rgb(0x22, 0x86, 0x3A), // forest green
tok_punct: Color::Rgb(0x60, 0x66, 0x73), // == muted