Walker + parser: surface mid-typing after separators and Form C/A ambiguity
The typing-surface matrix exposed two bugs the existing 859-test suite missed: walk_repeated: when the separator consumed but the inner item failed at EOF, the old path rolled the separator back and reported a definite error at the rollback position (`insert into T (a, ` flashed red on the `,` after each comma). Now propagates Incomplete with the inner's expected set so the input renderer treats it as mid-typing. build_insert Form C path: `insert into T (col)` walked to a complete match but produced `values: []` because Form C's value collector drops ident-shaped items. The user almost certainly meant Form A and just hasn't typed `values (...)` yet. Reject with a ValidationError naming the Form-A continuation; classify_input now reports IncompleteAtEof. completion_probe / expected_at_input: ValidationFailed used to return an empty expected set, leaving Tab with nothing to offer at the new Form-A flag point. Now surface result.tail_expected (skipped-Optional expectations captured before validation fired) so `values` is still offered as a candidate.
This commit is contained in:
@@ -422,6 +422,36 @@ fn build_insert(path: &MatchedPath) -> Result<Command, ValidationError> {
|
||||
// Form C: the first paren contained the value list. The
|
||||
// Repeated tagged the matched values via their natural
|
||||
// MatchedKind (Word/NumberLit/StringLit); collect them.
|
||||
//
|
||||
// Form-A-without-`values` recovery: the shared
|
||||
// INSERT_PAREN_ITEM choice accepts both VALUE_LITERAL
|
||||
// and Ident{Columns} so that Form A can resolve
|
||||
// column-name items inside its `( cols )` list. When the
|
||||
// user types `insert into T (col)` (column-shaped item,
|
||||
// no `values` keyword), the grammar walks to a complete
|
||||
// match but the user almost certainly meant Form A and
|
||||
// forgot the `values (...)` suffix. Reject here with a
|
||||
// ValidationError — the walker classifies validation
|
||||
// errors as `at_eof: true`, so the input renderer
|
||||
// surfaces this as IncompleteAtEof (mid-typing) rather
|
||||
// than dispatching a logically-broken Form C insert with
|
||||
// an empty value list.
|
||||
let user_listed_columns: Vec<String> = path
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|i| match &i.kind {
|
||||
MatchedKind::Ident {
|
||||
role: "insert_first_item",
|
||||
} => Some(i.text.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
if !user_listed_columns.is_empty() {
|
||||
return Err(ValidationError {
|
||||
message_key: "parse.custom.insert_form_a_missing_values",
|
||||
args: vec![("columns", user_listed_columns.join(", "))],
|
||||
});
|
||||
}
|
||||
let values = collect_values_in_parens(path, first_paren_idx)?;
|
||||
Ok(Command::Insert {
|
||||
table,
|
||||
|
||||
Reference in New Issue
Block a user