Repository: prettier/prettier (JavaScript)

Issue:
why the if comment is line breaked?
Is there any way to prevent line break?

**Prettier 2.2.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuc0DOMAEMCGaDWAgpgLyYAUAlKQHybAC+AOlJFBtnvgEKkXUk6jFmw64CAYT5Va9ZlBYB6AFTKWmTMswABAA44ATjgC29AEYQIAGzg4oDTAEs0mALTY4HfUePrNilgAzAFdWGEdoDwxyZ2pgP0dAmLRqcSIZRUUnFxJczAAVA2C4WXyuQj84KzQSxPIAQljOAm4MrOdSPIAxHGqSwQKubkq+5vwJNuzOslCAEzhAxyg4WdKuCRZ5EAAaEAhdcPRkUEMDCAB3AAVDBDRkEF7znABPO92zIzB8OBgAZX0wEsAObIGBFOC7ODGMwreazAAydiBwRwQLgXQgBmMOBg4SgIJQOGCMAgOxAAAsYMYrAB1cmOeBoAFwX63BmOABuDOe90cQKgmLgZKWNQMMEuRiB2OQgV6NV2ACs0AAPbifb5-ExweFLIVIWV9RUq37AmwARWCEHgMrlEJA3lF9zMOBhVjJugMSxgNMcsxg5OQAA4AAy7D0QGo0oy6e6PF5vEAAR0t8Al+zuKA9njgBg5Qt2BjgycchYlqOl+ttuxqxkcNsNIDQprgFqterBxV2uDMPr9AaQACYu0ZHFZgRIIMYKyBPABWMnBGplMwZg3ykAc4oASSg81gvzAnoOhF3vxgzxs9ZqDAYQA)
```sh
--html-whitespace-sensitivity ignore
--parser babel
--prose-wrap always
--quote-props preserve
--no-semi
--single-quote
```

**Input:**
```jsx
const taskA = () => {}
const taskB = () => {}
const taskC = () => {}

/**
  * @param {boolean} is - test param
  */
function test(is) {
  if(is) taskA() // is === True => TaskA
  else if(!is) taskB() // is === False => TaskB
  else taskC() // is === undefined => TaskC
}

```

**Output:**
```jsx
const taskA = () => {}
const taskB = () => {}
const taskC = () => {}

/**
 * @param {boolean} is - test param
 */
function test(is) {
  if (is) taskA()
  // is === True => TaskA
  else if (!is) taskB()
  // is === False => TaskB
  else taskC() // is === undefined => TaskC
}

```

**Expected behavior:**
```jsx
const taskA = () => {}
const taskB = () => {}
const taskC = () => {}

/**
 * @param {boolean} is - test param
 */
function test(is) {
  if (is) taskA() // is === True => TaskA
  else if (!is) taskB() // is === False => TaskB
  else taskC() // is === undefined => TaskC
}

```



Hints:
The intention was to support these often-requested cases:
```js
function test(is) {
  if (is) {
    taskA()
  }
  // case 1 "comment before else"
  else if (!is) {
    taskB()
  } // case 2 "comment after }", for things like "// end if ..."
  else taskC()
}
```
This issue might be an unintended side-effect of fixing that.

Patch:
diff --git a/src/language-js/comments.js b/src/language-js/comments.js
--- a/src/language-js/comments.js
+++ b/src/language-js/comments.js
@@ -27,6 +27,7 @@ const {
   isObjectProperty,
   getComments,
   CommentCheckFlags,
+  markerForIfWithoutBlockAndSameLineComment,
 } = require("./utils.js");
 const { locStart, locEnd } = require("./loc.js");
 
@@ -206,7 +207,24 @@ function handleIfStatementComments({
     if (precedingNode.type === "BlockStatement") {
       addTrailingComment(precedingNode, comment);
     } else {
-      addDanglingComment(enclosingNode, comment);
+      const isSingleLineComment =
+        comment.type === "SingleLine" ||
+        comment.loc.start.line === comment.loc.end.line;
+      const isSameLineComment =
+        comment.loc.start.line === precedingNode.loc.start.line;
+      if (isSingleLineComment && isSameLineComment) {
+        // example:
+        //   if (cond1) expr1; // comment A
+        //   else if (cond2) expr2; // comment A
+        //   else expr3;
+        addDanglingComment(
+          precedingNode,
+          comment,
+          markerForIfWithoutBlockAndSameLineComment
+        );
+      } else {
+        addDanglingComment(enclosingNode, comment);
+      }
     }
     return true;
   }
diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js
--- a/src/language-js/printer-estree.js
+++ b/src/language-js/printer-estree.js
@@ -28,6 +28,7 @@ const {
   hasIgnoreComment,
   isCallExpression,
   isMemberExpression,
+  markerForIfWithoutBlockAndSameLineComment,
 } = require("./utils.js");
 const { locStart, locEnd } = require("./loc.js");
 
@@ -180,7 +181,7 @@ function printPathNoParens(path, options, print, args) {
     // Babel extension.
     case "EmptyStatement":
       return "";
-    case "ExpressionStatement":
+    case "ExpressionStatement": {
       // Detect Flow and TypeScript directives
       if (node.directive) {
         return [printDirective(node.expression, options), semi];
@@ -200,11 +201,20 @@ function printPathNoParens(path, options, print, args) {
         }
       }
 
+      const danglingComment = printDanglingComments(
+        path,
+        options,
+        /** sameIndent */ true,
+        ({ marker }) => marker === markerForIfWithoutBlockAndSameLineComment
+      );
+
       // Do not append semicolon after the only JSX element in a program
       return [
         print("expression"),
         isTheOnlyJsxElementInMarkdown(options, path) ? "" : semi,
+        danglingComment ? [" ", danglingComment] : "",
       ];
+    }
     // Babel non-standard node. Used for Closure-style type casts. See postprocess.js.
     case "ParenthesizedExpression": {
       const shouldHug =
diff --git a/src/language-js/utils.js b/src/language-js/utils.js
--- a/src/language-js/utils.js
+++ b/src/language-js/utils.js
@@ -1315,6 +1315,13 @@ function isEnabledHackPipeline(options) {
   return Boolean(options.__isUsingHackPipeline);
 }
 
+/**
+ * This is used as a marker for dangling comments.
+ */
+const markerForIfWithoutBlockAndSameLineComment = Symbol(
+  "ifWithoutBlockAndSameLineComment"
+);
+
 module.exports = {
   getFunctionParameters,
   iterateFunctionParametersPath,
@@ -1379,4 +1386,5 @@ module.exports = {
   hasComment,
   getComments,
   CommentCheckFlags,
+  markerForIfWithoutBlockAndSameLineComment,
 };


Test patch:
diff --git a/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/js/if/__snapshots__/jsfmt.spec.js.snap
@@ -87,6 +87,59 @@ function f() {
 ================================================================================
 `;
 
+exports[`expr_and_same_line_comments.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+if (a === 0) doSomething(); // comment A1
+else if (a === 1) doSomethingElse(); // comment B1
+else if (a === 2) doSomethingElse(); // comment C1
+
+if (a === 0) doSomething(); /* comment A2 */
+else if (a === 1) doSomethingElse(); /* comment B2 */
+else if (a === 2) doSomethingElse(); /* comment C2 */
+
+if (a === 0) expr; // comment A3
+else if (a === 1) expr; // comment B3
+else if (a === 2) expr; // comment C3
+
+if (a === 0) expr; /* comment A4 */
+else if (a === 1) expr; /* comment B4 */
+else if (a === 2) expr; /* comment C4 */
+
+if (a === 0) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment A5
+else if (a === 1) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment B5
+else if (a === 2) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment C5
+
+=====================================output=====================================
+if (a === 0) doSomething(); // comment A1
+else if (a === 1) doSomethingElse(); // comment B1
+else if (a === 2) doSomethingElse(); // comment C1
+
+if (a === 0) doSomething(); /* comment A2 */
+else if (a === 1) doSomethingElse(); /* comment B2 */
+else if (a === 2) doSomethingElse(); /* comment C2 */
+
+if (a === 0) expr; // comment A3
+else if (a === 1) expr; // comment B3
+else if (a === 2) expr; // comment C3
+
+if (a === 0) expr; /* comment A4 */
+else if (a === 1) expr; /* comment B4 */
+else if (a === 2) expr; /* comment C4 */
+
+if (a === 0)
+  looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment A5
+else if (a === 1)
+  looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment B5
+else if (a === 2)
+  looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment C5
+
+================================================================================
+`;
+
 exports[`if_comments.js format 1`] = `
 ====================================options=====================================
 parsers: ["babel", "flow", "typescript"]
diff --git a/tests/format/js/if/expr_and_same_line_comments.js b/tests/format/js/if/expr_and_same_line_comments.js
new file mode 100644
--- /dev/null
+++ b/tests/format/js/if/expr_and_same_line_comments.js
@@ -0,0 +1,19 @@
+if (a === 0) doSomething(); // comment A1
+else if (a === 1) doSomethingElse(); // comment B1
+else if (a === 2) doSomethingElse(); // comment C1
+
+if (a === 0) doSomething(); /* comment A2 */
+else if (a === 1) doSomethingElse(); /* comment B2 */
+else if (a === 2) doSomethingElse(); /* comment C2 */
+
+if (a === 0) expr; // comment A3
+else if (a === 1) expr; // comment B3
+else if (a === 2) expr; // comment C3
+
+if (a === 0) expr; /* comment A4 */
+else if (a === 1) expr; /* comment B4 */
+else if (a === 2) expr; /* comment C4 */
+
+if (a === 0) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment A5
+else if (a === 1) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment B5
+else if (a === 2) looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong; // comment C5