feat(hint): advertise the optional seed count in the hint panel (#26)

At `seed <table> ▮` the hint showed only the `set`/`--seed` chips and
never mentioned the optional row count — a bare positional number with no
candidate, on an already-complete command, so neither the candidate
ladder nor the resolver surfaced it. (A prior IntroProse attempt was
reverted: pending_hint_mode is cleared by the trailing optionals.)

Carry a skipped Optional's IntroProse hint: walk_optional stashes the
inner's key into a new WalkContext.surviving_intro_hint (key + position)
before the empty match clears pending_hint_mode; the snapshot keeps it
only when the skip position is the cursor (so it never leaks past a
later-consumed `set …` clause, nor once the count is given); the
resolver returns it ahead of the empty-expected short-circuit. The seed
count is wrapped Hinted{IntroProse("hint.seed_count")}; the prose names
the count (default 20), the `.column` column-fill form, and `set` /
`--seed`. Tab still cycles the keywords.

Only IntroProse is carried; ProseOnly/ForceProse and the CREATE-TABLE
element (a required Repeated) are untouched. No AmbientHint/renderer
change. Fires in both modes.

ADR-0022 Amendment 7; +3 tests.
This commit is contained in:
claude@clouddev1
2026-06-12 21:34:48 +00:00
parent deb0948d6c
commit ee3ccd8d77
9 changed files with 216 additions and 2 deletions
@@ -772,6 +772,58 @@ invalid_ident_does_not_fire_for_column_prefix_at_sql_expr_slot}`;
`theme::function_colour_is_distinct_from_keyword_identifier_and_type`.
See ADR-0031's status note for the grammar-side anchor.
## Amendment 7 — optional positional args reach the hint panel (2026-06-12)
Issue #26. At `seed <table> ▮` the hint panel showed only the
`set` / `--seed` continuation chips and never mentioned the
**optional row count** — even though a count (`seed users 50`) is
the most common next move. The count is a bare positional
`NumberLit` with no keyword/candidate text, so the candidate ladder
can't surface it; and `seed <table>` is already a *complete*
command, so the hint resolver short-circuits (empty expected set).
The existing `IntroProse` `HintMode` (ADR-0024 §HintMode-per-node;
issue #4's CREATE-TABLE element hint) is the right tool — it shows
prose that *introduces* a position whose first-class move has no
candidate, with the keyword alternatives folded into the prose and
Tab still cycling them. But it did not reach this position: a
`Node::Hinted`'s mode lives in `pending_hint_mode`, which the very
next match clears — including the **empty** match of a skipped
`Optional`. The CREATE-TABLE element survives only because it sits
in a *required* `Repeated(min:1)`; an optional positional followed
by more optionals (the seed count) is cleared before the resolver
reads it.
### Mechanism
A small, general carry: when `walk_optional` skips its inner (the
inner didn't engage), it stashes any `IntroProse` key the inner
left in `pending_hint_mode` into a new `WalkContext` field,
`surviving_intro_hint: Option<(key, position)>`, **before** the
empty match clears `pending_hint_mode`. The trailing optionals,
which are not `IntroProse`, don't overwrite it. The hint snapshot
keeps the key **only when `position == cursor`** (the slice end),
so it shows while the cursor sits at the count slot but not once a
later clause (`set …`) consumes input past it, nor once the count
itself is supplied. The resolver returns that `IntroProse` even for
an otherwise-complete command (ahead of the empty-expected
short-circuit).
The seed grammar wraps the count in
`Hinted { IntroProse("hint.seed_count"), NumberLit }`; the prose
names the count (with its default 20) plus the `.column`
column-fill form and the `set` / `--seed` keywords (user-chosen
scope: mention every option). Only `IntroProse` is carried —
`ProseOnly` / `ForceProse` mark *active* slots and reach the
resolver through the normal path, unchanged. The CREATE-TABLE
element (in a `Repeated`, not an `Optional`) is untouched.
This is a refinement of ADR-0024 §HintMode-per-node and a sibling
of issue #4; no `AmbientHint` / renderer change. Covered by
`input_render::{seed_count_is_advertised_at_the_optional_position,
seed_count_hint_does_not_leak_once_the_count_or_a_clause_is_given,
seed_count_hint_also_fires_after_a_column_fill_target}`.
## Out of scope
Deliberately deferred to keep this ADR shippable as a single