Repository: prettier/prettier (JavaScript)
Date: 2022-02-24 10:35:18+00:00
Task type: Bug Fix

Issue:
Unnecessary line breaks caused by `ifBreak` with group id nested in `group` with the same id
**Prettier 2.5.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEwA6UAEmBmEBOmAFJFAM4yZn5gAKAhjABaYQ6YDaAjAEwDMAGky8+AfRHj+QiTP7jpYiXyVdRAFj48AugEpM6LJgC+GIyAEgIABxgBLaGWSh6+fBADuDfAkcp6AG3d6AE9HCwAjfHowAGs4GABlegBbOAAZWyg4ZBwAsjgIqNj4hKtozIBzZBh8AFcCkDhk8LgAE1a2tPooCtr6CrgAMQJkxjse5BB6WpgIcxAmGGT-AHUmW3gyMrA4BJ8N2wA3DeDJsDIwkEz8-BhaKIrRnLyGgCsyAA8Eyv84AEVahB4M9-PkLGV8DdJuF6C1-PMrPhMjAVrZWsxkAAOAAM4Lc+RWUSsk0RcBuh2yFgAjoD4PdrL4pmQALRZNptebeGm2bz3fpPJC5UENfLJWzVOoin7-WnZQUvCwwWGo9FMZA8RVRWz+SoAYQgyQFjTIAFZ5rV8gAVWG+IVgkCHeoASSgHVgCTASJsAEFXQkYMFfiD8kYjEA)
<!-- prettier-ignore -->
```sh
--parser babel
```

**Input:**
<!-- prettier-ignore -->
```jsx
{
  for (const srcPath of [123, 123_123_123, 123_123_123_1, 13_123_3123_31_432]) {
  }
}
```

**Output:**
<!-- prettier-ignore -->
```jsx
{
  for (const srcPath of [
    123, 123_123_123, 123_123_123_1, 13_123_3123_31_432,
  ]) {
  }
}

```

**Expected behavior:**

Should format as input.


Found this when playing with the "doc", in this case, the input is exactly 80 width. I think there should be bug in the doc print.

The test code from https://github.com/prettier/prettier/blob/2cde2dbf9d280260f8c499039d89419b21fa4dc0/tests/format/js/arrays/issue-10159.js#L3

@thorn0 You may interest.


Issue discussion:
It's known: #10159
I'll close that issue in favor of this one because you have a better description here.
The root cause is described here: https://github.com/prettier/prettier/pull/10160#issuecomment-768668404
Sorry, totally forgot about that.
After debugging with your doc [here](https://github.com/prettier/prettier/pull/10160#issuecomment-768668404), I think the bug is in `fits()`, maybe also related to how [`line` works in `fits()`](https://github.com/prettier/prettier/blob/896970a18518bc31f1f15233b5ec612394453624/src/document/doc-printer.js#L246)



Solution patch:
diff --git a/changelog_unreleased/api/12362.md b/changelog_unreleased/api/12362.md
new file mode 100644
index 000000000000..3bd000439297
--- /dev/null
+++ b/changelog_unreleased/api/12362.md
@@ -0,0 +1,19 @@
+#### Fix doc printer issue when using `ifBreak` inside `group` (#12362 by @fisker)
+
+<!-- prettier-ignore -->
+```jsx
+// Input
+//                                                                              |80
+for (const number of [123_123_123, 123_123_123, 123_123_123, 123_123_123, 12]) {
+}
+
+// Prettier stable
+for (const number of [
+  123_123_123, 123_123_123, 123_123_123, 123_123_123, 12,
+]) {
+}
+
+// Prettier main
+for (const number of [123_123_123, 123_123_123, 123_123_123, 123_123_123, 12]) {
+}
+```
diff --git a/src/document/doc-printer.js b/src/document/doc-printer.js
index 1a45f78c615d..c0c31363bb58 100644
--- a/src/document/doc-printer.js
+++ b/src/document/doc-printer.js
@@ -202,10 +202,6 @@ function fits(next, restCommands, width, options, hasLineSuffix, mustBeFlat) {
               ? getLast(doc.expandedStates)
               : doc.contents,
           ]);
-
-          if (doc.id) {
-            groupModeMap[doc.id] = groupMode;
-          }
           break;
         }
         case "fill":
@@ -216,7 +212,9 @@ function fits(next, restCommands, width, options, hasLineSuffix, mustBeFlat) {
           break;
         case "if-break":
         case "indent-if-break": {
-          const groupMode = doc.groupId ? groupModeMap[doc.groupId] : mode;
+          const groupMode = doc.groupId
+            ? groupModeMap[doc.groupId] || MODE_FLAT
+            : mode;
           if (groupMode === MODE_BREAK) {
             const breakContents =
               doc.type === "if-break"


Test patch:
diff --git a/tests/format/js/arrays/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/arrays/__snapshots__/jsfmt.spec.js.snap
index a5e5d3eac8d4..c1d523ae81c2 100644
--- a/tests/format/js/arrays/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/js/arrays/__snapshots__/jsfmt.spec.js.snap
@@ -56,9 +56,7 @@ printWidth: 80
   }
 }
 {
-  for (const srcPath of [
-    123, 123_123_123, 123_123_123_1, 13_123_3123_31_432,
-  ]) {
+  for (const srcPath of [123, 123_123_123, 123_123_123_1, 13_123_3123_31_432]) {
   }
 }
 {
diff --git a/tests/format/typescript/interface/long-type-parameters/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/interface/long-type-parameters/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..cd6e58aef9bf
--- /dev/null
+++ b/tests/format/typescript/interface/long-type-parameters/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,79 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`long-type-parameters.ts - {"printWidth":109} format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 109
+                                                                                                             | printWidth
+=====================================input======================================
+// https://github.com/vega/vega-lite/blob/ae13aff7b480cf9c994031eca08a6b1720e01ab3/src/mark.ts#L602
+export interface MarkDef<
+  M extends string | Mark = Mark,
+  ES extends ExprRef | SignalRef = ExprRef | SignalRef
+> extends GenericMarkDef<M>,
+    Omit<
+      MarkConfig<ES> &
+        AreaConfig<ES> &
+        BarConfig<ES> & // always extends RectConfig
+        LineConfig<ES> &
+        TickConfig<ES>,
+      'startAngle' | 'endAngle' | 'width' | 'height'
+    >,
+    MarkDefMixins<ES> {}
+
+=====================================output=====================================
+// https://github.com/vega/vega-lite/blob/ae13aff7b480cf9c994031eca08a6b1720e01ab3/src/mark.ts#L602
+export interface MarkDef<
+  M extends string | Mark = Mark,
+  ES extends ExprRef | SignalRef = ExprRef | SignalRef
+> extends GenericMarkDef<M>,
+    Omit<
+      MarkConfig<ES> &
+        AreaConfig<ES> &
+        BarConfig<ES> & // always extends RectConfig
+        LineConfig<ES> &
+        TickConfig<ES>,
+      "startAngle" | "endAngle" | "width" | "height"
+    >,
+    MarkDefMixins<ES> {}
+
+================================================================================
+`;
+
+exports[`long-type-parameters.ts - {"printWidth":110} format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 110
+                                                                                                              | printWidth
+=====================================input======================================
+// https://github.com/vega/vega-lite/blob/ae13aff7b480cf9c994031eca08a6b1720e01ab3/src/mark.ts#L602
+export interface MarkDef<
+  M extends string | Mark = Mark,
+  ES extends ExprRef | SignalRef = ExprRef | SignalRef
+> extends GenericMarkDef<M>,
+    Omit<
+      MarkConfig<ES> &
+        AreaConfig<ES> &
+        BarConfig<ES> & // always extends RectConfig
+        LineConfig<ES> &
+        TickConfig<ES>,
+      'startAngle' | 'endAngle' | 'width' | 'height'
+    >,
+    MarkDefMixins<ES> {}
+
+=====================================output=====================================
+// https://github.com/vega/vega-lite/blob/ae13aff7b480cf9c994031eca08a6b1720e01ab3/src/mark.ts#L602
+export interface MarkDef<M extends string | Mark = Mark, ES extends ExprRef | SignalRef = ExprRef | SignalRef>
+  extends GenericMarkDef<M>,
+    Omit<
+      MarkConfig<ES> &
+        AreaConfig<ES> &
+        BarConfig<ES> & // always extends RectConfig
+        LineConfig<ES> &
+        TickConfig<ES>,
+      "startAngle" | "endAngle" | "width" | "height"
+    >,
+    MarkDefMixins<ES> {}
+
+================================================================================
+`;
diff --git a/tests/format/typescript/interface/long-type-parameters/jsfmt.spec.js b/tests/format/typescript/interface/long-type-parameters/jsfmt.spec.js
new file mode 100644
index 000000000000..03ff9aee6579
--- /dev/null
+++ b/tests/format/typescript/interface/long-type-parameters/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, ["typescript"], { printWidth: 109 });
+run_spec(__dirname, ["typescript"], { printWidth: 110 });
diff --git a/tests/format/typescript/interface/long-type-parameters/long-type-parameters.ts b/tests/format/typescript/interface/long-type-parameters/long-type-parameters.ts
new file mode 100644
index 000000000000..feedc34865a7
--- /dev/null
+++ b/tests/format/typescript/interface/long-type-parameters/long-type-parameters.ts
@@ -0,0 +1,14 @@
+// https://github.com/vega/vega-lite/blob/ae13aff7b480cf9c994031eca08a6b1720e01ab3/src/mark.ts#L602
+export interface MarkDef<
+  M extends string | Mark = Mark,
+  ES extends ExprRef | SignalRef = ExprRef | SignalRef
+> extends GenericMarkDef<M>,
+    Omit<
+      MarkConfig<ES> &
+        AreaConfig<ES> &
+        BarConfig<ES> & // always extends RectConfig
+        LineConfig<ES> &
+        TickConfig<ES>,
+      'startAngle' | 'endAngle' | 'width' | 'height'
+    >,
+    MarkDefMixins<ES> {}
diff --git a/tests/integration/__tests__/doc-printer.js b/tests/integration/__tests__/doc-printer.js
new file mode 100644
index 000000000000..04493142bc09
--- /dev/null
+++ b/tests/integration/__tests__/doc-printer.js
@@ -0,0 +1,27 @@
+"use strict";
+
+const prettier = require("prettier-local");
+const { group, ifBreak } = prettier.doc.builders;
+const { printDocToString } = prettier.doc.printer;
+const docToString = (doc, options) =>
+  printDocToString(doc, { tabSize: 2, ...options }).formatted;
+
+// Extra group should not effect `ifBreak` inside
+test("`ifBreak` inside `group`", () => {
+  const groupId = Symbol("test-group-id");
+  const FLAT_TEXT = "flat";
+  const doc = group(ifBreak("break", FLAT_TEXT, { groupId }), {
+    id: groupId,
+  });
+  const docs = [
+    doc,
+    [group(""), doc],
+    [doc, group("")],
+    group(doc),
+    group(doc, { id: Symbol("wrapper-group-id") }),
+  ];
+
+  expect(
+    docs.map((doc) => docToString(doc, { printWidth: FLAT_TEXT.length }))
+  ).toStrictEqual(Array.from({ length: docs.length }, () => FLAT_TEXT));
+});