From 4559a131318b933afd9a7db63768b097a496d1d2 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:36:36 -0600 Subject: [PATCH 1/2] Add .gitattributes to control line endings --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..94f480d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file From 2708e8ad85e7e5da945a18eb5e8d581ecabad621 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 5 Feb 2026 15:01:35 -0600 Subject: [PATCH 2/2] fix the unnecessary parens --- src/formatting.rs | 22 +++++++++++++++++-- ...def_with_pipeline_double_parens_issue82.nu | 1 + tests/fixtures/expected/issue76.nu | 2 +- ...def_with_pipeline_double_parens_issue82.nu | 1 + tests/ground_truth.rs | 6 +++++ toolkit.nu | 2 +- 6 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/expected/def_with_pipeline_double_parens_issue82.nu create mode 100644 tests/fixtures/input/def_with_pipeline_double_parens_issue82.nu diff --git a/src/formatting.rs b/src/formatting.rs index d799388..9ffcad4 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -334,7 +334,7 @@ impl<'a> Formatter<'a> { self.format_closure_expression(*block_id, expr.span); } Expr::Subexpression(block_id) => { - self.format_subexpression(*block_id); + self.format_subexpression(*block_id, expr.span); } Expr::List(items) => self.format_list(items), @@ -705,8 +705,15 @@ impl<'a> Formatter<'a> { } /// Format a subexpression - fn format_subexpression(&mut self, block_id: nu_protocol::BlockId) { + fn format_subexpression(&mut self, block_id: nu_protocol::BlockId, span: Span) { let block = self.working_set.get_block(block_id); + // Check if the subexpression has explicit parentheses in the source + let has_explicit_parens = self.source.get(span.start) == Some(&b'(') + && self.source.get(span.end - 1) == Some(&b')'); + if !has_explicit_parens { + self.format_block(block); + return; + } // Special case: subexpressions containing only a string interpolation don't need parentheses if block.pipelines.len() == 1 && block.pipelines[0].elements.len() == 1 { if let Expr::StringInterpolation(_) = &block.pipelines[0].elements[0].expr.expr { @@ -714,6 +721,17 @@ impl<'a> Formatter<'a> { return; } } + // Special case: subexpressions containing a pipeline starting with $in don't need parentheses + if block.pipelines.len() == 1 && !block.pipelines[0].elements.is_empty() { + let first_element = &block.pipelines[0].elements[0]; + let element_text = String::from_utf8_lossy( + &self.source[first_element.expr.span.start..first_element.expr.span.end], + ); + if element_text == "$in" { + self.format_block(block); + return; + } + } self.write("("); let is_simple = block.pipelines.len() == 1 && block.pipelines[0].elements.len() <= 3; diff --git a/tests/fixtures/expected/def_with_pipeline_double_parens_issue82.nu b/tests/fixtures/expected/def_with_pipeline_double_parens_issue82.nu new file mode 100644 index 0000000..f5968dc --- /dev/null +++ b/tests/fixtures/expected/def_with_pipeline_double_parens_issue82.nu @@ -0,0 +1 @@ +def in-lang [ln] { $in | items {|k v| {$k: ($v | get $ln | default ($v | get 'en')) } } | reduce -f {} {|it acc| $acc | merge $it } } diff --git a/tests/fixtures/expected/issue76.nu b/tests/fixtures/expected/issue76.nu index 89ca911..a24f44d 100644 --- a/tests/fixtures/expected/issue76.nu +++ b/tests/fixtures/expected/issue76.nu @@ -1 +1 @@ -def pretty-print-command [] { ($"`(ansi default_dimmed)(ansi default_italic)($in)(ansi reset)`") } \ No newline at end of file +def pretty-print-command [] { $"`(ansi default_dimmed)(ansi default_italic)($in)(ansi reset)`" } \ No newline at end of file diff --git a/tests/fixtures/input/def_with_pipeline_double_parens_issue82.nu b/tests/fixtures/input/def_with_pipeline_double_parens_issue82.nu new file mode 100644 index 0000000..38abf59 --- /dev/null +++ b/tests/fixtures/input/def_with_pipeline_double_parens_issue82.nu @@ -0,0 +1 @@ +def in-lang [ln] { ($in | items {|k v| {$k: ($v | get $ln | default ($v | get 'en')) } } | reduce -f {} {|it acc| $acc | merge $it }) } diff --git a/tests/ground_truth.rs b/tests/ground_truth.rs index 1559a0e..9e4d8bb 100644 --- a/tests/ground_truth.rs +++ b/tests/ground_truth.rs @@ -180,6 +180,12 @@ fn ground_truth_def_statement() { run_ground_truth_test(&test_binary, "def_statement"); } +#[test] +fn ground_truth_def_with_pipeline() { + let test_binary = get_test_binary(); + run_ground_truth_test(&test_binary, "def_with_pipeline_double_parens_issue82"); +} + // ============================================================================ // Ground Truth Tests - Control Flow // ============================================================================ diff --git a/toolkit.nu b/toolkit.nu index aef595e..c627ba4 100644 --- a/toolkit.nu +++ b/toolkit.nu @@ -6,7 +6,7 @@ # (**2**) catch classical flaws in the new changes with *clippy* and (**3**) # make sure all the tests pass. # print the pipe input inside backticks, dimmed and italic, as a pretty command -def pretty-print-command [] { ($"`(ansi default_dimmed)(ansi default_italic)($in)(ansi reset)`") } +def pretty-print-command [] { $"`(ansi default_dimmed)(ansi default_italic)($in)(ansi reset)`" } # check standard code formatting and apply the changes export def fmt [--check, --verbose] { # do not apply the format changes, only check the syntax