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:
@@ -497,6 +497,12 @@ fn walk_repeated(
|
||||
loop {
|
||||
let saved_path_len = path.items.len();
|
||||
let saved_byte_len = per_byte.len();
|
||||
// Track whether the separator successfully consumed
|
||||
// before the inner attempt. Used below to distinguish
|
||||
// "user typed `,` then stopped at EOF — mid-typing the
|
||||
// next item" from "list naturally ended at the inner
|
||||
// boundary".
|
||||
let mut sep_consumed_to: Option<usize> = None;
|
||||
let result = if count == 0 {
|
||||
walk_node(source, cur, inner, ctx, path, per_byte)
|
||||
} else if let Some(sep) = separator {
|
||||
@@ -504,6 +510,7 @@ fn walk_repeated(
|
||||
let sep_saved_byte = per_byte.len();
|
||||
match walk_node(source, cur, sep, ctx, path, per_byte) {
|
||||
NodeWalkResult::Matched { end, .. } => {
|
||||
sep_consumed_to = Some(end);
|
||||
walk_node(source, end, inner, ctx, path, per_byte)
|
||||
}
|
||||
NodeWalkResult::NoMatch { .. } => {
|
||||
@@ -521,7 +528,31 @@ fn walk_repeated(
|
||||
cur = end;
|
||||
count += 1;
|
||||
}
|
||||
NodeWalkResult::NoMatch { expected, .. } => {
|
||||
NodeWalkResult::NoMatch { expected, position: inner_pos } => {
|
||||
// Mid-typing-the-next-item recovery: if the
|
||||
// separator just consumed and the inner failed
|
||||
// at EOF, the user is partway through typing the
|
||||
// next item — propagate as Incomplete so the
|
||||
// outer walker classifies the input as
|
||||
// mid-typing rather than rolling the separator
|
||||
// back and producing a structural Mismatch at
|
||||
// the separator position.
|
||||
//
|
||||
// Without this branch, `insert into T (a, ` at
|
||||
// EOF would roll back the `,`, then the outer
|
||||
// `(`-list expected `)` at `cur`, see the
|
||||
// separator instead, and report a definite
|
||||
// error at the separator. Real users hit this
|
||||
// every time they type a comma and pause.
|
||||
if let Some(post_sep) = sep_consumed_to {
|
||||
let post_ws = skip_whitespace(source, post_sep);
|
||||
if post_ws >= source.len() {
|
||||
return NodeWalkResult::Incomplete {
|
||||
position: inner_pos,
|
||||
expected,
|
||||
};
|
||||
}
|
||||
}
|
||||
path.items.truncate(saved_path_len);
|
||||
per_byte.truncate(saved_byte_len);
|
||||
last_expected = Some(expected);
|
||||
|
||||
Reference in New Issue
Block a user