feat: ADR-0036 Phase 3b — live typed-slot hints + highlighting for INSERT VALUES

Give each positional INSERT VALUES position its column identity so a lone
literal gets the column-typed slot (live per-column hint + mismatch
highlight) and any expression falls through to sql_expr — completing the
typed-DML-values feature for the INSERT surface (single/multi-row, Form A
and Form B).

New zero-width Node::SetColumn(&TableColumn) primitive establishes the
active column for the value position that follows (sets current_column +
pending_value_column, like an Ident{writes_column} but without consuming
input); a DynamicSubgrammar emits SetColumn(col) + the shared SET_VALUE
per position. Column mapping mirrors do_sql_insert: Form A → listed
columns; Form B → all columns in declaration order (advanced-mode Form B
auto-fills nothing; an omitted shortid in Form A is auto-filled and has no
VALUES position).

Reconcile with the per-tuple arity diagnostic (ADR-0033 §8.1): a
fixed-length typed Seq would reject wrong-arity tuples and suppress that
post-walk diagnostic, so the tuple value list is an arity-gating lookahead
— a correct-arity tuple uses the typed Seq; a wrong-arity tuple keeps the
type-blind sql_expr repeat so §8.1 fires unchanged. Correct-arity tuples
get full live feedback, including a wrong-kind literal like 'text' into an
int column.

Records ADR-0036 Amendment 1 (Phase 3b detail + the arity reconciliation);
ADR-0036 is now fully implemented.

Tests: 1947 passing (+8), 0 failed, 0 skipped, 1 ignored; clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-27 07:22:44 +00:00
parent 49ea03b0d5
commit 8906661f69
6 changed files with 396 additions and 20 deletions
+13
View File
@@ -250,6 +250,19 @@ fn walk_node_inner(
Box::leak(Box::new(factory(ctx, source, pos)));
walk_node(source, pos, resolved, ctx, path, per_byte)
}
Node::SetColumn(col) => {
// ADR-0036 Phase 3b: zero-width — establish the active
// column for the value position that follows, exactly as an
// `Ident { writes_column: true }` would (current_column for
// the typed slot's dispatch; pending_value_column for the
// hint's "for `col`:" framing), but without consuming a
// column identifier (VALUES positions are positional). The
// following `SET_VALUE` slot reads `current_column`.
let col: &crate::completion::TableColumn = col;
ctx.current_column = Some(col.clone());
ctx.pending_value_column = Some(col.name.clone());
NodeWalkResult::Matched { end: pos, skipped: Vec::new() }
}
Node::TypedValueSlot {
ty,
column_name,