Repository: jlongster/prettier (js)

Issue:
Flow comment syntax errors with single arg in arrow functions
Here are examples of Flow syntax errors using comment syntax when annotating an arrow function with a single arg. This is using Prettier v0.15.0. Link: [Playground](https://jlongster.github.io/prettier/#%7B%22content%22%3A%22%2F%2F%20Error%5Cnconst%20beep%20%3D%20(data%2F*%3A%20Object*%2F)%20%3D%3E%20%7B%7D%5Cn%5Cn%2F%2F%20OK%5Cnconst%20beep%20%3D%20(data%2F*%3A%20Object*%2F%2C%20secondData%2F*%3A%20Object*%2F)%20%3D%3E%20%7B%7D%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Afalse%2C%22trailingComma%22%3Afalse%2C%22bracketSpacing%22%3Afalse%2C%22doc%22%3Afalse%7D%7D)

Here is the pasted example from the link above for quick viewing:

Before:
``` js
// Error
const beep = (data/*: Object*/) => {}

// OK
const beep = (data/*: Object*/, secondData/*: Object*/) => {}

```

After:
``` js
// Error
const beep = data /*: Object*/ => {};

// OK
const beep = (data /*: Object*/, secondData /*: Object*/) => {};
```

The error from Flow will look something like
```
  1: const beep = data /*: Object*/ => {};
                       ^^^ Unexpected token /*:
```

Pull request description:
Add parenthesis around single argument arrow if comments
Fixes #568

Patch:
diff --git a/.gitignore b/.gitignore
index 2c48b4041..93f136199 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
 node_modules
 npm-debug.log
-test.js
diff --git a/src/comments.js b/src/comments.js
index 9de74b035..e63180274 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -151,8 +151,10 @@ function attach(comments, ast, text) {
     ) {
       // If a comment exists on its own line, prefer a leading comment.
       // We also need to check if it's the first line of the file.
-      if (handleMemberExpressionComment(enclosingNode, followingNode, comment) ||
-          handleIfStatementComments(enclosingNode, followingNode, comment)) {
+      if (
+        handleMemberExpressionComment(enclosingNode, followingNode, comment) ||
+          handleIfStatementComments(enclosingNode, followingNode, comment)
+      ) {
         // We're good
       } else if (followingNode) {
         // Always a leading comment.
@@ -302,10 +304,12 @@ function addBlockStatementFirstComment(node, comment) {
 //   if (1) { ... }
 //   else {
 //     // comment
-//     ...  
+//     ...
 //   }
 function handleIfStatementComments(enclosingNode, followingNode, comment) {
-  if (!enclosingNode || enclosingNode.type !== "IfStatement" || !followingNode) {
+  if (
+    !enclosingNode || enclosingNode.type !== "IfStatement" || !followingNode
+  ) {
     return false;
   }
 
@@ -327,10 +331,12 @@ function handleIfStatementComments(enclosingNode, followingNode, comment) {
 }
 
 function handleMemberExpressionComment(enclosingNode, followingNode, comment) {
-  if (enclosingNode &&
+  if (
+    enclosingNode &&
       enclosingNode.type === "MemberExpression" &&
       followingNode &&
-      followingNode.type === "Identifier") {
+      followingNode.type === "Identifier"
+  ) {
     addLeadingComment(enclosingNode, comment);
     return true;
   }
@@ -376,11 +382,9 @@ function printTrailingComment(commentPath, print, options, parentNode) {
   const contents = printComment(commentPath);
   const isBlock = comment.type === "Block" || comment.type === "CommentBlock";
 
-  if (
-    util.hasNewline(options.originalText, locStart(comment), {
+  if (util.hasNewline(options.originalText, locStart(comment), {
       backwards: true
-    })
-  ) {
+    })) {
     // This allows comments at the end of nested structures:
     // {
     //   x: 1,
diff --git a/src/printer.js b/src/printer.js
index 538ec61bd..83784b6b9 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -120,7 +120,9 @@ function genericPrintNoParens(path, options, print) {
         path.each(
           function(childPath) {
             parts.push(print(childPath), ";", hardline);
-            if (util.isNextLineEmpty(options.originalText, childPath.getValue())) {
+            if (
+              util.isNextLineEmpty(options.originalText, childPath.getValue())
+            ) {
               parts.push(hardline);
             }
           },
@@ -166,13 +168,15 @@ function genericPrintNoParens(path, options, print) {
       const parts = [];
       printBinaryishExpressions(path, parts, print, options);
 
-      return group(concat([
+      return group(
+        concat([
           // Don't include the initial expression in the indentation
           // level. The first item is guaranteed to be the first
           // left-most expression.
           parts.length > 0 ? parts[0] : "",
           indent(options.tabWidth, concat(parts.slice(1)))
-        ]));
+        ])
+      );
     }
     case "AssignmentPattern":
       return concat([
@@ -257,6 +261,8 @@ function genericPrintNoParens(path, options, print) {
           !n.rest &&
           n.params[0].type === "Identifier" &&
           !n.params[0].typeAnnotation &&
+          !n.params[0].leadingComments &&
+          !n.params[0].trailingComments &&
           !n.predicate &&
           !n.returnType
       ) {
@@ -726,7 +732,7 @@ function genericPrintNoParens(path, options, print) {
     case "NullLiteral":
       return "null"; // Babel 6 Literal split
     case "RegExpLiteral":
-      return printRegex(n);
+      return n.extra.raw;
     // Babel 6 Literal split
     case "NumericLiteral":
       return printNumber(n.extra.raw);
@@ -736,7 +742,6 @@ function genericPrintNoParens(path, options, print) {
     case "StringLiteral":
     case "Literal":
       if (typeof n.value === "number") return printNumber(n.raw);
-      if (n.regex) return printRegex(n.regex);
       if (typeof n.value !== "string") return "" + n.value;
 
       return nodeStr(n, options); // Babel 6
@@ -775,10 +780,10 @@ function genericPrintNoParens(path, options, print) {
             concat([
               line,
               "? ",
-              indent(options.tabWidth, path.call(print, "consequent")),
+              indent(2, path.call(print, "consequent")),
               line,
               ": ",
-              indent(options.tabWidth, path.call(print, "alternate"))
+              indent(2, path.call(print, "alternate"))
             ])
           )
         ])
@@ -1187,7 +1192,11 @@ function genericPrintNoParens(path, options, print) {
         return "{}";
       }
 
-      return concat(["{", indent(options.tabWidth, concat([
+      return concat([
+        "{",
+        indent(
+          options.tabWidth,
+          concat([
             hardline,
             path.call(
               function(bodyPath) {
@@ -1195,7 +1204,11 @@ function genericPrintNoParens(path, options, print) {
               },
               "body"
             )
-          ])), hardline, "}"]);
+          ])
+        ),
+        hardline,
+        "}"
+      ]);
     case "ClassPropertyDefinition":
       parts.push("static ", path.call(print, "definition"));
 
@@ -1231,10 +1244,7 @@ function genericPrintNoParens(path, options, print) {
     case "ClassExpression":
       return concat(printClass(path, options, print));
     case "TemplateElement":
-      return join(
-        literalline,
-        n.value.raw.split("\n").map(line => normalizeEscapes(line))
-      );
+      return join(literalline, n.value.raw.split("\n"));
     case "TemplateLiteral":
       var expressions = path.map(print, "expressions");
 
@@ -1687,7 +1697,11 @@ function printMethod(path, options, print) {
     key = concat(["[", key, "]"]);
   }
 
-  parts.push(key, path.call(print, "value", "typeParameters"), group(concat([
+  parts.push(
+    key,
+    path.call(print, "value", "typeParameters"),
+    group(
+      concat([
         path.call(
           function(valuePath) {
             return printFunctionParams(valuePath, print, options);
@@ -1695,7 +1709,11 @@ function printMethod(path, options, print) {
           "value"
         ),
         path.call(p => printReturnType(p, print), "value")
-      ])), " ", path.call(print, "value", "body"));
+      ])
+    ),
+    " ",
+    path.call(print, "value", "body")
+  );
 
   return concat(parts);
 }
@@ -2044,7 +2062,11 @@ function printMemberChain(path, options, print) {
     } else if (node.type === "MemberExpression") {
       printedNodes.unshift({
         node: node,
-        printed: comments.printComments(path, p => printMemberLookup(path, options, print), options)
+        printed: comments.printComments(
+          path,
+          p => printMemberLookup(path, options, print),
+          options
+        )
       });
       path.call(object => rec(object), "object");
     } else {
@@ -2056,7 +2078,6 @@ function printMemberChain(path, options, print) {
   }
   rec(path);
 
-
   // Once we have a linear list of printed nodes, we want to create groups out
   // of it.
   //
@@ -2094,8 +2115,9 @@ function printMemberChain(path, options, print) {
   // MemberExpression
   var hasSeenCallExpression = false;
   for (; i < printedNodes.length; ++i) {
-    if (hasSeenCallExpression &&
-        printedNodes[i].node.type === "MemberExpression") {
+    if (
+      hasSeenCallExpression && printedNodes[i].node.type === "MemberExpression"
+    ) {
       groups.push(currentGroup);
       currentGroup = [];
       hasSeenCallExpression = false;
@@ -2122,10 +2144,12 @@ function printMemberChain(path, options, print) {
   // node is just an identifier with the name starting with a capital
   // letter or just a sequence of _$. The rationale is that they are
   // likely to be factories.
-  if (groups[0].length === 1 &&
+  if (
+    groups[0].length === 1 &&
       groups[0][0].node.type === "Identifier" &&
       groups[0][0].node.name.match(/(^[A-Z])|^[_$]+$/) &&
-      groups.length >= 2) {
+      groups.length >= 2
+  ) {
     // Push all the values of groups[0] at the beginning of groups[1]
     [].unshift.apply(groups[1], groups[0]);
     // Remove groups[0]
@@ -2150,10 +2174,7 @@ function printMemberChain(path, options, print) {
     printGroup(groups[0]),
     indent(
       options.tabWidth,
-      group(concat([
-        hardline,
-        join(hardline, groups.slice(1).map(printGroup))
-      ]))
+      group(concat([hardline, join(hardline, groups.slice(1).map(printGroup))]))
     )
   ]);
 
@@ -2211,7 +2232,7 @@ function printJSXChildren(path, options, print) {
         const partiallyEscapedValue = options.parser === "flow"
           ? child.raw
           : util.htmlEscapeInsideAngleBracket(child.value);
-        const value = partiallyEscapedValue.replace(/\u00a0/g, '&nbsp;');
+        const value = partiallyEscapedValue.replace(/\u00a0/g, "&nbsp;");
 
         if (/\S/.test(value)) {
           // treat each line of text as its own entity
@@ -2526,7 +2547,7 @@ function nodeStr(node, options) {
   // Workaround a bug in the Javascript version of the flow parser where
   // astral unicode characters like \uD801\uDC28 are incorrectly parsed as
   // a sequence of \uFFFD.
-  if (options.parser === "flow" && str.indexOf("\uFFFD") !== -1) {
+  if (options.parser === "flow" && str.indexOf("\ufffd") !== -1) {
     return node.raw;
   }
 
@@ -2591,41 +2612,12 @@ function makeString(rawContent, enclosingQuote) {
     return match;
   });
 
-  return enclosingQuote + normalizeEscapes(newContent) + enclosingQuote;
-}
-
-function printRegex(regexData) {
-  const pattern = regexData.pattern;
-  const flags = regexData.flags;
-  const skipES2015 = !flags.includes("u");
-
-  return "/" + normalizeEscapes(pattern, skipES2015) + "/" + flags;
-}
-
-function normalizeEscapes(rawContent, skipES2015) {
-  // Matches \x escapes and \u escapes (including the ES2015 variant) and other
-  // escapes.
-  const regex = /(\\x[\da-f]{2}|\\u[\da-f]{4}|\\u\{[\da-f]+\})|(\\[\s\S])/gi;
-
-  return rawContent.replace(regex, (match, escape, otherEscape) => {
-    // Return other escapes as-is.
-    if (otherEscape) {
-      return otherEscape;
-    }
-
-    // Regexes without the /u flag do not support ES2015 unicode escapes, so
-    // return the match as-is.
-    if (skipES2015 && escape[2] === "{") {
-      return escape;
-    }
-
-    // Print all \x and \u escapes lowercase.
-    return escape.toLowerCase();
-  });
+  return enclosingQuote + newContent + enclosingQuote;
 }
 
 function printNumber(rawNumber) {
-  return rawNumber.toLowerCase()
+  return rawNumber
+    .toLowerCase()
     // Remove unnecessary plus and zeroes from scientific notation.
     .replace(/^([\d.]+e)(?:\+|(-))?0*/, "$1$2")
     // Make sure numbers always start with a digit.
@@ -2658,6 +2650,7 @@ function shouldPrintSameLine(node) {
     type === "CallExpression" ||
     type === "FunctionExpression" ||
     type === "Identifier" ||
+    type === "JSXElement" ||
     type === "Literal" ||
     type === "MemberExpression" ||
     type === "NewExpression" ||


Test patch:
diff --git a/tests/flow_comments/__snapshots__/jsfmt.spec.js.snap b/tests/flow_comments/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..bdc17200d
--- /dev/null
+++ b/tests/flow_comments/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,29 @@
+exports[`test arrow.js 1`] = `
+"// Error
+const beep = (data/*: Object*/) => {}
+
+// OK
+const beep = (data/*: Object*/, secondData/*: Object*/) => {}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Error
+const beep = (data: Object) => {};
+
+// OK
+const beep = (data: Object, secondData: Object) => {};
+"
+`;
+
+exports[`test arrow.js 2`] = `
+"// Error
+const beep = (data/*: Object*/) => {}
+
+// OK
+const beep = (data/*: Object*/, secondData/*: Object*/) => {}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Error
+const beep = (data /*: Object*/) => {};
+
+// OK
+const beep = (data /*: Object*/, secondData /*: Object*/) => {};
+"
+`;
diff --git a/tests/flow_comments/arrow.js b/tests/flow_comments/arrow.js
new file mode 100644
index 000000000..7f5d39d75
--- /dev/null
+++ b/tests/flow_comments/arrow.js
@@ -0,0 +1,5 @@
+// Error
+const beep = (data/*: Object*/) => {}
+
+// OK
+const beep = (data/*: Object*/, secondData/*: Object*/) => {}
diff --git a/tests/flow_comments/jsfmt.spec.js b/tests/flow_comments/jsfmt.spec.js
new file mode 100644
index 000000000..d5ec770a9
--- /dev/null
+++ b/tests/flow_comments/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname);
+run_spec(__dirname, {parser: 'babylon'});
diff --git a/tests/jsx/__snapshots__/jsfmt.spec.js.snap b/tests/jsx/__snapshots__/jsfmt.spec.js.snap
index c3a386b49..130a922b9 100644
--- a/tests/jsx/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/jsx/__snapshots__/jsfmt.spec.js.snap
@@ -193,3 +193,34 @@ exports[`test quotes.js 1`] = `
 <div id={\"\'\\\"&quot;<>&amp;quot;\"} />;
 "
 `;
+
+exports[`test object-property.js 1`] = `
+"const tabs = [
+  {
+    title: \"General Info\",
+    content: (
+      <GeneralForm
+        long-attribute=\"i-need-long-value-here\"
+        onSave={ onSave }
+        onCancel={ onCancel }
+        countries={ countries }
+      />
+    )
+  }
+];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const tabs = [
+  {
+    title: \"General Info\",
+    content: (
+      <GeneralForm
+        long-attribute=\"i-need-long-value-here\"
+        onSave={onSave}
+        onCancel={onCancel}
+        countries={countries}
+      />
+    )
+  }
+];
+"
+`;
diff --git a/tests/jsx/object-property.js b/tests/jsx/object-property.js
new file mode 100644
index 000000000..b133d5242
--- /dev/null
+++ b/tests/jsx/object-property.js
@@ -0,0 +1,13 @@
+const tabs = [
+  {
+    title: "General Info",
+    content: (
+      <GeneralForm
+        long-attribute="i-need-long-value-here"
+        onSave={ onSave }
+        onCancel={ onCancel }
+        countries={ countries }
+      />
+    )
+  }
+];
diff --git a/tests/literal/__snapshots__/jsfmt.spec.js.snap b/tests/literal/__snapshots__/jsfmt.spec.js.snap
index 458f81705..9e26c22ac 100644
--- a/tests/literal/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/literal/__snapshots__/jsfmt.spec.js.snap
@@ -116,48 +116,3 @@ function test5(): string {
 0.1e-10;
 "
 `;
-
-exports[`test regex.js 1`] = `
-"// Normalization of \\x and \\u escapes:
-
-// Basic case.
-/a\\xAaAb\\uB1cDE/gim;
-
-// ES2015 unicode escapes.
-/\\u{1Fa3}/u;
-/\\u{00000000A0}/u;
-
-// Leaves what looks like a ES2015 unicode escape alone if not using the /u flag.
-/\\u{1Fa3}/;
-
-// Leaves what looks like escapes but aren\'t alone.
-/\\xA\\u00BG/;
-
-// Leaves other escapes alone.
-/\\B\\S/;
-
-// Handles escaped backslashes.
-/\\\\xAB\\\\\\xAB\\\\\\\\xAB\\B\\\\\\B\\uAbCd/;
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Normalization of \\x and \\u escapes:
-
-// Basic case.
-/a\\xaaAb\\ub1cdE/gim;
-
-// ES2015 unicode escapes.
-/\\u{1fa3}/u;
-/\\u{00000000a0}/u;
-
-// Leaves what looks like a ES2015 unicode escape alone if not using the /u flag.
-/\\u{1Fa3}/;
-
-// Leaves what looks like escapes but aren\'t alone.
-/\\xA\\u00BG/;
-
-// Leaves other escapes alone.
-/\\B\\S/;
-
-// Handles escaped backslashes.
-/\\\\xAB\\\\\\xab\\\\\\\\xAB\\B\\\\\\B\\uabcd/;
-"
-`;
diff --git a/tests/literal/regex.js b/tests/literal/regex.js
deleted file mode 100644
index 30a7b0478..000000000
--- a/tests/literal/regex.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// Normalization of \x and \u escapes:
-
-// Basic case.
-/a\xAaAb\uB1cDE/gim;
-
-// ES2015 unicode escapes.
-/\u{1Fa3}/u;
-/\u{00000000A0}/u;
-
-// Leaves what looks like a ES2015 unicode escape alone if not using the /u flag.
-/\u{1Fa3}/;
-
-// Leaves what looks like escapes but aren't alone.
-/\xA\u00BG/;
-
-// Leaves other escapes alone.
-/\B\S/;
-
-// Handles escaped backslashes.
-/\\xAB\\\xAB\\\\xAB\B\\\B\uAbCd/;
diff --git a/tests/strings/__snapshots__/jsfmt.spec.js.snap b/tests/strings/__snapshots__/jsfmt.spec.js.snap
index 7cff00584..b75363d4e 100644
--- a/tests/strings/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/strings/__snapshots__/jsfmt.spec.js.snap
@@ -24,29 +24,6 @@ exports[`test strings.js 1`] = `
 
   \'\\uD801\\uDC28\',
 ];
-
-// Normalization of \\x and \\u escapes:
-
-// Basic case.
-\"a\\xAaAb\\uB1cDE\";
-\`a\\xAaAb\\uB1cDE\`;
-
-// ES2015 unicode escapes.
-\"\\u{1Fa3}\";
-\`\\u{1Fa3}\`;
-\"\\u{00000000A0}\";
-\`\\u{00000000A0}\`;
-
-// Leaves other escapes alone.
-\"\\B\\S\";
-\`\\B\\S\`;
-
-// Handles escaped backslashes.
-\"\\\\xAB\\\\\\xAB\\\\\\\\xAB\\B\\\\\\B\\u1234\";
-\`\\\\xAB\\\\\\xAB\\\\\\\\xAB\\B\\\\\\B\\u1234\`;
-
-// Mix of everything.
-\"\\xF0\"\`a\\uaBcD\${\'\\xFdE\'}\\\\\\u{00AbCdE}\`;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 [
   \"abc\",
@@ -65,18 +42,6 @@ exports[`test strings.js 1`] = `
   \"\\0\",
   \"🐶\",
   \'\\uD801\\uDC28\'
-]; // Normalization of \\x and \\u escapes: // Basic case.
-
-\"a\\xaaAb\\ub1cdE\";
-\`a\\xaaAb\\ub1cdE\`; // ES2015 unicode escapes.
-\"\\u{1fa3}\";
-\`\\u{1fa3}\`;
-\"\\u{00000000a0}\";
-\`\\u{00000000a0}\`; // Leaves other escapes alone.
-\"\\B\\S\";
-\`\\B\\S\`; // Handles escaped backslashes.
-\"\\\\xAB\\\\\\xab\\\\\\\\xAB\\B\\\\\\B\\u1234\";
-\`\\\\xAB\\\\\\xab\\\\\\\\xAB\\B\\\\\\B\\u1234\`; // Mix of everything.
-\"\\xf0\"\`a\\uabcd\${\"\\xfdE\"}\\\\\\u{00abcde}\`;
+];
 "
 `;
diff --git a/tests/strings/strings.js b/tests/strings/strings.js
index c2722e2bb..d31dceb2b 100644
--- a/tests/strings/strings.js
+++ b/tests/strings/strings.js
@@ -23,26 +23,3 @@
 
   '\uD801\uDC28',
 ];
-
-// Normalization of \x and \u escapes:
-
-// Basic case.
-"a\xAaAb\uB1cDE";
-`a\xAaAb\uB1cDE`;
-
-// ES2015 unicode escapes.
-"\u{1Fa3}";
-`\u{1Fa3}`;
-"\u{00000000A0}";
-`\u{00000000A0}`;
-
-// Leaves other escapes alone.
-"\B\S";
-`\B\S`;
-
-// Handles escaped backslashes.
-"\\xAB\\\xAB\\\\xAB\B\\\B\u1234";
-`\\xAB\\\xAB\\\\xAB\B\\\B\u1234`;
-
-// Mix of everything.
-"\xF0"`a\uaBcD${'\xFdE'}\\\u{00AbCdE}`;
diff --git a/tests/ternaries/__snapshots__/jsfmt.spec.js.snap b/tests/ternaries/__snapshots__/jsfmt.spec.js.snap
index 84f0402ff..b78a8903c 100644
--- a/tests/ternaries/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/ternaries/__snapshots__/jsfmt.spec.js.snap
@@ -49,3 +49,55 @@ const obj5 = conditionIsTruthy
     };
 "
 `;
+
+exports[`test test.js 2`] = `
+"const obj0 = conditionIsTruthy ? shortThing : otherShortThing
+
+const obj1 = conditionIsTruthy ? { some: \'long\', object: \'with\', lots: \'of\', stuff } : shortThing
+
+const obj2 = conditionIsTruthy ? shortThing : { some: \'long\', object: \'with\', lots: \'of\', stuff }
+
+const obj3 = conditionIsTruthy ? { some: \'eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger\', object: \'with\', lots: \'of\', stuff } : shortThing
+
+const obj4 = conditionIsTruthy ? shortThing : { some: \'eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger\', object: \'with\', lots: \'of\', stuff }
+
+const obj5 = conditionIsTruthy ? { some: \'long\', object: \'with\', lots: \'of\', stuff } : { some: \'eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger\', object: \'with\', lots: \'of\', stuff }
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const obj0 = conditionIsTruthy ? shortThing : otherShortThing;
+
+const obj1 = conditionIsTruthy
+    ? { some: \"long\", object: \"with\", lots: \"of\", stuff }
+    : shortThing;
+
+const obj2 = conditionIsTruthy
+    ? shortThing
+    : { some: \"long\", object: \"with\", lots: \"of\", stuff };
+
+const obj3 = conditionIsTruthy
+    ? {
+          some: \"eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger\",
+          object: \"with\",
+          lots: \"of\",
+          stuff
+      }
+    : shortThing;
+
+const obj4 = conditionIsTruthy
+    ? shortThing
+    : {
+          some: \"eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger\",
+          object: \"with\",
+          lots: \"of\",
+          stuff
+      };
+
+const obj5 = conditionIsTruthy
+    ? { some: \"long\", object: \"with\", lots: \"of\", stuff }
+    : {
+          some: \"eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger\",
+          object: \"with\",
+          lots: \"of\",
+          stuff
+      };
+"
+`;
diff --git a/tests/ternaries/jsfmt.spec.js b/tests/ternaries/jsfmt.spec.js
index 989047bcc..1ec983a6b 100644
--- a/tests/ternaries/jsfmt.spec.js
+++ b/tests/ternaries/jsfmt.spec.js
@@ -1,1 +1,2 @@
 run_spec(__dirname);
+run_spec(__dirname, { tabWidth: 4 });