feat(hint): clause-concept hints layered on F1 (issue #37)

Add a hint.concept.* tier-3 layer surfaced when the cursor sits inside a
recognized clause (referential actions, 1:n/m:n cardinality, primary key,
unique, check, foreign key), layered beneath the per-form block. New
Node::Concept grammar wrapper records clause byte-spans; concept_topic_at_cursor
resolves the innermost containing span. Examples are mode-keyed so they stay
syntax-correct in both simple and advanced mode. Draft ADR (number at merge).
This commit is contained in:
claude@clouddev1
2026-06-23 13:24:54 +00:00
parent e845a6ee72
commit 208da81108
13 changed files with 1231 additions and 20 deletions
+27
View File
@@ -290,6 +290,33 @@ fn walk_node_inner(
ctx.pending_hint_mode = Some(*mode);
walk_node(source, pos, inner, ctx, path, per_byte)
}
Node::Concept { topic, inner } => {
// Issue #37 / ADR clause-concept-hints D1. Walk the
// inner clause, then record its covered byte span in
// `ctx.concept_spans` iff the inner *committed* — i.e.
// matched, ran out mid-clause (Incomplete), or hit a
// hard failure after engaging (Failed). On `NoMatch`
// the node never engaged (e.g. a `Choice` tried this
// branch and it didn't apply), so record nothing — no
// stale span. `start` is the post-whitespace position
// the walk wrapper already resolved.
let result = walk_node(source, pos, inner, ctx, path, per_byte);
let end = match &result {
NodeWalkResult::Matched { end, .. } => Some(*end),
NodeWalkResult::Incomplete { position, .. }
| NodeWalkResult::Failed { position, .. } => Some(*position),
NodeWalkResult::NoMatch { .. } => None,
};
if let Some(end) = end {
ctx.concept_spans
.push(crate::dsl::walker::context::ConceptSpan {
topic,
start: pos,
end,
});
}
result
}
Node::Flag(name) => walk_flag(source, pos, name, path, per_byte),
Node::Repeated {
inner,