Repository: jlongster/prettier (js)

Issue:
Comments between ) and { on if/then/else is not stable
```js
async function f() {
  if (untrackedChoice === 0) /* Cancel */ {
    return null;
  } else if (untrackedChoice === 1) /* Add */ {
    await repository.addAll(Array.from(untrackedChanges.keys()));
    shouldAmend = true;
  } else if (untrackedChoice === 2) /* Allow Untracked */ {
    allowUntracked = true;
  }
}
```

turns into

```js
async function f() {
  if (untrackedChoice === 0)
    /* Cancel */ {
      return null;
    }
  else if (untrackedChoice === 1)
    /* Add */ {
      await repository.addAll(Array.from(untrackedChanges.keys()));
      shouldAmend = true;
    }
  else if (untrackedChoice === 2)
    /* Allow Untracked */ {
      allowUntracked = true;
    }
}
```

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22async%20function%20f()%20%7B%5Cn%20%20if%20(untrackedChoice%20%3D%3D%3D%200)%20%2F*%20Cancel%20*%2F%20%7B%5Cn%20%20%20%20return%20null%3B%5Cn%20%20%7D%20else%20if%20(untrackedChoice%20%3D%3D%3D%201)%20%2F*%20Add%20*%2F%20%7B%5Cn%20%20%20%20await%20repository.addAll(Array.from(untrackedChanges.keys()))%3B%5Cn%20%20%20%20shouldAmend%20%3D%20true%3B%5Cn%20%20%7D%20else%20if%20(untrackedChoice%20%3D%3D%3D%202)%20%2F*%20Allow%20Untracked%20*%2F%20%7B%5Cn%20%20%20%20allowUntracked%20%3D%20true%3B%5Cn%20%20%7D%5Cn%7D%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Atrue%2C%22trailingComma%22%3Atrue%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%7D%7D

which then turns into

```js
async function f() {
  if (untrackedChoice === 0) {
    /* Cancel */ return null;
  } else if (untrackedChoice === 1) {
    /* Add */ await repository.addAll(Array.from(untrackedChanges.keys()));
    shouldAmend = true;
  } else if (untrackedChoice === 2) {
    /* Allow Untracked */ allowUntracked = true;
  }
}
```

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22async%20function%20f()%20%7B%5Cn%20%20if%20(untrackedChoice%20%3D%3D%3D%200)%5Cn%20%20%20%20%2F*%20Cancel%20*%2F%20%7B%5Cn%20%20%20%20%20%20return%20null%3B%5Cn%20%20%20%20%7D%5Cn%20%20else%20if%20(untrackedChoice%20%3D%3D%3D%201)%5Cn%20%20%20%20%2F*%20Add%20*%2F%20%7B%5Cn%20%20%20%20%20%20await%20repository.addAll(Array.from(untrackedChanges.keys()))%3B%5Cn%20%20%20%20%20%20shouldAmend%20%3D%20true%3B%5Cn%20%20%20%20%7D%5Cn%20%20else%20if%20(untrackedChoice%20%3D%3D%3D%202)%5Cn%20%20%20%20%2F*%20Allow%20Untracked%20*%2F%20%7B%5Cn%20%20%20%20%20%20allowUntracked%20%3D%20true%3B%5Cn%20%20%20%20%7D%5Cn%7D%5Cn%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Atrue%2C%22trailingComma%22%3Atrue%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%7D%7D

But... seriously, why would you put comments there!?

Pull request description:
Stabilize comments inside of if/then/else before {
We already have the logic for fixing if/then/else comments but those ones are triggering another codepath.

Fixes #618

Patch:
diff --git a/editors/jetbrains/README.md b/editors/jetbrains/README.md
index f75eddde7..1a7ae61f4 100644
--- a/editors/jetbrains/README.md
+++ b/editors/jetbrains/README.md
@@ -14,6 +14,6 @@ If on the other hand you have `prettier` installed locally, replace the **Progra
 
 ### Configure Keymap
 
-Now when you setup **External Tool** I guess you what to add hotkey for it. Go to *File | Settings | Keymap* for Windows and Linux *WebStorm | Preferences | Keymap* and type external tool name in search box.
+Now when you setup **External Tool** I guess you want to add hotkey for it. Go to *File | Settings | Keymap* for Windows and Linux *WebStorm | Preferences | Keymap* and type external tool name in search box.
 
 See [this documentation](https://www.jetbrains.com/help/webstorm/configuring-keyboard-shortcuts.html) about configuring keyboard shortcuts.
diff --git a/src/comments.js b/src/comments.js
index d97dae762..6109ce834 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -170,12 +170,14 @@ function attach(comments, ast, text) {
         addDanglingComment(ast, comment);
       }
     } else {
-      // Otherwise, text exists both before and after the comment on
-      // the same line. If there is both a preceding and following
-      // node, use a tie-breaking algorithm to determine if it should
-      // be attached to the next or previous node. In the last case,
-      // simply attach the right node;
-      if (precedingNode && followingNode) {
+      if (handleIfStatementComments(enclosingNode, followingNode, comment)) {
+        // We're good
+      } else if (precedingNode && followingNode) {
+        // Otherwise, text exists both before and after the comment on
+        // the same line. If there is both a preceding and following
+        // node, use a tie-breaking algorithm to determine if it should
+        // be attached to the next or previous node. In the last case,
+        // simply attach the right node;
         const tieCount = tiesToBreak.length;
         if (tieCount > 0) {
           var lastTie = tiesToBreak[tieCount - 1];
diff --git a/src/doc-debug.js b/src/doc-debug.js
index efcf9d20d..e1fc15671 100644
--- a/src/doc-debug.js
+++ b/src/doc-debug.js
@@ -92,6 +92,12 @@ function printDoc(doc) {
       printDoc(doc.contents) +
       ")";
   }
+
+  if (doc.type === "line-suffix") {
+    return "lineSuffix(" + printDoc(doc.contents) + ")";
+  }
+
+  throw new Error('Unknown doc type ' + doc.type);
 }
 
 module.exports = {
diff --git a/src/printer.js b/src/printer.js
index e78ec9718..95acac03a 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -684,13 +684,13 @@ function genericPrintNoParens(path, options, print) {
           parts.push(concat([": ", printedValue]));
         } else {
           parts.push(
-            concat([
+            group(concat([
               ":",
               ifBreak(" (", " "),
               indent(options.tabWidth, concat([softline, printedValue])),
               softline,
               ifBreak(")")
-            ])
+            ]))
           );
         }
       }
@@ -712,7 +712,14 @@ function genericPrintNoParens(path, options, print) {
     case "ArrayPattern":
       if (n.elements.length === 0) {
         parts.push(
-          concat(["[", comments.printDanglingComments(path, options), "]"])
+          group(
+            concat([
+              "[",
+              comments.printDanglingComments(path, options),
+              softline,
+              "]"
+            ])
+          )
         );
       } else {
         const lastElem = util.getLast(n.elements);


Test patch:
diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap
index 063b75f2d..b6c036234 100644
--- a/tests/comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap
@@ -74,6 +74,32 @@ declare class Foo extends Qux<string> {/* dangling */}
 "
 `;
 
+exports[`test dangling_array.js 1`] = `
+"expect(() => {}).toTriggerReadyStateChanges([
+  // Nothing.
+]);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+expect(() => {}).toTriggerReadyStateChanges(
+  [
+    // Nothing.
+  ]
+);
+"
+`;
+
+exports[`test dangling_array.js 2`] = `
+"expect(() => {}).toTriggerReadyStateChanges([
+  // Nothing.
+]);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+expect(() => {}).toTriggerReadyStateChanges(
+  [
+    // Nothing.
+  ]
+);
+"
+`;
+
 exports[`test first-line.js 1`] = `
 "a // comment
 b
diff --git a/tests/comments/dangling_array.js b/tests/comments/dangling_array.js
new file mode 100644
index 000000000..599897d57
--- /dev/null
+++ b/tests/comments/dangling_array.js
@@ -0,0 +1,3 @@
+expect(() => {}).toTriggerReadyStateChanges([
+  // Nothing.
+]);
diff --git a/tests/if_comments/__snapshots__/jsfmt.spec.js.snap b/tests/if_comments/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..569510378
--- /dev/null
+++ b/tests/if_comments/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,73 @@
+exports[`test if_comments.js 1`] = `
+"async function f() {
+  if (untrackedChoice === 0) /* Cancel */ {
+    return null;
+  } else if (untrackedChoice === 1) /* Add */ {
+    await repository.addAll(Array.from(untrackedChanges.keys()));
+    shouldAmend = true;
+  } else if (untrackedChoice === 2) /* Allow Untracked */ {
+    allowUntracked = true;
+  }
+}
+
+async function f() {
+  if (untrackedChoice === 0)
+    /* Cancel */ {
+      return null;
+    }
+  else if (untrackedChoice === 1)
+    /* Add */ {
+      await repository.addAll(Array.from(untrackedChanges.keys()));
+      shouldAmend = true;
+    }
+  else if (untrackedChoice === 2)
+    /* Allow Untracked */ {
+      allowUntracked = true;
+    }
+}
+
+async function f() {
+  if (untrackedChoice === 0) {
+    /* Cancel */ return null;
+  } else if (untrackedChoice === 1) {
+    /* Add */ await repository.addAll(Array.from(untrackedChanges.keys()));
+    shouldAmend = true;
+  } else if (untrackedChoice === 2) {
+    /* Allow Untracked */ allowUntracked = true;
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+async function f() {
+  if (untrackedChoice === 0) {
+    /* Cancel */ return null;
+  } else if (untrackedChoice === 1) {
+    /* Add */ await repository.addAll(Array.from(untrackedChanges.keys()));
+    shouldAmend = true;
+  } else if (untrackedChoice === 2) {
+    /* Allow Untracked */ allowUntracked = true;
+  }
+}
+
+async function f() {
+  if (untrackedChoice === 0) {
+    /* Cancel */ return null;
+  } else if (untrackedChoice === 1) {
+    /* Add */ await repository.addAll(Array.from(untrackedChanges.keys()));
+    shouldAmend = true;
+  } else if (untrackedChoice === 2) {
+    /* Allow Untracked */ allowUntracked = true;
+  }
+}
+
+async function f() {
+  if (untrackedChoice === 0) {
+    /* Cancel */ return null;
+  } else if (untrackedChoice === 1) {
+    /* Add */ await repository.addAll(Array.from(untrackedChanges.keys()));
+    shouldAmend = true;
+  } else if (untrackedChoice === 2) {
+    /* Allow Untracked */ allowUntracked = true;
+  }
+}
+"
+`;
diff --git a/tests/if_comments/if_comments.js b/tests/if_comments/if_comments.js
new file mode 100644
index 000000000..2b3552999
--- /dev/null
+++ b/tests/if_comments/if_comments.js
@@ -0,0 +1,37 @@
+async function f() {
+  if (untrackedChoice === 0) /* Cancel */ {
+    return null;
+  } else if (untrackedChoice === 1) /* Add */ {
+    await repository.addAll(Array.from(untrackedChanges.keys()));
+    shouldAmend = true;
+  } else if (untrackedChoice === 2) /* Allow Untracked */ {
+    allowUntracked = true;
+  }
+}
+
+async function f() {
+  if (untrackedChoice === 0)
+    /* Cancel */ {
+      return null;
+    }
+  else if (untrackedChoice === 1)
+    /* Add */ {
+      await repository.addAll(Array.from(untrackedChanges.keys()));
+      shouldAmend = true;
+    }
+  else if (untrackedChoice === 2)
+    /* Allow Untracked */ {
+      allowUntracked = true;
+    }
+}
+
+async function f() {
+  if (untrackedChoice === 0) {
+    /* Cancel */ return null;
+  } else if (untrackedChoice === 1) {
+    /* Add */ await repository.addAll(Array.from(untrackedChanges.keys()));
+    shouldAmend = true;
+  } else if (untrackedChoice === 2) {
+    /* Allow Untracked */ allowUntracked = true;
+  }
+}
diff --git a/tests/if_comments/jsfmt.spec.js b/tests/if_comments/jsfmt.spec.js
new file mode 100644
index 000000000..989047bcc
--- /dev/null
+++ b/tests/if_comments/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname);
diff --git a/tests/object-prop-break-in/__snapshots__/jsfmt.spec.js.snap b/tests/object-prop-break-in/__snapshots__/jsfmt.spec.js.snap
index 1bc4417f6..cb925d852 100644
--- a/tests/object-prop-break-in/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/object-prop-break-in/__snapshots__/jsfmt.spec.js.snap
@@ -1,3 +1,20 @@
+exports[`test comment.js 1`] = `
+"function foo() {
+  return {
+    // this comment causes the problem
+    bar: baz() + 1
+  };
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+function foo() {
+  return {
+    // this comment causes the problem
+    bar: baz() + 1
+  };
+}
+"
+`;
+
 exports[`test test.js 1`] = `
 "const a = classnames({
   \"some-prop\": this.state.longLongLongLongLongLongLongLongLongTooLongProp
diff --git a/tests/object-prop-break-in/comment.js b/tests/object-prop-break-in/comment.js
new file mode 100644
index 000000000..470a85819
--- /dev/null
+++ b/tests/object-prop-break-in/comment.js
@@ -0,0 +1,6 @@
+function foo() {
+  return {
+    // this comment causes the problem
+    bar: baz() + 1
+  };
+}