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:
+22
-5
@@ -25,11 +25,22 @@ use crate::mode::Mode;
|
||||
/// one token but which the user types as a single fluent piece.
|
||||
/// Pairs of (walker-expected-literal, full-composite-text).
|
||||
///
|
||||
/// When the walker reports `Expectation::Literal(opener)` at the
|
||||
/// cursor, the engine surfaces the full composite text as a Tab
|
||||
/// candidate. Today the only entry is `1:n` (the opener for
|
||||
/// `add 1:n relationship`) — adding more is a one-line edit.
|
||||
const COMPOSITE_CANDIDATES: &[(&str, &str)] = &[("1", "1:n")];
|
||||
/// When the walker reports `Expectation::Literal(opener)` or
|
||||
/// `Expectation::Word(opener)` at the cursor, the engine surfaces the
|
||||
/// full composite text as a Tab candidate. Used for multi-token
|
||||
/// fragments the user thinks of as a single phrase:
|
||||
///
|
||||
/// - `1:n` — the opener for `add 1:n relationship`.
|
||||
/// - `double precision` — the lone two-word SQL type alias
|
||||
/// (ADR-0035 §6.3; the grammar has a dedicated branch so the per-word
|
||||
/// `Ident` validator never has to make sense of `double` alone).
|
||||
/// Surfacing it as a composite stops bare `double` from appearing in
|
||||
/// the type candidate list alongside `int`/`text`/etc. (issue #5).
|
||||
/// Source 1's keyword filter drops openers that appear here so the
|
||||
/// composite replaces the bare opener rather than appearing
|
||||
/// alongside it.
|
||||
const COMPOSITE_CANDIDATES: &[(&str, &str)] =
|
||||
&[("1", "1:n"), ("double", "double precision")];
|
||||
|
||||
/// Per-project schema lookup cache (ADR-0022 §9, ADR-0024 §Phase D).
|
||||
///
|
||||
@@ -525,6 +536,11 @@ pub fn candidates_at_cursor_with_in_mode(
|
||||
// Declaration order is preserved (matches the canonical
|
||||
// command shape, e.g. `to` before `table` for
|
||||
// `add column [to] [table] …`).
|
||||
//
|
||||
// Composite openers (e.g. `double` for `double precision`) are
|
||||
// filtered out here so Source 1.6 (the composite pipeline) can
|
||||
// surface the full multi-word candidate without the bare opener
|
||||
// also appearing alongside (issue #5).
|
||||
let mut keywords: Vec<String> = expected
|
||||
.iter()
|
||||
.filter_map(|e| match e {
|
||||
@@ -532,6 +548,7 @@ pub fn candidates_at_cursor_with_in_mode(
|
||||
_ => None,
|
||||
})
|
||||
.filter(|w| !w.is_empty() && w.chars().all(|c| c.is_ascii_alphabetic()))
|
||||
.filter(|w| !COMPOSITE_CANDIDATES.iter().any(|(opener, _)| opener == w))
|
||||
.map(str::to_string)
|
||||
.filter(|name| matches_prefix(name))
|
||||
.collect();
|
||||
|
||||
Reference in New Issue
Block a user