Bug 1226810 - Upgrade pretty-fast to latest version;r=fitzgen
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 16 Dec 2015 10:15:23 -0800
changeset 315857 09c6b51e11ef7db960db25f01853474dccf44ac4
parent 315856 e78afd6c4ede40e282ccf88ec5994c7eb6e68475
child 315858 7489db357bd48b2bd1751b474c3a91cfa55a9420
push id8469
push userbgrinstead@mozilla.com
push dateWed, 16 Dec 2015 19:53:11 +0000
reviewersfitzgen
bugs1226810
milestone46.0a1
Bug 1226810 - Upgrade pretty-fast to latest version;r=fitzgen
devtools/shared/pretty-fast/UPGRADING.md
devtools/shared/pretty-fast/pretty-fast.js
devtools/shared/pretty-fast/tests/unit/test.js
--- a/devtools/shared/pretty-fast/UPGRADING.md
+++ b/devtools/shared/pretty-fast/UPGRADING.md
@@ -1,7 +1,9 @@
 # UPGRADING
 
 1. `git clone https://github.com/mozilla/pretty-fast.git`
 
 2. Copy `pretty-fast/pretty-fast.js` to `devtools/shared/pretty-fast/pretty-fast.js`
 
 3. Copy `pretty-fast/test.js` to `devtools/shared/pretty-fast/tests/unit/test.js`
+
+4. If necessary, upgrade acorn (see devtools/shared/acorn/UPGRADING.md)
\ No newline at end of file
--- a/devtools/shared/pretty-fast/pretty-fast.js
+++ b/devtools/shared/pretty-fast/pretty-fast.js
@@ -12,17 +12,17 @@
   } else if (typeof exports === "object") {
     module.exports = factory();
   } else {
     root.prettyFast = factory();
   }
 }(this, function () {
   "use strict";
 
-  var acorn = this.acorn || require("acorn/acorn");
+  var acorn = this.acorn || require("acorn/dist/acorn");
   var sourceMap = this.sourceMap || require("source-map");
   var SourceNode = sourceMap.SourceNode;
 
   // If any of these tokens are seen before a "[" token, we know that "[" token
   // is the start of an array literal, rather than a property access.
   //
   // The only exception is "}", which would need to be disambiguated by
   // parsing. The majority of the time, an open bracket following a closing
@@ -80,27 +80,27 @@
    *        The token we want to determine if it is an array literal.
    * @param Object lastToken
    *        The last token we added to the pretty printed results.
    *
    * @returns Boolean
    *          True if we believe it is an array literal, false otherwise.
    */
   function isArrayLiteral(token, lastToken) {
-    if (token.type.type != "[") {
+    if (token.type.label != "[") {
       return false;
     }
     if (!lastToken) {
       return true;
     }
     if (lastToken.type.isAssign) {
       return true;
     }
     return !!PRE_ARRAY_LITERAL_TOKENS[
-      lastToken.type.keyword || lastToken.type.type
+      lastToken.type.keyword || lastToken.type.label
     ];
   }
 
   // If any of these tokens are followed by a token on a new line, we know that
   // ASI cannot happen.
   var PREVENT_ASI_AFTER_TOKENS = {
     // Binary operators
     "*": true,
@@ -205,25 +205,25 @@
    *
    * @returns Boolean
    *          True if we believe ASI occurs.
    */
   function isASI(token, lastToken) {
     if (!lastToken) {
       return false;
     }
-    if (token.startLoc.line === lastToken.startLoc.line) {
+    if (token.loc.start.line === lastToken.loc.start.line) {
       return false;
     }
     if (PREVENT_ASI_AFTER_TOKENS[
-      lastToken.type.type || lastToken.type.keyword
+      lastToken.type.label || lastToken.type.keyword
     ]) {
       return false;
     }
-    if (PREVENT_ASI_BEFORE_TOKENS[token.type.type || token.type.keyword]) {
+    if (PREVENT_ASI_BEFORE_TOKENS[token.type.label || token.type.keyword]) {
       return false;
     }
     return true;
   }
 
   /**
    * Determine if we have encountered a getter or setter.
    *
@@ -238,19 +238,19 @@
    *        The stack of open parens/curlies/brackets/etc.
    *
    * @returns Boolean
    *          True if this is a getter or setter.
    */
   function isGetterOrSetter(token, lastToken, stack) {
     return stack[stack.length - 1] == "{"
       && lastToken
-      && lastToken.type.type == "name"
+      && lastToken.type.label == "name"
       && (lastToken.value == "get" || lastToken.value == "set")
-      && token.type.type == "name";
+      && token.type.label == "name";
   }
 
   /**
    * Determine if we should add a newline after the given token.
    *
    * @param Object token
    *        The token we are looking at.
    * @param Array stack
@@ -258,22 +258,22 @@
    *
    * @returns Boolean
    *          True if we should add a newline.
    */
   function isLineDelimiter(token, stack) {
     if (token.isArrayLiteral) {
       return true;
     }
-    var ttt = token.type.type;
+    var ttl = token.type.label;
     var top = stack[stack.length - 1];
-    return ttt == ";" && top != "("
-      || ttt == "{"
-      || ttt == "," && top != "("
-      || ttt == ":" && (top == "case" || top == "default");
+    return ttl == ";" && top != "("
+      || ttl == "{"
+      || ttl == "," && top != "("
+      || ttl == ":" && (top == "case" || top == "default");
   }
 
   /**
    * Append the necessary whitespace to the result after we have added the given
    * token.
    *
    * @param Object token
    *        The token that was just added to the result.
@@ -283,17 +283,17 @@
    *        The stack of open parens/curlies/brackets/etc.
    *
    * @returns Boolean
    *          Returns true if we added a newline to result, false in all other
    *          cases.
    */
   function appendNewline(token, write, stack) {
     if (isLineDelimiter(token, stack)) {
-      write("\n", token.startLoc.line, token.startLoc.column);
+      write("\n", token.loc.start.line, token.loc.start.column);
       return true;
     }
     return false;
   }
 
   /**
    * Determines if we need to add a space between the last token we added and
    * the token we are about to add.
@@ -310,61 +310,61 @@
       }
       if (lastToken.type.isAssign) {
         return true;
       }
       if (lastToken.type.binop != null) {
         return true;
       }
 
-      var ltt = lastToken.type.type;
+      var ltt = lastToken.type.label;
       if (ltt == "?") {
         return true;
       }
       if (ltt == ":") {
         return true;
       }
       if (ltt == ",") {
         return true;
       }
       if (ltt == ";") {
         return true;
       }
 
       var ltk = lastToken.type.keyword;
       if (ltk != null) {
         if (ltk == "break" || ltk == "continue" || ltk == "return") {
-          return token.type.type != ";";
+          return token.type.label != ";";
         }
         if (ltk != "debugger"
             && ltk != "null"
             && ltk != "true"
             && ltk != "false"
             && ltk != "this"
             && ltk != "default") {
           return true;
         }
       }
 
-      if (ltt == ")" && (token.type.type != ")"
-                         && token.type.type != "]"
-                         && token.type.type != ";"
-                         && token.type.type != ","
-                         && token.type.type != ".")) {
+      if (ltt == ")" && (token.type.label != ")"
+                         && token.type.label != "]"
+                         && token.type.label != ";"
+                         && token.type.label != ","
+                         && token.type.label != ".")) {
         return true;
       }
     }
 
     if (token.type.isAssign) {
       return true;
     }
     if (token.type.binop != null) {
       return true;
     }
-    if (token.type.type == "?") {
+    if (token.type.label == "?") {
       return true;
     }
 
     return false;
   }
 
   /**
    * Add the required whitespace before this token, whether that is a single
@@ -384,96 +384,96 @@
    * @param Number indentLevel
    *        The number of indents deep we are.
    * @param Array stack
    *        The stack of open curlies, brackets, etc.
    */
   function prependWhiteSpace(token, lastToken, addedNewline, write, options,
                              indentLevel, stack) {
     var ttk = token.type.keyword;
-    var ttt = token.type.type;
+    var ttl = token.type.label;
     var newlineAdded = addedNewline;
-    var ltt = lastToken ? lastToken.type.type : null;
+    var ltt = lastToken ? lastToken.type.label : null;
 
     // Handle whitespace and newlines after "}" here instead of in
     // `isLineDelimiter` because it is only a line delimiter some of the
     // time. For example, we don't want to put "else if" on a new line after
     // the first if's block.
     if (lastToken && ltt == "}") {
       if (ttk == "while" && stack[stack.length - 1] == "do") {
         write(" ",
-              lastToken.startLoc.line,
-              lastToken.startLoc.column);
+              lastToken.loc.start.line,
+              lastToken.loc.start.column);
       } else if (ttk == "else" ||
                  ttk == "catch" ||
                  ttk == "finally") {
         write(" ",
-              lastToken.startLoc.line,
-              lastToken.startLoc.column);
-      } else if (ttt != "(" &&
-                 ttt != ";" &&
-                 ttt != "," &&
-                 ttt != ")" &&
-                 ttt != ".") {
+              lastToken.loc.start.line,
+              lastToken.loc.start.column);
+      } else if (ttl != "(" &&
+                 ttl != ";" &&
+                 ttl != "," &&
+                 ttl != ")" &&
+                 ttl != ".") {
         write("\n",
-              lastToken.startLoc.line,
-              lastToken.startLoc.column);
+              lastToken.loc.start.line,
+              lastToken.loc.start.column);
         newlineAdded = true;
       }
     }
 
     if (isGetterOrSetter(token, lastToken, stack)) {
       write(" ",
-            lastToken.startLoc.line,
-            lastToken.startLoc.column);
+            lastToken.loc.start.line,
+            lastToken.loc.start.column);
     }
 
-    if (ttt == ":" && stack[stack.length - 1] == "?") {
+    if (ttl == ":" && stack[stack.length - 1] == "?") {
       write(" ",
-            lastToken.startLoc.line,
-            lastToken.startLoc.column);
+            lastToken.loc.start.line,
+            lastToken.loc.start.column);
     }
 
     if (lastToken && ltt != "}" && ttk == "else") {
       write(" ",
-            lastToken.startLoc.line,
-            lastToken.startLoc.column);
+            lastToken.loc.start.line,
+            lastToken.loc.start.column);
     }
 
     function ensureNewline() {
       if (!newlineAdded) {
         write("\n",
-              lastToken.startLoc.line,
-              lastToken.startLoc.column);
+              lastToken.loc.start.line,
+              lastToken.loc.start.column);
         newlineAdded = true;
       }
     }
 
     if (isASI(token, lastToken)) {
       ensureNewline();
     }
 
-    if (decrementsIndent(ttt, stack)) {
+    if (decrementsIndent(ttl, stack)) {
       ensureNewline();
     }
 
     if (newlineAdded) {
       if (ttk == "case" || ttk == "default") {
         write(repeat(options.indent, indentLevel - 1),
-              token.startLoc.line,
-              token.startLoc.column);
+              token.loc.start.line,
+              token.loc.start.column);
       } else {
         write(repeat(options.indent, indentLevel),
-              token.startLoc.line,
-              token.startLoc.column);
+              token.loc.start.line,
+              token.loc.start.column);
       }
     } else if (needsSpaceAfter(token, lastToken)) {
       write(" ",
-            lastToken.startLoc.line,
-            lastToken.startLoc.column);
+            lastToken.loc.start.line,
+            lastToken.loc.start.column);
     }
   }
 
   /**
    * Repeat the `str` string `n` times.
    *
    * @param String str
    *        The string to be repeated.
@@ -536,58 +536,58 @@
    * Add the given token to the pretty printed results.
    *
    * @param Object token
    *        The token to add.
    * @param Function write
    *        The function to write pretty printed code to the result SourceNode.
    */
   function addToken(token, write) {
-    if (token.type.type == "string") {
+    if (token.type.label == "string") {
       write("'" + sanitize(token.value) + "'",
-            token.startLoc.line,
-            token.startLoc.column);
-    } else if (token.type.type == "regexp") {
+            token.loc.start.line,
+            token.loc.start.column);
+    } else if (token.type.label == "regexp") {
       write(String(token.value.value),
-            token.startLoc.line,
-            token.startLoc.column);
+            token.loc.start.line,
+            token.loc.start.column);
     } else {
-      write(String(token.value != null ? token.value : token.type.type),
-            token.startLoc.line,
-            token.startLoc.column);
+      write(String(token.value != null ? token.value : token.type.label),
+            token.loc.start.line,
+            token.loc.start.column);
     }
   }
 
   /**
    * Returns true if the given token type belongs on the stack.
    */
   function belongsOnStack(token) {
-    var ttt = token.type.type;
+    var ttl = token.type.label;
     var ttk = token.type.keyword;
-    return ttt == "{"
-      || ttt == "("
-      || ttt == "["
-      || ttt == "?"
+    return ttl == "{"
+      || ttl == "("
+      || ttl == "["
+      || ttl == "?"
       || ttk == "do"
       || ttk == "switch"
       || ttk == "case"
       || ttk == "default";
   }
 
   /**
    * Returns true if the given token should cause us to pop the stack.
    */
   function shouldStackPop(token, stack) {
-    var ttt = token.type.type;
+    var ttl = token.type.label;
     var ttk = token.type.keyword;
     var top = stack[stack.length - 1];
-    return ttt == "]"
-      || ttt == ")"
-      || ttt == "}"
-      || (ttt == ":" && (top == "case" || top == "default" || top == "?"))
+    return ttl == "]"
+      || ttl == ")"
+      || ttl == "}"
+      || (ttl == ":" && (top == "case" || top == "default" || top == "?"))
       || (ttk == "while" && top == "do");
   }
 
   /**
    * Returns true if the given token type should cause us to decrement the
    * indent level.
    */
   function decrementsIndent(tokenType, stack) {
@@ -595,17 +595,17 @@
       || (tokenType == "]" && stack[stack.length - 1] == "[\n");
   }
 
   /**
    * Returns true if the given token should cause us to increment the indent
    * level.
    */
   function incrementsIndent(token) {
-    return token.type.type == "{"
+    return token.type.label == "{"
       || token.isArrayLiteral
       || token.type.keyword == "switch";
   }
 
   /**
    * Add a comment to the pretty printed code.
    *
    * @param Function write
@@ -708,19 +708,19 @@
     }());
 
     // Whether or not we added a newline on after we added the last token.
     var addedNewline = false;
 
     // The current token we will be adding to the pretty printed code.
     var token;
 
-    // Shorthand for token.type.type, so we don't have to repeatedly access
+    // Shorthand for token.type.label, so we don't have to repeatedly access
     // properties.
-    var ttt;
+    var ttl;
 
     // Shorthand for token.type.keyword, so we don't have to repeatedly access
     // properties.
     var ttk;
 
     // The last token we added to the pretty printed code.
     var lastToken;
 
@@ -742,87 +742,103 @@
     //   - "case"
     //   - "default"
     //
     // The difference between "[" and "[\n" is that "[\n" is used when we are
     // treating "[" and "]" tokens as line delimiters and should increment and
     // decrement the indent level when we find them.
     var stack = [];
 
-    // Acorn's tokenizer will always yield comments *before* the token they
-    // follow (unless the very first thing in the source is a comment), so we
-    // have to queue the comments in order to pretty print them in the correct
-    // location. For example, the source file:
+    // Pass through acorn's tokenizer and append tokens and comments into a
+    // single queue to process.  For example, the source file:
     //
     //     foo
     //     // a
     //     // b
     //     bar
     //
-    // When tokenized by acorn, gives us the following token stream:
+    // After this process, tokenQueue has the following token stream:
     //
-    //     [ '// a', '// b', foo, bar ]
-    var commentQueue = [];
+    //     [ foo, '// a', '// b', bar]
+    var tokenQueue = [];
 
-    var getToken = acorn.tokenize(input, {
+    var tokens = acorn.tokenizer(input, {
       locations: true,
       sourceFile: options.url,
       onComment: function (block, text, start, end, startLoc, endLoc) {
-        if (lastToken) {
-          commentQueue.push({
-            block: block,
-            text: text,
-            line: startLoc.line,
-            column: startLoc.column,
-            trailing: lastToken.endLoc.line == startLoc.line
-          });
-        } else {
-          addComment(write, indentLevel, options, block, text, startLoc.line,
-                     startLoc.column);
-          addedNewline = true;
-        }
+        tokenQueue.push({
+          type: {},
+          comment: true,
+          block: block,
+          text: text,
+          loc: { start: startLoc, end: endLoc }
+        });
       }
     });
 
     for (;;) {
-      token = getToken();
+      token = tokens.getToken();
+      tokenQueue.push(token);
+      if (token.type.label == "eof") {
+        break;
+      }
+    }
+
+    for (var i = 0; i < tokenQueue.length; i++) {
+      token = tokenQueue[i];
+
+      if (token.comment) {
+        var commentIndentLevel = indentLevel;
+        if (lastToken && (lastToken.loc.end.line == token.loc.start.line)) {
+          commentIndentLevel = 0;
+          write(" ");
+        }
+        addComment(write, commentIndentLevel, options, token.block, token.text,
+                   token.loc.start.line, token.loc.start.column);
+        addedNewline = true;
+        continue;
+      }
 
       ttk = token.type.keyword;
-      ttt = token.type.type;
+      ttl = token.type.label;
 
-      if (ttt == "eof") {
+      if (ttl == "eof") {
         if (!addedNewline) {
           write("\n");
         }
         break;
       }
 
       token.isArrayLiteral = isArrayLiteral(token, lastToken);
 
       if (belongsOnStack(token)) {
         if (token.isArrayLiteral) {
           stack.push("[\n");
         } else {
-          stack.push(ttt || ttk);
+          stack.push(ttl || ttk);
         }
       }
 
-      if (decrementsIndent(ttt, stack)) {
+      if (decrementsIndent(ttl, stack)) {
         indentLevel--;
-        if (ttt == "}"
+        if (ttl == "}"
             && stack.length > 1
             && stack[stack.length - 2] == "switch") {
           indentLevel--;
         }
       }
 
       prependWhiteSpace(token, lastToken, addedNewline, write, options,
                         indentLevel, stack);
       addToken(token, write);
-      if (commentQueue.length === 0 || !commentQueue[0].trailing) {
+
+      // If the next token is going to be a comment starting on the same line,
+      // then no need to add one here
+      var nextToken = tokenQueue[i + 1];
+      if (!nextToken || !nextToken.comment || token.loc.end.line != nextToken.loc.start.line) {
         addedNewline = appendNewline(token, write, stack);
       }
 
       if (shouldStackPop(token, stack)) {
         stack.pop();
         if (token == "}" && stack.length
             && stack[stack.length - 1] == "switch") {
           stack.pop();
@@ -833,43 +849,25 @@
         indentLevel++;
       }
 
       // Acorn's tokenizer re-uses tokens, so we have to copy the last token on
       // every iteration. We follow acorn's lead here, and reuse the lastToken
       // object the same way that acorn reuses the token object. This allows us
       // to avoid allocations and minimize GC pauses.
       if (!lastToken) {
-        lastToken = { startLoc: {}, endLoc: {} };
+        lastToken = { loc: { start: {}, end: {} } };
       }
       lastToken.start = token.start;
       lastToken.end = token.end;
-      lastToken.startLoc.line = token.startLoc.line;
-      lastToken.startLoc.column = token.startLoc.column;
-      lastToken.endLoc.line = token.endLoc.line;
-      lastToken.endLoc.column = token.endLoc.column;
+      lastToken.loc.start.line = token.loc.start.line;
+      lastToken.loc.start.column = token.loc.start.column;
+      lastToken.loc.end.line = token.loc.end.line;
+      lastToken.loc.end.column = token.loc.end.column;
       lastToken.type = token.type;
       lastToken.value = token.value;
       lastToken.isArrayLiteral = token.isArrayLiteral;
-
-      // Apply all the comments that have been queued up.
-      if (commentQueue.length) {
-        if (!addedNewline && !commentQueue[0].trailing) {
-          write("\n");
-        }
-        if (commentQueue[0].trailing) {
-          write(" ");
-        }
-        for (var i = 0, n = commentQueue.length; i < n; i++) {
-          var comment = commentQueue[i];
-          var commentIndentLevel = commentQueue[i].trailing ? 0 : indentLevel;
-          addComment(write, commentIndentLevel, options, comment.block,
-                     comment.text, comment.line, comment.column);
-        }
-        addedNewline = true;
-        commentQueue.splice(0, commentQueue.length);
-      }
     }
 
     return result.toStringWithSourceMap({ file: options.url });
   };
 
 }.bind(this)));
--- a/devtools/shared/pretty-fast/tests/unit/test.js
+++ b/devtools/shared/pretty-fast/tests/unit/test.js
@@ -500,27 +500,33 @@ var testCases = [
   {
     name: "Bug 975477 don't move end of line comments to next line",
     input: "switch (request.action) {\n" +
            "  case 'show': //$NON-NLS-0$\n" +
            "    if (localStorage.hideicon !== 'true') { //$NON-NLS-0$\n" +
            "      chrome.pageAction.show(sender.tab.id);\n" +
            "    }\n" +
            "    break;\n" +
+           "  case 'hide': /*Multiline\n" +
+           "    Comment */" +
+           "    break;\n" +
            "  default:\n" +
            "    console.warn('unknown request'); //$NON-NLS-0$\n" +
            "    // don't respond if you don't understand the message.\n" +
            "    return;\n" +
            "}\n",
     output: "switch (request.action) {\n" +
             "  case 'show': //$NON-NLS-0$\n" +
             "    if (localStorage.hideicon !== 'true') { //$NON-NLS-0$\n" +
             "      chrome.pageAction.show(sender.tab.id);\n" +
             "    }\n" +
             "    break;\n" +
+            "  case 'hide': /*Multiline\n" +
+            "    Comment */\n" +
+            "    break;\n" +
             "  default:\n" +
             "    console.warn('unknown request'); //$NON-NLS-0$\n" +
             "    // don't respond if you don't understand the message.\n" +
             "    return;\n" +
             "}\n"
   }
 
 ];