completion: §10.5 qualified-prefix + edit-scenario look-ahead
ADR-0032 §10.5 — at the cursor, an `<ident>.` prefix narrows column candidates to that qualifier's binding columns. Resolves through from_scope aliases first, then table names, then cte_bindings (for `cte_alias.|`). Falls back to the schema cache for DSL paths (`from <Table>.<col>`). Unresolved qualifier → empty column list; the structural error path surfaces the unresolved-prefix message. Look-ahead probe — the "edit an existing query" workflow. When the cursor is mid-projection but FROM exists after the cursor, a second walk on the full input populates from_scope and the column candidates narrow accordingly. Gated on the leading walk producing no scope so cursor-past-FROM positions pay no cost. The full input must parse for this to work; an unparseable mid-edit state falls back to the §10.6 global posture. CompletionProbe now exposes `from_scope` (top-frame table bindings) and `cte_bindings` (union of in-scope CTE bindings, innermost-first dedupe). The walker drains these at the cursor position; the completion engine reads them for qualifier resolution and unqualified narrowing. Test totals: 1415 → 1424 passing (+9: 5 qualified-prefix + 4 look-ahead). Clippy clean.
This commit is contained in:
@@ -241,6 +241,17 @@ pub struct CompletionProbe {
|
||||
/// WHERE-expression operand, which also accepts a column
|
||||
/// reference — ADR-0026 §8).
|
||||
pub pending_hint_mode: Option<crate::dsl::grammar::HintMode>,
|
||||
/// The active `from_scope` at the cursor (top frame on
|
||||
/// the walker's scope stack). Empty when no FROM has been
|
||||
/// reached or the walker is schemaless. Used by the
|
||||
/// completion engine to narrow `cte.|` / `t.|` qualified-
|
||||
/// prefix candidates to a single binding's columns
|
||||
/// (ADR-0032 §10.5).
|
||||
pub from_scope: Vec<context::TableBinding>,
|
||||
/// CTE bindings visible at the cursor across all in-scope
|
||||
/// frames (innermost to outermost). The same source the
|
||||
/// qualified-prefix completion consults for `cte.|` shapes.
|
||||
pub cte_bindings: Vec<context::CteBinding>,
|
||||
}
|
||||
|
||||
/// Run a schema-aware walk and report the completion-engine's
|
||||
@@ -282,6 +293,8 @@ pub fn completion_probe_in_mode(
|
||||
expected: mode_filtered_entries(),
|
||||
current_table_columns: None,
|
||||
pending_hint_mode: None,
|
||||
from_scope: Vec::new(),
|
||||
cte_bindings: Vec::new(),
|
||||
};
|
||||
}
|
||||
let mut ctx = context::WalkContext::with_schema(schema);
|
||||
@@ -292,6 +305,8 @@ pub fn completion_probe_in_mode(
|
||||
expected: mode_filtered_entries(),
|
||||
current_table_columns: None,
|
||||
pending_hint_mode: None,
|
||||
from_scope: Vec::new(),
|
||||
cte_bindings: Vec::new(),
|
||||
};
|
||||
};
|
||||
let expected = match result.outcome {
|
||||
@@ -318,10 +333,35 @@ pub fn completion_probe_in_mode(
|
||||
// position.
|
||||
outcome::WalkOutcome::ValidationFailed { .. } => result.tail_expected,
|
||||
};
|
||||
// Snapshot the cursor's lexical scope: top frame's
|
||||
// from_scope and the union of every frame's cte_bindings
|
||||
// (innermost first so a shadowing inner CTE wins on name
|
||||
// collision per ADR-0032 §10.3).
|
||||
let (from_scope, cte_bindings) = {
|
||||
let top_from = ctx
|
||||
.from_scope_stack
|
||||
.last()
|
||||
.map(|f| f.from_scope.clone())
|
||||
.unwrap_or_default();
|
||||
let mut ctes: Vec<context::CteBinding> = Vec::new();
|
||||
for frame in ctx.from_scope_stack.iter().rev() {
|
||||
for binding in &frame.cte_bindings {
|
||||
if !ctes
|
||||
.iter()
|
||||
.any(|c| c.name.eq_ignore_ascii_case(&binding.name))
|
||||
{
|
||||
ctes.push(binding.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
(top_from, ctes)
|
||||
};
|
||||
CompletionProbe {
|
||||
expected,
|
||||
current_table_columns: ctx.current_table_columns,
|
||||
pending_hint_mode: ctx.pending_hint_mode,
|
||||
from_scope,
|
||||
cte_bindings,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user