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:
+21
-4
@@ -24,7 +24,8 @@
|
||||
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
|
||||
use crate::dsl::parser::parse_command_with_schema;
|
||||
use crate::dsl::parser::{parse_command_in_mode, parse_command_with_schema, parse_command_with_schema_in_mode};
|
||||
use crate::mode::Mode;
|
||||
use crate::dsl::walker;
|
||||
use crate::dsl::{ParseError, parse_command};
|
||||
use crate::theme::Theme;
|
||||
@@ -67,7 +68,15 @@ pub fn render_input_runs(
|
||||
cache: &crate::completion::SchemaCache,
|
||||
) -> Vec<StyledRun> {
|
||||
let mut runs = lex_to_runs(input, theme);
|
||||
if let InputState::DefiniteErrorAt(pos) = classify_input_with_schema(input, cache) {
|
||||
// `render_input_runs` is invoked from `ui.rs` only when the
|
||||
// effective mode is plain `Simple` (advanced rendering uses
|
||||
// `plain_input_spans`), so the classification must use the
|
||||
// simple-mode walker view (ADR-0030 §2): a SQL form here
|
||||
// surfaces as a definite error overlay, consistent with the
|
||||
// dispatch path's "this is SQL" hint on submit.
|
||||
if let InputState::DefiniteErrorAt(pos) =
|
||||
classify_parse_result(parse_command_with_schema_in_mode(input, cache, Mode::Simple))
|
||||
{
|
||||
overlay_error(&mut runs, pos, theme);
|
||||
}
|
||||
if let Some(inv) = crate::completion::invalid_ident_at_cursor(input, cursor_byte, cache) {
|
||||
@@ -320,7 +329,13 @@ pub fn ambient_hint(
|
||||
// Candidates win when any exist — the panel surfaces them
|
||||
// directly because they're more actionable than prose
|
||||
// framings.
|
||||
if let Some(comp) = crate::completion::candidates_at_cursor(input, cursor, cache) {
|
||||
// `ambient_hint` is only called for `EffectiveMode::Simple`
|
||||
// (ui.rs gates it), so completion runs through the
|
||||
// simple-mode walker view — `select` does not surface here
|
||||
// (ADR-0030 §2).
|
||||
if let Some(comp) =
|
||||
crate::completion::candidates_at_cursor_in_mode(input, cursor, cache, Mode::Simple)
|
||||
{
|
||||
return Some(AmbientHint::Candidates {
|
||||
items: comp.candidates,
|
||||
selected: None,
|
||||
@@ -348,7 +363,9 @@ pub fn ambient_hint(
|
||||
)));
|
||||
}
|
||||
// Otherwise fall back to the prose framings from stage 5.
|
||||
match parse_command(input) {
|
||||
// ADR-0030 §2: simple-mode hint uses the simple-mode walker
|
||||
// view so a SQL form surfaces with the "this is SQL" hint.
|
||||
match parse_command_in_mode(input, Mode::Simple) {
|
||||
Ok(_) => Some(AmbientHint::Prose(crate::t!("hint.ambient_complete"))),
|
||||
Err(ParseError::Empty) => None,
|
||||
Err(ParseError::Invalid {
|
||||
|
||||
Reference in New Issue
Block a user