input_render: schema-aware classify_input for wrong-count value lists

classify_input was schemaless; wrong-count Form B value lists
(`insert into Customers values ('Alice')` against a 3-column table)
showed as Valid until submit. Add classify_input_with_schema that
threads the SchemaCache through parse_command_with_schema and wire
render_input_runs to use it. Schemaless classify_input is kept public
for handoff-11/12 regression tests that exercise schema-independent
positions.
This commit is contained in:
claude@clouddev1
2026-05-15 20:31:01 +00:00
parent 24e641bc21
commit fffb44ff4f
+36 -3
View File
@@ -24,6 +24,7 @@
use ratatui::style::{Modifier, Style}; use ratatui::style::{Modifier, Style};
use crate::dsl::parser::parse_command_with_schema;
use crate::dsl::walker; use crate::dsl::walker;
use crate::dsl::{ParseError, parse_command}; use crate::dsl::{ParseError, parse_command};
use crate::theme::Theme; use crate::theme::Theme;
@@ -66,7 +67,7 @@ pub fn render_input_runs(
cache: &crate::completion::SchemaCache, cache: &crate::completion::SchemaCache,
) -> Vec<StyledRun> { ) -> Vec<StyledRun> {
let mut runs = lex_to_runs(input, theme); let mut runs = lex_to_runs(input, theme);
if let InputState::DefiniteErrorAt(pos) = classify_input(input) { if let InputState::DefiniteErrorAt(pos) = classify_input_with_schema(input, cache) {
overlay_error(&mut runs, pos, theme); overlay_error(&mut runs, pos, theme);
} }
if let Some(inv) = crate::completion::invalid_ident_at_cursor(input, cursor_byte, cache) { if let Some(inv) = crate::completion::invalid_ident_at_cursor(input, cursor_byte, cache) {
@@ -98,13 +99,45 @@ pub enum InputState {
} }
/// Classify `input` into one of the three mid-typing states. /// Classify `input` into one of the three mid-typing states.
/// Cheap (lex + parse) per ADR-0022 §13. ///
/// Schemaless. Wrong-count / wrong-type value-list cases that
/// only the schema-aware parser would catch surface as `Valid`
/// here. For UX-correct classification at typing time prefer
/// [`classify_input_with_schema`] — `render_input_runs` always
/// does. Kept public because handoff-11/12 regression tests use
/// it for schema-independent assertions (cheap, predictable).
///
/// ADR-0022 §13: cheap (lex + parse).
#[must_use] #[must_use]
pub fn classify_input(input: &str) -> InputState { pub fn classify_input(input: &str) -> InputState {
if input.trim().is_empty() { if input.trim().is_empty() {
return InputState::Empty; return InputState::Empty;
} }
match parse_command(input) { classify_parse_result(parse_command(input))
}
/// Schema-aware variant of [`classify_input`].
///
/// Threads the `SchemaCache` through `parse_command_with_schema`
/// so that typed-slot rejections (Phase D — wrong-count Form B
/// value lists, wrong-type column values, etc.) surface as
/// `DefiniteErrorAt`/`IncompleteAtEof` at typing time, before
/// the user submits.
#[must_use]
pub fn classify_input_with_schema(
input: &str,
cache: &crate::completion::SchemaCache,
) -> InputState {
if input.trim().is_empty() {
return InputState::Empty;
}
classify_parse_result(parse_command_with_schema(input, cache))
}
fn classify_parse_result(
result: Result<crate::dsl::Command, ParseError>,
) -> InputState {
match result {
Ok(_) => InputState::Valid, Ok(_) => InputState::Valid,
Err(ParseError::Empty) => InputState::Empty, Err(ParseError::Empty) => InputState::Empty,
Err(err @ ParseError::Invalid { position, .. }) => { Err(err @ ParseError::Invalid { position, .. }) => {