2g: advanced-mode highlight + engine.* wiring + matrix tests

Cross-cut verification matrix for ADR-0032 Phase 2 is now fully
populated with concrete test references — every row green. Filling
the matrix surfaced three real gaps that this commit closes.

1. Advanced-mode syntax highlighting (ADR-0030 §8 matrix row).
   The `ui.rs` Advanced branch routed through `plain_input_spans`,
   bypassing the highlight walker entirely. In production SQL
   keywords past the entry word rendered as plain identifiers.
   Fix: mode-aware variants of `highlight_runs`,
   `render_input_runs`, `lex_to_runs`, and `input_diagnostics`;
   the Advanced render path now uses the highlighted form with
   `Mode::Advanced`. `plain_input_spans` removed (unused).

2. Engine.* key wiring (ADR-0032 §11.4 / §13 matrix rows + handoff
   §3.3 follow-up). The four Phase-2 engine.* catalog entries
   were authored in 2d but never reached: `translate_generic`
   discarded the engine message and returned a vague catalog
   entry. Fix: pattern-match the engine message text for the four
   Phase-2 categories (aggregate misuse, group-by required,
   compound arity mismatch fallback, scalar-subquery cardinality)
   inside `translate_generic`, routing each to its engine-neutral
   catalog entry.

3. Matrix-coverage tests. Thirteen new tests covering the rows
   that had no explicit coverage:
   - 3 SQL keyword/operator/CASE highlight tests
   - 4 engine.* engine-message tests
   - 3 sql_expr column-completion tests (WHERE, HAVING)
   - 3 predicate-warning slot tests (CASE, ORDER BY, projection)
   - 1 all-10-playground-types recovery test (tests/sql_select.rs)

Plan document (docs/plans/20260520-adr-0032-phase-2.md) updated:
every (TBD) row in the cross-cut matrix replaced with a concrete
test file::function reference and a green status marker.

Test totals: 1428 → 1441 passing (+13 new). Clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-20 21:38:08 +00:00
parent ee0dafd86b
commit ed881eea59
8 changed files with 456 additions and 96 deletions
+15 -40
View File
@@ -655,25 +655,25 @@ fn render_input_panel(app: &App, theme: &Theme, frame: &mut Frame<'_>, area: Rec
// inverted so the cursor is visible without enabling a real
// terminal cursor.
//
// Simple-mode input gets per-token colouring (ADR-0022 §3)
// via input_render::render_input_runs. Advanced-mode input
// — DSL lexer doesn't speak SQL — renders plain (§12), with
// the same before/under/after cursor shape we always had.
// Per-token colouring (ADR-0022 §3 / ADR-0030 §8) in both
// modes — `render_input_runs_in_mode` runs the highlight
// walker with the active mode so SQL keywords / operators /
// CASE / function calls colour correctly in Advanced mode.
let cursor = app.input_cursor.min(app.input.len());
let spans = match effective {
EffectiveMode::Simple => {
let runs = crate::input_render::render_input_runs(
&app.input,
cursor,
theme,
&app.schema_cache,
);
runs_to_spans(&app.input, &runs)
}
let mode_for_render = match effective {
EffectiveMode::Simple => crate::mode::Mode::Simple,
EffectiveMode::AdvancedPersistent | EffectiveMode::AdvancedOneShot => {
plain_input_spans(&app.input, cursor, theme)
crate::mode::Mode::Advanced
}
};
let runs = crate::input_render::render_input_runs_in_mode(
&app.input,
cursor,
theme,
&app.schema_cache,
mode_for_render,
);
let spans = runs_to_spans(&app.input, &runs);
// ADR-0027 §4: the rightmost six columns of the input row
// (a five-column label plus a one-column gap) are reserved
// unconditionally, so the text area is always
@@ -725,31 +725,6 @@ fn runs_to_spans<'a>(
.collect()
}
/// Plain (no token highlighting) input rendering for advanced
/// mode. Same before/under/after cursor shape as the
/// pre-ADR-0022 input panel; here as a deliberate fallback.
fn plain_input_spans<'a>(input: &'a str, cursor: usize, theme: &Theme) -> Vec<Span<'a>> {
let cursor = cursor.min(input.len());
let before = &input[..cursor];
let (under, after) = if cursor < input.len() {
let mut end = cursor + 1;
while end < input.len() && !input.is_char_boundary(end) {
end += 1;
}
(&input[cursor..end], &input[end..])
} else {
(" ", "")
};
vec![
Span::styled(before, Style::default().fg(theme.fg)),
Span::styled(
under,
Style::default().fg(theme.fg).add_modifier(Modifier::REVERSED),
),
Span::styled(after, Style::default().fg(theme.fg)),
]
}
fn render_hint_panel(app: &App, theme: &Theme, frame: &mut Frame<'_>, area: Rect) {
let block = Block::default()
.borders(Borders::ALL)