Skip to content

Improve IFS support#988

Open
lu-zero wants to merge 3 commits intoreubeno:mainfrom
lu-zero:improve-IFS-support
Open

Improve IFS support#988
lu-zero wants to merge 3 commits intoreubeno:mainfrom
lu-zero:improve-IFS-support

Conversation

@lu-zero
Copy link
Copy Markdown
Contributor

@lu-zero lu-zero commented Feb 11, 2026

I tried to use brush(winnow) to parse the portage tree and this is one of the issue found and solved.

@lu-zero lu-zero force-pushed the improve-IFS-support branch from e6f6fe9 to 899cb67 Compare February 11, 2026 23:05
@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 11, 2026

Performance Benchmark Report

Benchmark name Baseline (μs) Test/PR (μs) Delta (μs) Delta %
clone_shell_object 17.26 μs 17.78 μs 0.52 μs 🟠 +3.00%
eval_arithmetic 0.15 μs 0.15 μs 0.00 μs ⚪ Unchanged
expand_one_string 1.60 μs 1.62 μs 0.02 μs ⚪ Unchanged
for_loop 22.61 μs 22.48 μs -0.12 μs ⚪ Unchanged
full_peg_complex 58.09 μs 58.69 μs 0.59 μs ⚪ Unchanged
full_peg_for_loop 6.22 μs 6.24 μs 0.01 μs ⚪ Unchanged
full_peg_nested_expansions 16.56 μs 16.59 μs 0.04 μs ⚪ Unchanged
full_peg_pipeline 4.21 μs 4.26 μs 0.05 μs 🟠 +1.14%
full_peg_simple 1.81 μs 1.82 μs 0.01 μs ⚪ Unchanged
function_call 2.08 μs 2.23 μs 0.15 μs ⚪ Unchanged
instantiate_shell 54.77 μs 53.91 μs -0.86 μs 🟢 -1.57%
instantiate_shell_with_init_scripts 27055.46 μs 27787.62 μs 732.15 μs ⚪ Unchanged
parse_peg_bash_completion 2073.80 μs 2096.89 μs 23.09 μs ⚪ Unchanged
parse_peg_complex 19.91 μs 18.84 μs -1.07 μs 🟢 -5.35%
parse_peg_for_loop 1.99 μs 1.98 μs -0.01 μs ⚪ Unchanged
parse_peg_pipeline 2.14 μs 2.10 μs -0.04 μs 🟢 -2.10%
parse_peg_simple 1.10 μs 1.12 μs 0.02 μs ⚪ Unchanged
run_echo_builtin_command 16.35 μs 17.00 μs 0.64 μs 🟠 +3.94%
tokenize_sample_script 3.47 μs 3.53 μs 0.06 μs ⚪ Unchanged

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
brush-core/src/completion.rs 🟠 69.94% 🟠 72.2% 🟢 2.26%
brush-core/src/expansion.rs 🟢 94.62% 🟢 94.64% 🟢 0.02%
brush-core/src/pathsearch.rs 🔴 28.13% 🟢 88.24% 🟢 60.11%
brush-core/src/results.rs 🟢 83.33% 🟢 80.16% 🔴 -3.17%
brush-core/src/shell/fs.rs 🟢 90.18% 🟢 98.21% 🟢 8.03%
Overall Coverage 🟢 74.6% 🟢 74.91% 🟢 0.31%

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

Test Summary: bash-completion test suite

Outcome Count Percentage
✅ Pass 1565 74.21
❗️ Error 19 0.90
❌ Fail 171 8.11
⏩ Skip 339 16.07
❎ Expected Fail 13 0.62
✔️ Unexpected Pass 2 0.09
📊 Total 2109 100.00

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves IFS (Internal Field Separator) support in brush, specifically fixing the handling of empty vs. unset IFS values. This is critical for bash compatibility, as empty IFS should disable field splitting while unset IFS should use the default " \t\n" splitting behavior. The fix was discovered while using brush to parse the portage tree.

Changes:

  • Fixed environment variable retrieval to distinguish between empty and unset IFS values
  • Refactored brace expansion to return a vector of words instead of joining them with spaces, preserving field boundaries
  • Added 20 comprehensive test cases covering empty/unset IFS behavior across various expansion types

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
brush-shell/tests/cases/compat/ifs.yaml Added 20 test cases covering empty/unset IFS with variable expansion, brace expansion, command substitution, arithmetic expansion, and mixed cases
brush-core/src/expansion.rs Refactored brace_expand_if_needed to brace_expand_to_words returning Vec instead of joined string; updated basic_expand to process each brace-expanded word separately; updated unit tests
brush-core/src/env.rs Changed get_str from map(to_cow_str) to and_then(try_get_cow_str) to distinguish empty string from unset values

Copy link
Copy Markdown
Owner

@reubeno reubeno left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments... but apart from them I think I understand what this is about and why it would fix some cases. Could you update the PR description or commits with a brief summary of what the actual issue(s) were? I want to make sure I really understand, and it will also be useful for composing the merge commit. (Your original commit for the env fix had a great description. The other commit was a bit more sparse in its details.)

for piece in brush_parser::word::parse(brace_expanded.as_ref(), &self.parser_options)? {
let piece_expansion = self.expand_word_piece(piece.piece).await?;
expansions.push(piece_expansion);
// When there's no actual brace expansion (single word), preserve all expansion
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "when there's no actual brace expansion" but it and the code below only checks for how many words came back. {1..1} is a concrete example where brace expansion does happen but only a single word is returned (i.e., 1).

Fixes IFS handling when unset in local scope and potentially makes the
HOME fallback more correct.

The get_str method was incorrectly converting unset variables to empty strings
instead of returning None. This caused issues with IFS handling where unset
IFS variables should fall back to default behavior but were instead treated
as empty IFS.
… tracking

Add test exercising the corner cases.
@lu-zero lu-zero force-pushed the improve-IFS-support branch from 899cb67 to d572e33 Compare February 18, 2026 19:43
@reubeno reubeno added the state: needs review PR or issue author is waiting for a review label Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state: needs review PR or issue author is waiting for a review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants