Walker: node-attached HintMode via Node::Hinted (ADR-0024 §HintMode-per-node)

Replaces the hint resolver's signature-matching (does the expected set
contain all five literal forms? an Ident{NewName}?) with a grammar-
declared annotation. New Node::Hinted { mode, inner } wrapper; the
walker records the mode in WalkContext::pending_hint_mode on entry and
clears it on any successful match (cursor moved past the slot — this
also undoes the leak where a failed Hinted branch of a Choice would
otherwise strand a stale mode). The resolver reads pending_hint_mode
directly.

Value-literal fallback slots carry ProseOnly; NewName ident slots carry
ForceProse. hint_mode_at_input_inner now delegates to
hint_resolution_at_input — one resolution path, no duplicated logic.
No behaviour change; the typing-surface matrix guards it.
This commit is contained in:
claude@clouddev1
2026-05-15 21:58:22 +00:00
parent f1ff5970bf
commit 911a537a83
8 changed files with 193 additions and 165 deletions
+52 -22
View File
@@ -14,9 +14,15 @@
use crate::dsl::action::ReferentialAction;
use crate::dsl::command::{ChangeColumnMode, ColumnSpec, Command, RelationshipSelector};
use crate::dsl::grammar::{
CommandNode, IdentSource, Node, ValidationError, Word,
CommandNode, HintMode, IdentSource, Node, ValidationError, Word,
shared::{REFERENTIAL_CLAUSES, TYPE_SLOT, TYPE_VALIDATOR},
};
/// `HintMode` annotation shared by every `NewName` ident slot:
/// the user is inventing a name, so the hint panel forces the
/// "Type a name [then …]" prose rather than offering schema
/// candidates (ADR-0024 §HintMode-per-node).
const NEW_NAME_HINT: HintMode = HintMode::ForceProse("hint.ambient_typing_name");
use crate::dsl::types::Type;
use crate::dsl::walker::outcome::{MatchedKind, MatchedPath};
@@ -24,7 +30,7 @@ use crate::dsl::walker::outcome::{MatchedKind, MatchedPath};
// Building blocks
// =================================================================
const TABLE_NAME_NEW: Node = Node::Ident {
const TABLE_NAME_NEW_IDENT: Node = Node::Ident {
source: IdentSource::NewName,
role: "table_name",
validator: None,
@@ -33,6 +39,10 @@ const TABLE_NAME_NEW: Node = Node::Ident {
writes_column: false,
writes_user_listed_column: false,
};
const TABLE_NAME_NEW: Node = Node::Hinted {
mode: NEW_NAME_HINT,
inner: &TABLE_NAME_NEW_IDENT,
};
// `writes_table: true` so that the column-name slots that
// follow the table name in `drop column` / `rename column` /
@@ -61,7 +71,7 @@ const COLUMN_NAME: Node = Node::Ident {
writes_user_listed_column: false,
};
const COLUMN_NAME_NEW: Node = Node::Ident {
const COLUMN_NAME_NEW_IDENT: Node = Node::Ident {
source: IdentSource::NewName,
role: "column_name",
validator: None,
@@ -70,6 +80,10 @@ const COLUMN_NAME_NEW: Node = Node::Ident {
writes_column: false,
writes_user_listed_column: false,
};
const COLUMN_NAME_NEW: Node = Node::Hinted {
mode: NEW_NAME_HINT,
inner: &COLUMN_NAME_NEW_IDENT,
};
const RELATIONSHIP_NAME: Node = Node::Ident {
source: IdentSource::Relationships,
@@ -81,7 +95,7 @@ const RELATIONSHIP_NAME: Node = Node::Ident {
writes_user_listed_column: false,
};
const RELATIONSHIP_NAME_NEW: Node = Node::Ident {
const RELATIONSHIP_NAME_NEW_IDENT: Node = Node::Ident {
source: IdentSource::NewName,
role: "relationship_name",
validator: None,
@@ -90,6 +104,10 @@ const RELATIONSHIP_NAME_NEW: Node = Node::Ident {
writes_column: false,
writes_user_listed_column: false,
};
const RELATIONSHIP_NAME_NEW: Node = Node::Hinted {
mode: NEW_NAME_HINT,
inner: &RELATIONSHIP_NAME_NEW_IDENT,
};
// `[to]` and `[table]` connectives.
const TO_OPT: Node = Node::Optional(&Node::Word(Word::keyword("to")));
@@ -308,6 +326,20 @@ const ADD_SHAPE: Node = Node::Choice(ADD_CHOICES);
// rename_column — `rename column [in] [table] <T> : <col> to <new>`
// =================================================================
const NEW_COLUMN_NAME_IDENT: Node = Node::Ident {
source: IdentSource::NewName,
role: "new_column_name",
validator: None,
highlight_override: None,
writes_table: false,
writes_column: false,
writes_user_listed_column: false,
};
const NEW_COLUMN_NAME: Node = Node::Hinted {
mode: NEW_NAME_HINT,
inner: &NEW_COLUMN_NAME_IDENT,
};
const RENAME_COLUMN_NODES: &[Node] = &[
Node::Word(Word::keyword("column")),
IN_OPT,
@@ -316,15 +348,7 @@ const RENAME_COLUMN_NODES: &[Node] = &[
Node::Punct(':'),
COLUMN_NAME,
Node::Word(Word::keyword("to")),
Node::Ident {
source: IdentSource::NewName,
role: "new_column_name",
validator: None,
highlight_override: None,
writes_table: false,
writes_column: false,
writes_user_listed_column: false,
},
NEW_COLUMN_NAME,
];
const RENAME_COLUMN: Node = Node::Seq(RENAME_COLUMN_NODES);
@@ -650,16 +674,22 @@ pub static CHANGE: CommandNode = CommandNode {
// (Phase C)
// =================================================================
const COL_NAME_IDENT: Node = Node::Ident {
source: IdentSource::NewName,
role: "col_name",
validator: None,
highlight_override: None,
writes_table: false,
writes_column: false,
writes_user_listed_column: false,
};
const COL_NAME: Node = Node::Hinted {
mode: NEW_NAME_HINT,
inner: &COL_NAME_IDENT,
};
const COL_SPEC_NODES: &[Node] = &[
Node::Ident {
source: IdentSource::NewName,
role: "col_name",
validator: None,
highlight_override: None,
writes_table: false,
writes_column: false,
writes_user_listed_column: false,
},
COL_NAME,
Node::Punct(':'),
Node::Ident {
source: IdentSource::Types,