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
@@ -134,6 +134,18 @@ pub struct WalkContext<'a> {
/// resolver reads this directly instead of inferring the
/// slot kind from the shape of the expected set.
pub pending_hint_mode: Option<crate::dsl::grammar::HintMode>,
/// Byte spans of the clause-concept regions the walk passed
/// through (issue #37 / ADR clause-concept-hints, D1).
///
/// A `Node::Concept { topic, inner }` wrapper pushes one entry
/// per *committed* inner walk (Matched / Incomplete / Failed —
/// never on NoMatch). Unlike `pending_hint_mode`, this is
/// **append-only and never cleared on match**, so a fully-typed
/// clause keeps its span — that is what lets the F1
/// clause-concept resolver report a concept for a cursor parked
/// *inside* already-typed clause text, not only at the slot
/// boundary.
pub concept_spans: Vec<ConceptSpan>,
/// An `IntroProse` hint captured from an *optional* slot that
/// the walk skipped (issue #26). Unlike `pending_hint_mode`
/// (cleared on the very next match — including the empty match
@@ -227,6 +239,19 @@ pub struct PendingCteHarvest {
pub cte_name_span: (usize, usize),
}
/// A clause-concept region recorded during a walk (issue #37).
///
/// `topic` is the `hint.concept.<topic>` catalogue key; `[start,
/// end]` is the inclusive byte range the clause covered as typed so
/// far (`end` is the matched end on a full match, or the stop
/// position on an incomplete / failed inner).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConceptSpan {
pub topic: &'static str,
pub start: usize,
pub end: usize,
}
impl<'a> WalkContext<'a> {
/// Schemaless walk context — the legacy default used by
/// pre-Phase-D callers and tests that don't care about
@@ -243,6 +268,7 @@ impl<'a> WalkContext<'a> {
pending_value_type: None,
pending_value_column: None,
pending_hint_mode: None,
concept_spans: Vec::new(),
surviving_intro_hint: None,
user_listed_columns: None,
subgrammar_depth: 0,
@@ -266,6 +292,7 @@ impl<'a> WalkContext<'a> {
pending_value_type: None,
pending_value_column: None,
pending_hint_mode: None,
concept_spans: Vec::new(),
surviving_intro_hint: None,
user_listed_columns: None,
subgrammar_depth: 0,