Skip to content

Fix minimal input backend to handle multi-line continuation#944

Open
Osso wants to merge 5 commits intoreubeno:mainfrom
Osso:fix-multiline-stdin
Open

Fix minimal input backend to handle multi-line continuation#944
Osso wants to merge 5 commits intoreubeno:mainfrom
Osso:fix-multiline-stdin

Conversation

@Osso
Copy link
Copy Markdown

@Osso Osso commented Jan 19, 2026

When reading from a non-terminal stdin, the minimal input backend now properly handles incomplete commands that span multiple lines (e.g., commands ending with && or ||).

Previously, the backend read a single line and passed it directly to the parser, which failed with "syntax error at end of input" for incomplete commands like echo a && followed by echo b on the next line.

Now the backend:

  1. Reads lines in a loop
  2. Checks if the accumulated input parses completely
  3. If incomplete (ParsingAtEndOfInput), reads more lines
  4. Shows continuation prompt when in terminal mode

Added tests for && continuation, || continuation, and complex multi-line patterns with pipes via stdin.

When reading from a non-terminal stdin, the minimal input backend now
properly handles incomplete commands that span multiple lines (e.g.,
commands ending with && or ||).

Previously, the backend read a single line and passed it directly to
the parser, which failed with "syntax error at end of input" for
incomplete commands like "echo a &&\necho b".

Now the backend:
1. Reads lines in a loop
2. Checks if the accumulated input parses completely
3. If incomplete (ParsingAtEndOfInput), reads more lines
4. Shows continuation prompt when in terminal mode

This matches the behavior of the reedline backend which uses a
validator to detect incomplete input.

Fixes: Commands like `echo a &&` followed by `echo b` on the next line
now work correctly when piped to brush.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Jan 19, 2026

Performance Benchmark Report

Benchmark name Baseline (μs) Test/PR (μs) Delta (μs) Delta %
clone_shell_object 16.97 μs 16.95 μs -0.02 μs ⚪ Unchanged
eval_arithmetic 0.15 μs 0.14 μs -0.01 μs ⚪ Unchanged
expand_one_string 1.60 μs 1.53 μs -0.06 μs ⚪ Unchanged
for_loop 22.69 μs 22.30 μs -0.38 μs 🟢 -1.68%
function_call 2.11 μs 2.10 μs -0.02 μs ⚪ Unchanged
instantiate_shell 52.42 μs 52.19 μs -0.23 μs ⚪ Unchanged
instantiate_shell_with_init_scripts 24437.20 μs 24232.98 μs -204.22 μs ⚪ Unchanged
parse_bash_completion 2002.51 μs 2022.46 μs 19.94 μs 🟠 +1.00%
parse_sample_script 1.94 μs 1.96 μs 0.01 μs ⚪ Unchanged
run_echo_builtin_command 15.08 μs 15.03 μs -0.05 μs ⚪ Unchanged
tokenize_sample_script 3.37 μs 3.37 μs -0.00 μs ⚪ Unchanged

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
brush-core/src/expansion.rs 🟢 94.25% 🟢 94.33% 🟢 0.08%
brush-core/src/jobs.rs 🟠 51.61% 🟠 50.23% 🔴 -1.38%
brush-core/src/results.rs 🟢 80.16% 🟢 83.33% 🟢 3.17%
brush-interactive/src/minimal/input_backend.rs 🔴 0% 🟠 59.21% 🟢 59.21%
brush-test-harness/src/execution.rs 🟢 80.75% 🟢 79.14% 🔴 -1.61%
Overall Coverage 🟢 74.66% 🟢 74.72% 🟢 0.06%

Minimum allowed coverage is 70%, this run produced 74.72%

Test Summary: bash-completion test suite

Outcome Count Percentage
✅ Pass 1558 73.87
❗️ Error 18 0.85
❌ Fail 179 8.49
⏩ Skip 339 16.07
❎ Expected Fail 13 0.62
✔️ Unexpected Pass 2 0.09
📊 Total 2109 100.00

@reubeno
Copy link
Copy Markdown
Owner

reubeno commented Jan 19, 2026

Thanks for your contribution, @Osso!

Our minimal input backend is intended to be very, well, minimal 🙂. I'm curious to know if you actually ran into this while using it or if it was more something you identified while reading or analyzing the code? Regardless of which, we're open to some minor changes to it as long as it stays relatively simple.

For the new tests you've added, did you confirm they ran against the minimal backend? I don't see any command-line arguments specifying that. One great way to confirm could be to check they fail without the fix.

I'd also encourage you to look over the tests and see if there are some ways you could reduce the repeated logic across them. The separate cases are mostly doing the same thing but with different inputs + result expectations. Perhaps you could extract out the common logic or convert them into a data/table driven test of some kind?

@Osso
Copy link
Copy Markdown
Author

Osso commented Jan 19, 2026

Yes I have been using a modified version of brush (alias handling, history handling, ...) as my default shell for about about a week and the minimal backend is being used while piping. There no need command line arguments as it is the default behavior: maybe that's the real problem? should it be using a different backend while piping?

If you are not aiming for that usage we can close this merge request.

Yes I can cleanup the tests.

@reubeno
Copy link
Copy Markdown
Owner

reubeno commented Jan 19, 2026

Ah, interesting. So you're still using the shell interactively but via a pipe? I think understand a bit better. I'm okay proceeding with the change after you clean up the tests.

Very cool to hear you've been able to build something that extends brush! If you're willing to share, it would be great to learn more. Perhaps you could show-and-tell in our Discussions area or on our Discord (if you're open to it)?

@Osso
Copy link
Copy Markdown
Author

Osso commented Jan 19, 2026

Right I didn't show an example. What I am doing is this:

  ```console
  $ printf 'echo hello &&\necho world\n' | /tmp/claude/brush -s
  error: main: syntax error at end of input

  $ printf 'echo hello &&\necho world\n' | /tmp/claude/brush-fixed -s
  hello
  world

  $ printf 'echo hello &&\necho world\n' | bash -s
  hello
  world

Osso added 3 commits January 20, 2026 02:28
- Consolidate three separate tests into one with TestCase struct
- Explicitly specify --input-backend=minimal to clarify test target
Extract read_line_from<R: BufRead>() method that accepts any reader,
allowing tests to call it directly with a Cursor instead of spawning
a subprocess. Tests now verify the backend's line accumulation logic
without process overhead.
@Osso
Copy link
Copy Markdown
Author

Osso commented Jan 20, 2026

Ok I updated the tests first by factoring them then I changed them to call minimal backend directly in the latest commit. Let me know which one your prefer.

@reubeno
Copy link
Copy Markdown
Owner

reubeno commented Jan 22, 2026

Thanks! I should be able to properly review this weekend. In the meantime I see 2 checks failing on the PR from non-Unix builds. Would you be able to look at them?

@reubeno reubeno added the state: updates requested Pull requests with updates requested label Jan 23, 2026
On WASM targets, tokio::task::block_in_place is not available
(no multi-thread runtime), so we fall back to treating every line
as complete (no multi-line continuation support on WASM).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state: updates requested Pull requests with updates requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants