Repository: posit-dev/air (rust)

Issue:
Parentheses should hug expanded expressions tightly
Currently:

```r
(
    1 +
        2
)
```

Should be:

```r
(1 +
    2)
```

Pull request description:
Tightly hug in parenthesized expressions
Closes #248 

Co-authored-by: Lionel Henry <lionel.hry@proton.me>

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f828f3..8c8cba1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 # Development version
 
+- Parenthesized expressions now tightly hug (#248).
+
 - We now allow up to 2 lines between top-level elements of a file. This makes it possible to separate long scripts into visually distinct sections (#40).
 
 - Unary formulas (i.e. anonymous functions) like `~ .x + 1` now add a space between the `~` and the right-hand side, unless the right-hand side is very simple, like `~foo` or `~1` (#235).
diff --git a/crates/air_r_formatter/src/r/auxiliary/parenthesized_expression.rs b/crates/air_r_formatter/src/r/auxiliary/parenthesized_expression.rs
index b99db92..d001de6 100644
--- a/crates/air_r_formatter/src/r/auxiliary/parenthesized_expression.rs
+++ b/crates/air_r_formatter/src/r/auxiliary/parenthesized_expression.rs
@@ -13,13 +13,19 @@ impl FormatNodeRule<RParenthesizedExpression> for FormatRParenthesizedExpression
             r_paren_token,
         } = node.as_fields();
 
-        write!(
-            f,
-            [
-                l_paren_token.format(),
-                group(&soft_block_indent(&body.format())),
-                r_paren_token.format()
-            ]
-        )
+        let l_paren_token = l_paren_token?;
+        let body = body?;
+        let r_paren_token = r_paren_token?;
+
+        let body = format_with(|f| {
+            if body.syntax().has_comments_direct() {
+                // Fully expand the parentheses to allow placement of comments
+                write!(f, [block_indent(&body.format())])
+            } else {
+                write!(f, [body.format()])
+            }
+        });
+
+        write!(f, [l_paren_token.format(), body, r_paren_token.format()])
     }
 }


Test patch:
diff --git a/crates/air_r_formatter/tests/specs/r/binary_expression.R b/crates/air_r_formatter/tests/specs/r/binary_expression.R
index e438fa0..4c66fa9 100644
--- a/crates/air_r_formatter/tests/specs/r/binary_expression.R
+++ b/crates/air_r_formatter/tests/specs/r/binary_expression.R
@@ -345,7 +345,7 @@ x[df
 x[[df
 |> foo()]]
 
-# This does retain the break, because it comes after the pipe
+# This does retain the persistent line break, because it comes after the pipe
 (df |>
 foo())
 
diff --git a/crates/air_r_formatter/tests/specs/r/binary_expression.R.snap b/crates/air_r_formatter/tests/specs/r/binary_expression.R.snap
index 454b77e..220faba 100644
--- a/crates/air_r_formatter/tests/specs/r/binary_expression.R.snap
+++ b/crates/air_r_formatter/tests/specs/r/binary_expression.R.snap
@@ -352,7 +352,7 @@ x[df
 x[[df
 |> foo()]]
 
-# This does retain the break, because it comes after the pipe
+# This does retain the persistent line break, because it comes after the pipe
 (df |>
 foo())
 
@@ -1023,11 +1023,9 @@ x[df |> foo()]
 
 x[[df |> foo()]]
 
-# This does retain the break, because it comes after the pipe
-(
-  df |>
-    foo()
-)
+# This does retain the persistent line break, because it comes after the pipe
+(df |>
+  foo())
 
 x[
   df |>
diff --git a/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R b/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R
index efb21bf..e705dde 100644
--- a/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R
+++ b/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R
@@ -1,13 +1,10 @@
 (1)
 
-# TODO: Should we keep it simple and say that fully expanding here (due to the
-# braced expression) is the best that we want to do?
+# Parentheses always tightly hug
 ({ 1 + 1 })
 (function() { 1 + 1 })
 
-# Shouldn't automatically break in the function call
 (fn(a, b, c))
-
 (fn_call_that_is_really_long(with_really_long, arguments_like_this, and_this_one_too))
 
 ((1))
diff --git a/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R.snap b/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R.snap
index eaf649d..c543727 100644
--- a/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R.snap
+++ b/crates/air_r_formatter/tests/specs/r/parenthesized_expression.R.snap
@@ -7,14 +7,11 @@ info: r/parenthesized_expression.R
 ```R
 (1)
 
-# TODO: Should we keep it simple and say that fully expanding here (due to the
-# braced expression) is the best that we want to do?
+# Parentheses always tightly hug
 ({ 1 + 1 })
 (function() { 1 + 1 })
 
-# Shouldn't automatically break in the function call
 (fn(a, b, c))
-
 (fn_call_that_is_really_long(with_really_long, arguments_like_this, and_this_one_too))
 
 ((1))
@@ -48,29 +45,20 @@ Persistent line breaks: Respect
 ```R
 (1)
 
-# TODO: Should we keep it simple and say that fully expanding here (due to the
-# braced expression) is the best that we want to do?
-(
-  {
-    1 + 1
-  }
-)
-(
-  function() {
-    1 + 1
-  }
-)
+# Parentheses always tightly hug
+({
+  1 + 1
+})
+(function() {
+  1 + 1
+})
 
-# Shouldn't automatically break in the function call
 (fn(a, b, c))
-
-(
-  fn_call_that_is_really_long(
-    with_really_long,
-    arguments_like_this,
-    and_this_one_too
-  )
-)
+(fn_call_that_is_really_long(
+  with_really_long,
+  arguments_like_this,
+  and_this_one_too
+))
 
 ((1))