Repository: prettier/prettier (JavaScript)

Issue:
Unstable formatting: variable assignment, expression in parentheses, method call
**Prettier 2.5.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEBLAZgAgBQwE4CucAlADpSaYY75FkWVVa6EnmOPUt3sefO02DPowA20AOYA1AIZ5UMgEai4AORkBbOAEZMAXkwB6Q5gACMAM4BaVBKgQ8cXiMrYAbnIXK4ABl0BqTDc4PABPABlJWXklFXUtVQINRRCAJmIAOi0YAAsIABNsYgBuchAAGhAIAAcYVGgLZFA5PAgAdwAFOQRGlBlRNplQxsrFPBkwAGs4GABlTThw1Cg4ZHR+izhR8amZ2eqJ5YlkQUq4ZLh8-MvwmSgJAhkJOAAxBw0ZGDr75BAZAhgEAqIByMA0ogA6jlUPALAcwHBZj0Yag3DDQr8wBYRmgoJs8DAOuMJB81hstiAAFYWAAesyOKgAigQIPAyaJNpUDnh8b8YKFqnALGB5LVgdV5LAIah8rlkAAOHxc1qbCHjaq-CVCkLBYEARxZ8CJNV6f2sK0ul2BjgNqEcRKepKQ6w5FM2GlQJ1YlQsDLgzNZq2d5MqMCU0tlOWQqVD41QoiOAGEIBonSAhQBWYEETYAFSUvRdnJAbiIAEkoNdYLMRahagBBSuzfkqdmbAC+7aAA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
if (true)
  if (true)
    if (true)
      if (true)
        if (true)
          longVariableName1 = // @ts-ignore
          (variable01 + veryLongVariableNameNumber2).method();

```

**Output:**
<!-- prettier-ignore -->
```tsx
if (true)
  if (true)
    if (true)
      if (true)
        if (true)
          longVariableName1 = ( // @ts-ignore
            variable01 + veryLongVariableNameNumber2
          ).method();

```

**Second Output:**
<!-- prettier-ignore -->
```tsx
if (true)
  if (true)
    if (true)
      if (true)
        if (true)
          longVariableName1 = // @ts-ignore
          (variable01 + veryLongVariableNameNumber2).method();

```

**Expected behavior:**
The formatter output should be stable, not loop between two different formats.


Hints:
Another manifestation:

**Prettier 2.5.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuc0DOMAEBrCYAWAhgEYDCEEAlnAOKEC2cA8lE8RADZwCqUA7nEIA3OJgC8mAPSTMAfnkAdKJhWqATvgCuxQmuIAlLTr0AKQlCiaOu8hDhwAJgGUADh0pg4aAOqEO2H0IAT18ggEoAbiUlEAAaEAgXGEp0ZFBdNQg+AAVdBDRkED8+YIL44jVCMGw4GCcGOAAZSig4ZAAzPzQ4csrq2tcqloBzZBg1TR6QOHpiRwdHRvNhzUJhuAAxCDV6QhhkqFGUQk0YCDiQfBh6Dm98Sng0Fyq4J3yHyiEHoMKwNDKQC1umoYNlKsNdh0ulMAFZoAAeThGXAAipoIPAoRxuvFnmpgYUdHMOBcXGoWjBvJQHDB8MgABwABlxmW63kqLkKZK8cDUIguAEd0fAwYkCsc0ABaVqORwXNRwIWUBVgtaQpCdbFTbr0ShjCba5FwNEYtoa6HxGAkKk0ulIABMlsqlHch3I9HV0zQAFYLppugAVEjizU4kBCSYASSgC1gTjA5KSAEEY04YEEuFjugBfbNAA)
<!-- prettier-ignore -->
```sh
--parser babel
```

**Input:**
<!-- prettier-ignore -->
```jsx
const kochabCooieGameOnOboleUnweave = // ???
      rhubarbRhubarb(annularCooeedSplicesWalksWayWay);


```

**Output:**
<!-- prettier-ignore -->
```jsx
const kochabCooieGameOnOboleUnweave = rhubarbRhubarb( // ???
  annularCooeedSplicesWalksWayWay
);

```

**Second Output:**
<!-- prettier-ignore -->
```jsx
const kochabCooieGameOnOboleUnweave = rhubarbRhubarb(
  // ???
  annularCooeedSplicesWalksWayWay
);

```
though note that your output is stable after second pass. though ideally it shouldn't need that

Patch:
diff --git a/src/language-js/print/assignment.js b/src/language-js/print/assignment.js
--- a/src/language-js/print/assignment.js
+++ b/src/language-js/print/assignment.js
@@ -2,7 +2,7 @@
 
 const { isNonEmptyArray, getStringWidth } = require("../../common/util.js");
 const {
-  builders: { line, group, indent, indentIfBreak },
+  builders: { line, group, indent, indentIfBreak, lineSuffixBoundary },
   utils: { cleanDoc, willBreak, canBreak },
 } = require("../../document/index.js");
 const {
@@ -50,6 +50,7 @@ function printAssignment(
         group(leftDoc),
         operator,
         group(indent(line), { id: groupId }),
+        lineSuffixBoundary,
         indentIfBreak(rightDoc, { groupId }),
       ]);
     }


Test patch:
diff --git a/tests/format/js/assignment-comments/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/assignment-comments/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/js/assignment-comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/js/assignment-comments/__snapshots__/jsfmt.spec.js.snap
@@ -1,5 +1,47 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`call.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+if (true)
+  if (true)
+    if (true)
+      if (true)
+        if (true)
+          longVariableName1 = // @ts-ignore
+          (variable01 + veryLongVariableNameNumber2).method();
+
+=====================================output=====================================
+if (true)
+  if (true)
+    if (true)
+      if (true)
+        if (true)
+          longVariableName1 = // @ts-ignore
+            (variable01 + veryLongVariableNameNumber2).method();
+
+================================================================================
+`;
+
+exports[`call2.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+const kochabCooieGameOnOboleUnweave = // ???
+      rhubarbRhubarb(annularCooeedSplicesWalksWayWay);
+
+=====================================output=====================================
+const kochabCooieGameOnOboleUnweave = // ???
+  rhubarbRhubarb(annularCooeedSplicesWalksWayWay);
+
+================================================================================
+`;
+
 exports[`function.js format 1`] = `
 ====================================options=====================================
 parsers: ["babel", "flow", "typescript"]
@@ -82,7 +124,8 @@ f3 = (
   a = b //comment
 ) => {};
 
-f4 = () => {}; // Comment
+f4 = // Comment
+  () => {};
 
 f5 =
   // Comment
@@ -109,7 +152,8 @@ let f3 = (
   a = b //comment
 ) => {};
 
-let f4 = () => {}; // Comment
+let f4 = // Comment
+  () => {};
 
 let f5 =
   // Comment
@@ -141,7 +185,8 @@ const bifornCringerMoshedPerplexSawder = // !!!
   anodyneCondosMalateOverateRetinol;
 
 =====================================output=====================================
-const kochabCooieGameOnOboleUnweave = annularCooeedSplicesWalksWayWay; // ???
+const kochabCooieGameOnOboleUnweave = // ???
+  annularCooeedSplicesWalksWayWay;
 
 const bifornCringerMoshedPerplexSawder = // !!!
   glimseGlyphsHazardNoopsTieTie +
diff --git a/tests/format/js/assignment-comments/call.js b/tests/format/js/assignment-comments/call.js
new file mode 100644
--- /dev/null
+++ b/tests/format/js/assignment-comments/call.js
@@ -0,0 +1,7 @@
+if (true)
+  if (true)
+    if (true)
+      if (true)
+        if (true)
+          longVariableName1 = // @ts-ignore
+          (variable01 + veryLongVariableNameNumber2).method();
diff --git a/tests/format/js/assignment-comments/call2.js b/tests/format/js/assignment-comments/call2.js
new file mode 100644
--- /dev/null
+++ b/tests/format/js/assignment-comments/call2.js
@@ -0,0 +1,2 @@
+const kochabCooieGameOnOboleUnweave = // ???
+      rhubarbRhubarb(annularCooeedSplicesWalksWayWay);