Repository: prettier/plugin-ruby (javascript)

Issue:
Hash indented twice when method chain continues off the hash
## Metadata

* Ruby version: 2.6.5
* `@prettier/plugin-ruby` or `prettier` gem version: 1.2.4
* Options:
  * [x] `rubyHashLabel`
  * [x] `rubyModifier`
  * [x] `rubySingleQuote`
  * [ ] `rubyToProc`
  * [ ] `trailingComma`: "none"

## Input

```ruby
{
  'first' => 'a long-enough value so hash does not end up on one line',
  'second' => ''
}.merge(something).merge(something_else)
```

## Current output

```ruby
{
    'first' => 'a long-enough value so hash does not end up on one line',
    'second' => ''
  }
  .merge(something)
  .merge(something_else)
```

## Expected output

```ruby
{
  'first' => 'a long-enough value so hash does not end up on one line',
  'second' => ''
}
  .merge(something)
  .merge(something_else)
```


---

Thanks for this plugin! Now that #725 added support for `# prettier-ignore`, I've been able to enable it for larger portions of our application code with one small slice at a time :relaxed:

I spotted this strange formatting for a hash in one place where rubocop (yes, we also use rubocop with prettier) complained about the end result formatting and I agree with rubocop here that it does look strange.

I'm not sure if the "Expected output" is indeed what we'd like to have here, but at least that one would not make rubocop complain.
Hash indented twice when method chain continues off the hash
## Metadata

* Ruby version: 2.6.5
* `@prettier/plugin-ruby` or `prettier` gem version: 1.2.4
* Options:
  * [x] `rubyHashLabel`
  * [x] `rubyModifier`
  * [x] `rubySingleQuote`
  * [ ] `rubyToProc`
  * [ ] `trailingComma`: "none"

## Input

```ruby
{
  'first' => 'a long-enough value so hash does not end up on one line',
  'second' => ''
}.merge(something).merge(something_else)
```

## Current output

```ruby
{
    'first' => 'a long-enough value so hash does not end up on one line',
    'second' => ''
  }
  .merge(something)
  .merge(something_else)
```

## Expected output

```ruby
{
  'first' => 'a long-enough value so hash does not end up on one line',
  'second' => ''
}
  .merge(something)
  .merge(something_else)
```


---

Thanks for this plugin! Now that #725 added support for `# prettier-ignore`, I've been able to enable it for larger portions of our application code with one small slice at a time :relaxed:

I spotted this strange formatting for a hash in one place where rubocop (yes, we also use rubocop with prettier) complained about the end result formatting and I agree with rubocop here that it does look strange.

I'm not sure if the "Expected output" is indeed what we'd like to have here, but at least that one would not make rubocop complain.


Discussion:
Oops, sorry, submitted the issue before using a nicer title 😅 
Oops, sorry, submitted the issue before using a nicer title 😅 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
 ### Changed
 
 - [@nruth], [@kddeisz] - Ensure unary operators on method calls that are sending operators get the correct scanner events.
+- [@cvoege], [@kddeisz] - Do not add parentheses when they're not needed to non-breaking command_calls with ternary arguments.
+- [@valscion], [@kddeisz] - Print method chains more nicely off array and hash literals.
 
 ## [1.2.4] - 2021-01-03
 
diff --git a/src/ruby/nodes/calls.js b/src/ruby/nodes/calls.js
--- a/src/ruby/nodes/calls.js
+++ b/src/ruby/nodes/calls.js
@@ -24,6 +24,12 @@ function printCall(path, opts, print) {
   // call syntax so if `call` is implicit, we don't print it out.
   const messageDoc = messageNode === "call" ? "" : path.call(print, "body", 2);
 
+  // For certain left sides of the call nodes, we want to attach directly to
+  // the } or end.
+  if (noIndent.includes(receiverNode.type)) {
+    return concat([receiverDoc, operatorDoc, messageDoc]);
+  }
+
   // The right side of the call node, as in everything including the operator
   // and beyond.
   const rightSideDoc = concat([
@@ -47,18 +53,10 @@ function printCall(path, opts, print) {
   // If we're at the top of a chain, then we're going to print out a nice
   // multi-line layout if this doesn't break into multiple lines.
   if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
-    let breakDoc = concat(node.breakDoc.concat(rightSideDoc));
-    if (!noIndent.includes(node.firstReceiverType)) {
-      breakDoc = indent(breakDoc);
-    }
-
-    return ifBreak(group(breakDoc), concat([receiverDoc, group(rightSideDoc)]));
-  }
-
-  // For certain left sides of the call nodes, we want to attach directly to
-  // the } or end.
-  if (noIndent.includes(receiverNode.type)) {
-    return concat([receiverDoc, operatorDoc, messageDoc]);
+    return ifBreak(
+      group(indent(concat(node.breakDoc.concat(rightSideDoc)))),
+      concat([receiverDoc, group(rightSideDoc)])
+    );
   }
 
   return group(concat([receiverDoc, group(indent(rightSideDoc))]));
diff --git a/src/ruby/nodes/commands.js b/src/ruby/nodes/commands.js
--- a/src/ruby/nodes/commands.js
+++ b/src/ruby/nodes/commands.js
@@ -105,8 +105,13 @@ function printCommandCall(path, opts, print) {
   let breakDoc;
 
   if (hasTernaryArg(node.body[3])) {
-    parts.push("(");
-    breakDoc = parts.concat(indent(concat([softline, argDocs])), softline, ")");
+    breakDoc = parts.concat(
+      "(",
+      indent(concat([softline, argDocs])),
+      softline,
+      ")"
+    );
+    parts.push(" ");
   } else if (skipArgsAlign(path)) {
     parts.push(" ");
     breakDoc = parts.concat(argDocs);
diff --git a/src/utils/noIndent.js b/src/utils/noIndent.js
--- a/src/utils/noIndent.js
+++ b/src/utils/noIndent.js
@@ -4,6 +4,7 @@ const noIndent = [
   "heredoc",
   "if",
   "method_add_block",
+  "unless",
   "xstring_literal"
 ];
 


Test patch:
diff --git a/test/js/ruby/nodes/calls.test.js b/test/js/ruby/nodes/calls.test.js
--- a/test/js/ruby/nodes/calls.test.js
+++ b/test/js/ruby/nodes/calls.test.js
@@ -92,10 +92,7 @@ describe("calls", () => {
           ${item},
           ${item},
           ${item}
-        ]
-        .map(&:foo?)
-        .bbb
-        .ccc
+        ].map(&:foo?).bbb.ccc
     `);
 
     return expect(content).toChangeFormat(expected);
diff --git a/test/js/ruby/nodes/conditionals.test.js b/test/js/ruby/nodes/conditionals.test.js
--- a/test/js/ruby/nodes/conditionals.test.js
+++ b/test/js/ruby/nodes/conditionals.test.js
@@ -357,6 +357,9 @@ describe("conditionals", () => {
       return expect(content).toChangeFormat(expected);
     });
 
+    test("does not add parens if within a command_call non-breaking", () =>
+      expect("foo.bar baz ? foo : bar").toMatchFormat());
+
     test("adds parens if within a command_call", () => {
       const content = `foo.bar baz ? ${long} : ${long}`;
       const expected = ruby(`