Repository: jlongster/prettier (js)

Issue:
Extra paren for negative number literal
It's a weird set of conditions that this arrises in and I can't seem to get the repro to something smaller:

```js
state = {
  // students
  hoverColumn: -1,
};
```

becomes:

```js
state = {
  // students
  hoverColumn: (
    -1
  )
};
```

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22state%20%3D%20%7B%5Cn%20%20%2F%2F%20students%5Cn%20%20hoverColumn%3A%20-1%2C%5Cn%7D%3B%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Afalse%2C%22trailingComma%22%3Afalse%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%7D%7D

Pull request:
Whitelist UnaryExpression for parentless objects
It seems like unary are unlikely to need parenthesis

Fixes #542

Patch:
diff --git a/editors/jetbrains/README.md b/editors/jetbrains/README.md
index 35913d8af..f75eddde7 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 **Extarnal 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 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.
 
 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 e05c7f50f..1309a1889 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -152,7 +152,9 @@ 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 (followingNode) {
+      if (handleIfStatementComments(enclosingNode, followingNode, comment)) {
+        // We're good
+      } else if (followingNode) {
         // Always a leading comment.
         addLeadingComment(followingNode, comment);
       } else if (precedingNode) {
@@ -278,6 +280,52 @@ function addTrailingComment(node, comment) {
   addCommentHelper(node, comment);
 }
 
+function addBlockStatementFirstComment(node, comment) {
+  if (node.body.length === 0) {
+    addDanglingComment(node, comment);
+  } else {
+    addLeadingComment(node.body[0], comment);
+  }
+}
+
+// There are often comments before the else clause of if statements like
+//
+//   if (1) { ... }
+//   // comment
+//   else { ... }
+//
+// They are being attached as leading comments of the BlockExpression which
+// is not well printed. What we want is to instead move the comment inside
+// of the block and make it leadingComment of the first element of the block
+// or dangling comment of the block if there is nothing inside
+//
+//   if (1) { ... }
+//   else {
+//     // comment
+//     ...  
+//   }
+function handleIfStatementComments(enclosingNode, followingNode, comment) {
+  if (!enclosingNode || enclosingNode.type !== "IfStatement" || !followingNode) {
+    return false;
+  }
+
+  if (followingNode.type === "BlockStatement") {
+    addBlockStatementFirstComment(followingNode, comment);
+    return true;
+  }
+
+  if (followingNode.type === "IfStatement") {
+    if (followingNode.consequent.type === "BlockStatement") {
+      addBlockStatementFirstComment(followingNode.consequent, comment);
+    } else {
+      addLeadingComment(followingNode.consequent, comment);
+    }
+    return true;
+  }
+
+  return false;
+}
+
 function printComment(commentPath) {
   const comment = commentPath.getValue();
 
diff --git a/src/printer.js b/src/printer.js
index ec13aa8bd..538ec61bd 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -2208,9 +2208,10 @@ function printJSXChildren(path, options, print) {
         // correctly escaped (since it parsed).
         // We really want to use value and re-escape it ourself when possible
         // though.
-        const value = options.parser === "flow"
+        const partiallyEscapedValue = options.parser === "flow"
           ? child.raw
           : util.htmlEscapeInsideAngleBracket(child.value);
+        const value = partiallyEscapedValue.replace(/\u00a0/g, '&nbsp;');
 
         if (/\S/.test(value)) {
           // treat each line of text as its own entity
@@ -2664,7 +2665,8 @@ function shouldPrintSameLine(node) {
     type === "ObjectPattern" ||
     type === "StringLiteral" ||
     type === "ThisExpression" ||
-    type === "TypeCastExpression";
+    type === "TypeCastExpression" ||
+    type === "UnaryExpression";
 }
 
 function printAstToDoc(ast, options) {


Test patch:
diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap
index ca8c8260c..d4f993d89 100644
--- a/tests/comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap
@@ -70,6 +70,94 @@ declare class Foo extends Qux<string> {/* dangling */}
 "
 `;
 
+exports[`test if.js 1`] = `
+"if (1)
+// comment
+{
+  false
+}
+// comment
+else if (2)
+  true
+// multi
+// ple
+// lines
+else if (3)
+  // existing comment
+  true
+// okay?
+else if (4) {
+  // empty with existing comment
+}
+// comment
+else {
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+if (1) {
+  // comment
+  false;
+} else if (2)
+  // comment
+  true;
+else if (3)
+  // multi
+  // ple
+  // lines
+  // existing comment
+  true;
+else if (4) {
+  // okay?
+  // empty with existing comment
+} else {
+  // comment
+}
+"
+`;
+
+exports[`test if.js 2`] = `
+"if (1)
+// comment
+{
+  false
+}
+// comment
+else if (2)
+  true
+// multi
+// ple
+// lines
+else if (3)
+  // existing comment
+  true
+// okay?
+else if (4) {
+  // empty with existing comment
+}
+// comment
+else {
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+if (1) {
+  // comment
+  false;
+} else if (2)
+  // comment
+  true;
+else if (3)
+  // multi
+  // ple
+  // lines
+  // existing comment
+  true;
+else if (4) {
+  // okay?
+  // empty with existing comment
+} else {
+  // comment
+}
+"
+`;
+
 exports[`test issues.js 1`] = `
 "// Does not need to break as it fits in 80 columns
 this.call(a, /* comment */ b);
@@ -284,10 +372,9 @@ if (1) {
 
 // Comments trigger invalid JavaScript in-between else if
 if (1) {
-} else
+} else {
   // Comment
-  {
-  }
+}
 
 // The comment makes the line break in a weird way
 const result = asyncExecute(\"non_existing_command\", /* args */ []);
@@ -514,10 +601,9 @@ if (1) {
 
 // Comments trigger invalid JavaScript in-between else if
 if (1) {
-} else
+} else {
   // Comment
-  {
-  }
+}
 
 // The comment makes the line break in a weird way
 const result = asyncExecute(\"non_existing_command\", /* args */ []);
diff --git a/tests/comments/if.js b/tests/comments/if.js
new file mode 100644
index 000000000..a542793bd
--- /dev/null
+++ b/tests/comments/if.js
@@ -0,0 +1,21 @@
+if (1)
+// comment
+{
+  false
+}
+// comment
+else if (2)
+  true
+// multi
+// ple
+// lines
+else if (3)
+  // existing comment
+  true
+// okay?
+else if (4) {
+  // empty with existing comment
+}
+// comment
+else {
+}
diff --git a/tests/jsx_escape/__snapshots__/jsfmt.spec.js.snap b/tests/jsx_escape/__snapshots__/jsfmt.spec.js.snap
index 0a79acf98..dc6d640f0 100644
--- a/tests/jsx_escape/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/jsx_escape/__snapshots__/jsfmt.spec.js.snap
@@ -11,3 +11,38 @@ exports[`test escape.js 2`] = `
 <div>&lt;</div>;
 "
 `;
+
+exports[`test nbsp.js 1`] = `
+"many_nbsp = <div>&nbsp; &nbsp; </div>
+single_nbsp = <div>&nbsp;</div>
+many_raw_nbsp = <div>   </div>
+
+
+amp = <span>foo &amp; bar</span>
+raw_amp = <span>foo & bar</span>
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+many_nbsp = <div>&nbsp; &nbsp; </div>;
+single_nbsp = <div>&nbsp;</div>;
+many_raw_nbsp = <div>&nbsp;&nbsp;&nbsp;</div>;
+amp = <span>foo &amp; bar</span>;
+raw_amp = <span>foo & bar</span>;
+"
+`;
+
+exports[`test nbsp.js 2`] = `
+"many_nbsp = <div>&nbsp; &nbsp; </div>
+single_nbsp = <div>&nbsp;</div>
+many_raw_nbsp = <div>   </div>
+
+
+amp = <span>foo &amp; bar</span>
+raw_amp = <span>foo & bar</span>
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+many_nbsp = <div>&nbsp; &nbsp; </div>;
+single_nbsp = <div>&nbsp;</div>;
+many_raw_nbsp = <div>&nbsp;&nbsp;&nbsp;</div>;
+
+amp = <span>foo & bar</span>;
+raw_amp = <span>foo & bar</span>;
+"
+`;
diff --git a/tests/jsx_escape/jsfmt.spec.js b/tests/jsx_escape/jsfmt.spec.js
index d5ec770a9..7b8a9e6ab 100644
--- a/tests/jsx_escape/jsfmt.spec.js
+++ b/tests/jsx_escape/jsfmt.spec.js
@@ -1,2 +1,2 @@
-run_spec(__dirname);
+run_spec(__dirname, {parser: 'flow'});
 run_spec(__dirname, {parser: 'babylon'});
diff --git a/tests/jsx_escape/nbsp.js b/tests/jsx_escape/nbsp.js
new file mode 100644
index 000000000..87f6989a1
--- /dev/null
+++ b/tests/jsx_escape/nbsp.js
@@ -0,0 +1,7 @@
+many_nbsp = <div>&nbsp; &nbsp; </div>
+single_nbsp = <div>&nbsp;</div>
+many_raw_nbsp = <div>   </div>
+
+
+amp = <span>foo &amp; bar</span>
+raw_amp = <span>foo & bar</span>
diff --git a/tests/unary/__snapshots__/jsfmt.spec.js.snap b/tests/unary/__snapshots__/jsfmt.spec.js.snap
index 10e8ed0fa..1adb04cc4 100644
--- a/tests/unary/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/unary/__snapshots__/jsfmt.spec.js.snap
@@ -1,3 +1,16 @@
+exports[`test object.js 1`] = `
+"state = {
+  // students
+  hoverColumn: -1
+};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+state = {
+  // students
+  hoverColumn: -1
+};
+"
+`;
+
 exports[`test series.js 1`] = `
 "1 + ++x;
 1 + x++;
diff --git a/tests/unary/object.js b/tests/unary/object.js
new file mode 100644
index 000000000..fa406a975
--- /dev/null
+++ b/tests/unary/object.js
@@ -0,0 +1,4 @@
+state = {
+  // students
+  hoverColumn: -1
+};