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
+17 -10
View File
@@ -9,7 +9,8 @@
use crate::dsl::command::{AppCommand, Command, MessagesValue, ModeValue};
use crate::dsl::grammar::{
CommandNode, IdentSource, IdentValidator, Node, ValidationError, Word,
CommandNode, HintMode, IdentSource, IdentValidator, Node, ValidationError,
Word,
};
use crate::dsl::walker::outcome::{MatchedKind, MatchedPath};
@@ -43,17 +44,23 @@ const UNKNOWN_MESSAGES_VALIDATOR: IdentValidator = validate_unknown_messages;
const SAVE_AS_WORD: Node = Node::Word(Word::keyword("as"));
const IMPORT_TARGET_IDENT: Node = Node::Ident {
source: IdentSource::NewName,
role: "target",
validator: None,
highlight_override: None,
writes_table: false,
writes_column: false,
writes_user_listed_column: false,
};
const IMPORT_TARGET: Node = Node::Hinted {
mode: HintMode::ForceProse("hint.ambient_typing_name"),
inner: &IMPORT_TARGET_IDENT,
};
const IMPORT_AS_TARGET: Node = Node::Seq(&[
Node::Word(Word::keyword("as")),
Node::Ident {
source: IdentSource::NewName,
role: "target",
validator: None,
highlight_override: None,
writes_table: false,
writes_column: false,
writes_user_listed_column: false,
},
IMPORT_TARGET,
]);
const IMPORT_AS_TARGET_OPT: Node = Node::Optional(&IMPORT_AS_TARGET);