ADR-0024 HintMode dispatch via walker_hint_mode_at_input

Adds the `HintMode` dispatch layer the ADR specified: the
ambient-hint resolver now consults a single
`walker::hint_mode_at_input(source) -> Option<HintMode>` to
decide between the prose / candidates ladder, rather than
discovering each slot kind through three separate post-hoc
helpers (`value_literal_hint_at_cursor`,
`typing_name_at_cursor`, and so on).

Behaviour at slot positions today:

- **Value-literal slot** (`null`/`true`/`false`/number/string
  all in the expected set) → `HintMode::ProseOnly
  ("hint.value_literal_slot")`. The ambient-hint ladder
  emits the catalog prose at empty prefix; once the user types
  a partial (`n`, `tr`, `fa`) the partial check declines and
  normal candidate completion takes over.
- **NewName ident slot** → `HintMode::ForceProse
  ("hint.ambient_typing_name")`. The ladder still consults
  `typing_name_at_cursor` to learn what comes after the name
  (the post-name probe is unchanged); `ForceProse` is the
  declarative tag telling the resolver *that* we're in this
  mode.

`HintMode` itself gains `PartialEq + Eq` for tests, and
its docstring is rewritten to describe the live semantics.

This is the structural shape ADR-0024 §HintMode-per-node
describes: one slot → one hint mode → one dispatch arm. The
detection inside `hint_mode_at_input` is transitional — it
pattern-matches the walker's expected-set today, which is
exactly what the previous ad-hoc detectors did. Phase D will
replace the signature match with node-attached `HintMode`
annotations on the typed value slots (so `date_slot`,
`int_slot`, etc. each carry a type-specific catalog key).

Two helpers move into `input_render.rs`:
- `hint_leading_slice(input, cursor)` mirrors the look-back
  used by `candidates_at_cursor` so the hint resolver sees the
  same token-boundary view of the world.
- `cursor_partial_is_empty(input, cursor)` distinguishes
  empty-prefix from in-progress identifier shapes.

8 new walker tests pin the hint-mode resolver across
value-literal-after-paren, value-literal-after-set-assign,
value-literal-in-where, two NewName-slot cases, the
entry-keyword position, the complete-command position, and
the schema-ident position.

Tests: 817 passing, 0 failing, 1 ignored. Clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-15 17:32:17 +00:00
parent 7ae1a0fde1
commit 85817791dc
3 changed files with 238 additions and 29 deletions
+18 -6
View File
@@ -119,13 +119,25 @@ impl IdentSource {
}
}
/// Hint-panel mode for an expected node.
/// Hint-panel mode for an expected node (ADR-0024 §HintMode-per-node).
///
/// Phase A defaults to `Default`; the `ProseOnly` variant
/// attaches to typed value slots in Phase D so the hint reads
/// "Type a date as 'YYYY-MM-DD'" rather than candidate-cycling.
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
/// `Default` (today's behaviour) shows candidates if any, falls
/// back to a prose ladder otherwise. The other variants
/// override at slot positions where the candidate list would be
/// actively misleading or where the user benefits from format
/// guidance:
///
/// - `ProseOnly(catalog_key)` — show only prose from the
/// catalog; suppress Tab candidates. Used today by the
/// value-literal slot at empty prefix (the "null/true/false"
/// candidate trio is misleading at a slot that more often
/// takes a number / quoted text / date).
/// - `ForceProse(catalog_key)` — force this prose at the
/// catalog key regardless of candidates. Used today by
/// `NewName` ident slots ("Type a name, then `(`").
/// - `SuppressProse` — show only candidates; never fall back
/// to a prose ladder.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HintMode {
Default,
ForceProse(&'static str),