fix: advanced CREATE TABLE completion cluster

Three completion / hint bugs in the same advanced-mode grammar
+ walker path:

1. `create table T ` offered only `with` (the DSL fallback) — the
   `(` continuation for the SQL column-def list (ADR-0035 §4) was
   missing because the shared-entry-word completion merge in
   `completion_probe_in_mode` only fired at the entry-word boundary.
   Broadened to fire at any cursor depth and to handle
   `Expectation::Punct` continuations alongside `Word`/`Literal`. A
   shared-entry-word candidate whose grammar has already diverged
   (e.g. SQL `CREATE INDEX` past `create table …`) returns
   Mismatch and is naturally skipped — the viability check stays the
   gate, not the cursor depth.

2. `create table T (` showed only the table-level constraint
   keywords (`primary`, `unique`, `check`, `constraint`, `foreign`)
   in the ambient hint, leaving the column-name role invisible
   because COLUMN_DEF starts with an `Ident::NewName` slot that
   produces no concrete candidate. Added a new `HintMode::IntroProse(
   &'static str)` variant that surfaces catalog prose at slot entry
   without suppressing Tab completion (unlike `ProseOnly`) and
   without requiring `typing_name_at_cursor` to fire (unlike
   `ForceProse`). Wrapped ELEMENT in `Node::Hinted { mode: IntroProse(
   "hint.create_table_element"), … }`, with prose "Type a column
   name, or a table-level constraint: `primary`, `unique`, `check`,
   `constraint`, `foreign`". Tab still cycles every keyword.

3. The SQL_TYPE position leaked the bare keyword `double` (the
   first token of the dedicated `double precision` Choice branch
   per ADR-0035 §6.3) alongside the playground's regular type list.
   Added `("double", "double precision")` to `COMPOSITE_CANDIDATES`
   and extended the keyword filter to drop composite openers so the
   composite phrase replaces the bare opener instead of appearing
   alongside it. Tab now offers `double precision` as a single
   coherent candidate; the partial-typing prose at the same slot is
   subsumed by item 2's IntroProse (the user reads "Type a column
   name…" while mid-typing, then advances to the clean type list).

Tests added (4): pinning each behavioural promise above plus the
no-leakage assertion at the partial-typing prose position. Full
suite 2035 passed / 0 failed / 0 unexpected skips. Clippy clean.

The new `HintMode::IntroProse` variant is an additive extension to
the ADR-0024 HintMode-per-node model; no behaviour change to
existing modes. An ADR-0024 amendment recording it can follow later
if desired — flagged but not written.
This commit is contained in:
claude@clouddev1
2026-05-28 18:56:13 +00:00
parent c12ed1da9a
commit 6f87ad1842
7 changed files with 259 additions and 35 deletions
+10
View File
@@ -149,6 +149,15 @@ impl IdentSource {
/// - `ForceProse(catalog_key)` — force this prose at the
/// catalog key regardless of candidates. Used today by
/// `NewName` ident slots ("Type a name, then `(`").
/// - `IntroProse(catalog_key)` — show prose at slot entry to
/// *introduce* a position whose first-class candidate is an
/// ident slot (which would be invisible in a pure-candidate
/// render) but whose keyword alternatives are also available.
/// Unlike `ProseOnly`, Tab candidates remain available — the
/// user still cycles through the keyword set. Used at the
/// advanced-mode CREATE TABLE element slot, where the
/// column-name `NewName` slot would otherwise be invisible
/// alongside the table-level constraint keywords (issue #4).
/// - `SuppressProse` — show only candidates; never fall back
/// to a prose ladder.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -156,6 +165,7 @@ pub enum HintMode {
Default,
ForceProse(&'static str),
ProseOnly(&'static str),
IntroProse(&'static str),
SuppressProse,
}