app: mode-threaded completion, overlay, and validity indicator
The dispatch-layer mode gate (previous commit) made the submit behaviour correct — `select` runs in advanced mode and shows the SQL hint in simple mode. This commit extends that gating to the ambient assistance layer so simple-mode users do not see SQL leak through Tab completion, the live error overlay, or the `[ERR]`/`[WRN]` validity indicator either. `_in_mode` walker variants -------------------------- - `completion_probe_in_mode`, `expected_at_input_in_mode`, `input_verdict_in_mode`. Each sets `ctx.mode` before walking. The empty-input / unknown-entry fallback in `completion_probe` and `expected_at_input` filters the `REGISTRY` listing by `is_advanced_only` so Tab does not offer `select` in simple mode. Old signatures keep delegating to `Mode::Advanced` (back-compat for tests + other callers). `_in_mode` completion variants ------------------------------ - `candidates_at_cursor_in_mode`, `candidates_at_cursor_with_in_mode`. Internally they route the `parse_command` completeness probe through `parse_command_in_mode(input, mode)`, the `completion_probe` call through `completion_probe_in_mode`, and the `expected_at` fallback through `expected_at_input_in_mode`. Old signatures default to `Mode::Advanced`. `EffectiveMode::as_mode` ------------------------ - Collapses the persistent / one-shot distinction the UI cares about into the plain `Mode` the walker reads from `WalkContext::mode`. App-level call sites that thread mode into the walker chain use this. App / input-render wiring ------------------------- - `App::input_validity_verdict` runs only when effective mode is plain `Simple` (per ADR-0027), so it hardcodes `Mode::Simple` into the new `input_verdict_in_mode` call rather than threading. - `App::start_or_complete_at` / `_last` (the Tab handlers) pass `self.effective_mode().as_mode()` into `candidates_at_cursor_in_mode`, so a `:` one-shot or persistent advanced gives full SQL completion, persistent simple does not offer SQL. - `input_render::render_input_runs` and `ambient_hint` are invoked from `ui.rs` only when effective mode is plain `Simple` (advanced rendering uses `plain_input_spans` and skips ambient hinting per ADR-0022 §12). Their internal `classify_input_with_schema` / `candidates_at_cursor` / `parse_command` calls now go through the mode-aware variants with `Mode::Simple` hardcoded — a SQL form in simple mode surfaces as a definite-error overlay and the hint panel does not offer it. After this commit a simple-mode user typing `select` or `sel<Tab>` sees nothing SQL-shaped: no live highlight, no Tab completion candidate, the `[ERR]` indicator lit, and the on- submit hint that names the recovery paths. An advanced-mode user or a `:` one-shot sees the full SQL surface.
This commit is contained in:
+25
-3
@@ -110,6 +110,18 @@ impl EffectiveMode {
|
||||
pub const fn is_advanced(self) -> bool {
|
||||
matches!(self, Self::AdvancedPersistent | Self::AdvancedOneShot)
|
||||
}
|
||||
|
||||
/// Collapse the persistent/one-shot distinction the UI cares
|
||||
/// about into the plain [`Mode`] the walker reads from
|
||||
/// `WalkContext::mode` (ADR-0030 §2). Both advanced variants
|
||||
/// map to `Mode::Advanced`.
|
||||
#[must_use]
|
||||
pub const fn as_mode(self) -> Mode {
|
||||
match self {
|
||||
Self::Simple => Mode::Simple,
|
||||
Self::AdvancedPersistent | Self::AdvancedOneShot => Mode::Advanced,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -378,7 +390,15 @@ impl App {
|
||||
if !matches!(self.effective_mode(), EffectiveMode::Simple) {
|
||||
return None;
|
||||
}
|
||||
crate::dsl::walker::input_verdict(&self.input, Some(&self.schema_cache))
|
||||
// ADR-0030 §2: the indicator is shown only in plain
|
||||
// simple mode (the guard above), so the verdict reads
|
||||
// the simple-mode walker view — a SQL form lights up
|
||||
// ERROR via the walker's mode gate.
|
||||
crate::dsl::walker::input_verdict_in_mode(
|
||||
&self.input,
|
||||
Some(&self.schema_cache),
|
||||
Mode::Simple,
|
||||
)
|
||||
}
|
||||
|
||||
/// Process one event from the runtime, mutating state and
|
||||
@@ -782,10 +802,11 @@ impl App {
|
||||
|
||||
fn start_or_complete_at(&mut self, multi_start_idx: usize) {
|
||||
let cursor = self.input_cursor.min(self.input.len());
|
||||
let Some(comp) = crate::completion::candidates_at_cursor(
|
||||
let Some(comp) = crate::completion::candidates_at_cursor_in_mode(
|
||||
&self.input,
|
||||
cursor,
|
||||
&self.schema_cache,
|
||||
self.effective_mode().as_mode(),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
@@ -799,10 +820,11 @@ impl App {
|
||||
|
||||
fn start_or_complete_last(&mut self) {
|
||||
let cursor = self.input_cursor.min(self.input.len());
|
||||
let Some(comp) = crate::completion::candidates_at_cursor(
|
||||
let Some(comp) = crate::completion::candidates_at_cursor_in_mode(
|
||||
&self.input,
|
||||
cursor,
|
||||
&self.schema_cache,
|
||||
self.effective_mode().as_mode(),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user