[Pkg-javascript-commits] [less.js] 61/285: much improved comment parsing

Jonas Smedegaard dr at jones.dk
Mon Oct 26 23:23:38 UTC 2015


This is an automated email from the git hooks/post-receive script.

js pushed a commit to annotated tag v2.0.0
in repository less.js.

commit cde9b79b6c424590fe1238418edb45c57d6dea69
Author: Luke Page <luke.a.page at gmail.com>
Date:   Sun Aug 17 11:30:18 2014 +0100

    much improved comment parsing
---
 lib/less/parser.js                        | 113 ++++++++++++++++++------------
 lib/less/tree/comment.js                  |  10 +--
 test/css/comments.css                     |   4 ++
 test/css/comments2.css                    |  12 ++++
 test/css/debug/linenumbers-all.css        |   1 +
 test/css/debug/linenumbers-comments.css   |   1 +
 test/css/debug/linenumbers-mediaquery.css |   1 +
 test/less-test.js                         |  26 +++----
 test/less/comments.less                   |   5 ++
 test/less/comments2.less                  |  20 ++++++
 test/less/errors/comment-in-selector.less |   1 -
 test/less/errors/comment-in-selector.txt  |   2 -
 12 files changed, 132 insertions(+), 64 deletions(-)

diff --git a/lib/less/parser.js b/lib/less/parser.js
index b7d4a33..05e0d6c 100644
--- a/lib/less/parser.js
+++ b/lib/less/parser.js
@@ -156,7 +156,7 @@ var Parser = function Parser(env) {
     }
     function isWhitespace(str, pos) {
         var code = str.charCodeAt(pos | 0);
-        return (code <= 32) && (code === 32 || code === 10 || code === 9);
+        return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF);
     }
     //
     // Parse from a token, regexp or string, and move forward if match
@@ -228,21 +228,56 @@ var Parser = function Parser(env) {
         return tok;
     }
 
+    var CHARCODE_SPACE = 32,
+        CHARCODE_TAB = 9,
+        CHARCODE_LF = 10,
+        CHARCODE_CR = 13,
+        CHARCODE_FORWARD_SLASH = 47;
+
+    var autoCommentAbsorb = true,
+        commentStore = [];
+
     function skipWhitespace(length) {
         var oldi = i, oldj = j,
             curr = i - currentPos,
             endIndex = i + current.length - curr,
             mem = (i += length),
             inp = input,
-            c;
+            c, nextChar, comment;
 
         for (; i < endIndex; i++) {
             c = inp.charCodeAt(i);
-            if (c > 32) {
+
+            if (autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
+                nextChar = inp[i + 1];
+                if (nextChar === '/') {
+                    comment = {index: i, isLineComment: true};
+                    var nextNewLine = inp.indexOf("\n", i + 1);
+                    if (nextNewLine < 0) {
+                        nextNewLine = endIndex;
+                    }
+                    i = nextNewLine;
+                    comment.text = inp.substr(comment.i, i - comment.i);
+                    commentStore.push(comment);
+                    continue;
+                } else if (nextChar === '*') {
+                    var haystack = inp.substr(i);
+                    var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//);
+                    if (comment_search_result) {
+                        comment = {
+                            index: i,
+                            text: comment_search_result[0],
+                            isLineComment: false
+                        };
+                        i += comment.text.length - 1;
+                        commentStore.push(comment);
+                        continue;
+                    }
+                }
                 break;
             }
 
-            if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) {
+            if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
                 break;
             }
          }
@@ -403,6 +438,7 @@ var Parser = function Parser(env) {
             // with the `root` property set to true, so no `{}` are
             // output. The callback is called when the input is parsed.
             try {
+                skipWhitespace(0);
                 root = new(tree.Ruleset)(null, this.parsers.primary());
                 root.root = true;
                 root.firstRoot = true;
@@ -530,7 +566,7 @@ var Parser = function Parser(env) {
             // string, so we've got a parsing error.
             //
             // We try to extract a \n delimited string,
-            // showing the line where the parse error occured.
+            // showing the line where the parse error occurred.
             // We split it up into two parts (the part which parsed,
             // and the part which didn't), so we can color them differently.
             if (i < input.length - 1) {
@@ -643,8 +679,16 @@ var Parser = function Parser(env) {
 
                 while (current)
                 {
+                    while(true) {
+                        node = this.comment();
+                        if (!node) { break; }
+                        root.push(node);
+                    }
+                    if (peekChar('}')) {
+                        break;
+                    }
                     node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() ||
-                        mixin.call() || this.comment() || this.rulesetCall() || this.directive();
+                        mixin.call() || this.rulesetCall() || this.directive();
                     if (node) {
                         root.push(node);
                     } else {
@@ -652,43 +696,18 @@ var Parser = function Parser(env) {
                             break;
                         }
                     }
-                    if (peekChar('}')) {
-                        break;
-                    }
                 }
 
                 return root;
             },
 
-            // We create a Comment node for CSS comments `/* */`,
-            // but keep the LeSS comments `//` silent, by just skipping
-            // over them.
+            // comments are collected by the main parsing mechanism and then assigned to nodes
+            // where the current structure allows it
             comment: function () {
-                var comment;
-
-                if (input.charAt(i) !== '/') { return; }
-
-                if (input.charAt(i + 1) === '/') {
-                    return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo);
-                }
-                comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/);
-                if (comment) {
-                    return new(tree.Comment)(comment, false, i, env.currentFileInfo);
-                }
-            },
-
-            comments: function () {
-                var comment, comments = [];
-
-                while(true) {
-                    comment = this.comment();
-                    if (!comment) {
-                        break;
-                    }
-                    comments.push(comment);
+                if (commentStore.length) {
+                    var comment = commentStore.shift();
+                    return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, env.currentFileInfo);
                 }
-
-                return comments;
             },
 
             //
@@ -824,9 +843,13 @@ var Parser = function Parser(env) {
                         return;
                     }
 
+                    autoCommentAbsorb = false;
+
                     value = this.quoted() || this.variable() ||
                             $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
 
+                    autoCommentAbsorb = true;
+
                     expectChar(')');
 
                     return new(tree.URL)((value.value != null || value instanceof tree.Variable)
@@ -1062,7 +1085,7 @@ var Parser = function Parser(env) {
                         if (isCall) {
                             arg = parsers.detachedRuleset() || parsers.expression();
                         } else {
-                            parsers.comments();
+                            commentStore.length = 0;
                             if (input.charAt(i) === '.' && $re(/^\.{3}/)) {
                                 returner.variadic = true;
                                 if ($char(";") && !isSemiColonSeperated) {
@@ -1212,7 +1235,7 @@ var Parser = function Parser(env) {
                             return;
                         }
 
-                        parsers.comments();
+                        commentStore.length = 0;
 
                         if ($re(/^when/)) { // Guard
                             cond = expect(parsers.conditions, 'expected condition');
@@ -1239,9 +1262,8 @@ var Parser = function Parser(env) {
             entity: function () {
                 var entities = this.entities;
 
-                return entities.literal() || entities.variable() || entities.url() ||
-                       entities.call()    || entities.keyword()  || entities.javascript() ||
-                       this.comment();
+                return this.comment() || entities.literal() || entities.variable() || entities.url() ||
+                       entities.call()    || entities.keyword()  || entities.javascript();
             },
 
             //
@@ -1447,7 +1469,7 @@ var Parser = function Parser(env) {
                         break;
                     }
                     if (selectors) { selectors.push(s); } else { selectors = [ s ]; }
-                    this.comments();
+                    commentStore.length = 0;
                     if (s.condition && selectors.length > 1) {
                         error("Guards are only currently allowed on a single selector.");
                     }
@@ -1455,7 +1477,7 @@ var Parser = function Parser(env) {
                     if (s.condition) {
                         error("Guards are only currently allowed on a single selector.");
                     }
-                    this.comments();
+                    commentStore.length = 0;
                 }
 
                 if (selectors && (rules = this.block())) {
@@ -1930,6 +1952,11 @@ var Parser = function Parser(env) {
                 var entities = [], e, delim;
 
                 do {
+                    e = this.comment();
+                    if (e) {
+                        entities.push(e);
+                        continue;
+                    }
                     e = this.addition() || this.entity();
                     if (e) {
                         entities.push(e);
diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js
index 05bc220..10c1e79 100644
--- a/lib/less/tree/comment.js
+++ b/lib/less/tree/comment.js
@@ -1,8 +1,8 @@
 module.exports = function (tree) {
 
-var Comment = function (value, silent, index, currentFileInfo) {
+var Comment = function (value, isLineComment, index, currentFileInfo) {
     this.value = value;
-    this.silent = !!silent;
+    this.isLineComment = isLineComment;
     this.currentFileInfo = currentFileInfo;
 };
 Comment.prototype = {
@@ -11,13 +11,13 @@ Comment.prototype = {
         if (this.debugInfo) {
             output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index);
         }
-        output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n
+        output.add(this.value);
     },
     toCSS: tree.toCSS,
     isSilent: function(env) {
         var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
-            isCompressed = env.compress && !this.value.match(/^\/\*!/);
-        return this.silent || isReference || isCompressed;
+            isCompressed = env.compress && this.value[2] !== "!";
+        return this.isLineComment || isReference || isCompressed;
     },
     eval: function () { return this; },
     markReferenced: function () {
diff --git a/test/css/comments.css b/test/css/comments.css
index 3d86fb7..f79b759 100644
--- a/test/css/comments.css
+++ b/test/css/comments.css
@@ -67,3 +67,7 @@
   color: #A33;
 }
 /* } */
+/*by block */
+#output-block {
+  comment: /* // Not commented out // */;
+}
diff --git a/test/css/comments2.css b/test/css/comments2.css
new file mode 100644
index 0000000..1932655
--- /dev/null
+++ b/test/css/comments2.css
@@ -0,0 +1,12 @@
+ at -webkit-keyframes hover {
+  /* Safari and Chrome */
+}
+.bg {
+  background-image: linear-gradient(#333333 /*{comment}*/, #111111);
+}
+#planadvisor,
+.first,
+.planning {
+  margin: 10px;
+  total-width: (1 * 6em * 12) + (2em * 12);
+}
diff --git a/test/css/debug/linenumbers-all.css b/test/css/debug/linenumbers-all.css
index 87022ae..eabeb41 100644
--- a/test/css/debug/linenumbers-all.css
+++ b/test/css/debug/linenumbers-all.css
@@ -2,6 +2,7 @@
 /* line 1, {pathimport}test.less */
 @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000031}}
 /* @charset "ISO-8859-1"; */
+
 /* line 23, {pathimport}test.less */
 @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}}
 .tst3 {
diff --git a/test/css/debug/linenumbers-comments.css b/test/css/debug/linenumbers-comments.css
index e5d6bb3..a00fd7f 100644
--- a/test/css/debug/linenumbers-comments.css
+++ b/test/css/debug/linenumbers-comments.css
@@ -1,6 +1,7 @@
 @charset "UTF-8";
 /* line 1, {pathimport}test.less */
 /* @charset "ISO-8859-1"; */
+
 /* line 23, {pathimport}test.less */
 .tst3 {
   color: grey;
diff --git a/test/css/debug/linenumbers-mediaquery.css b/test/css/debug/linenumbers-mediaquery.css
index e252ab3..11cdd24 100644
--- a/test/css/debug/linenumbers-mediaquery.css
+++ b/test/css/debug/linenumbers-mediaquery.css
@@ -1,6 +1,7 @@
 @charset "UTF-8";
 @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000031}}
 /* @charset "ISO-8859-1"; */
+
 @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}}
 .tst3 {
   color: grey;
diff --git a/test/less-test.js b/test/less-test.js
index ad046d1..22e45a0 100644
--- a/test/less-test.js
+++ b/test/less-test.js
@@ -38,8 +38,8 @@ module.exports = function() {
             } else if (err) {
                 fail("ERROR: " + (err && err.message));
                 if (isVerbose) {
-                    console.error();
-                    console.error(err.stack);
+                    process.stdout.write("\n");
+                    process.stdout.write(err.stack + "\n");
                 }
             } else {
                 difference("FAIL", expectedSourcemap, sourcemap);
@@ -138,8 +138,8 @@ module.exports = function() {
                     else if (err) {
                         fail("ERROR: " + (err && err.message));
                         if (isVerbose) {
-                            console.error();
-                            console.error(err.stack);
+                            process.stdout.write("\n");
+                            process.stdout.write(err.stack + "\n");
                         }
                     } else {
                         difference("FAIL", css, less);
@@ -158,17 +158,17 @@ module.exports = function() {
               process.stdout.write(item.value);
           }
         });
-        console.log("");
+        process.stdout.write("\n");
     }
 
     function fail(msg) {
-        console.error(stylize(msg, 'red'));
+        process.stdout.write(stylize(msg, 'red') + "\n");
         failedTests++;
         endTest();
     }
 
     function difference(msg, left, right) {
-        console.warn(stylize(msg, 'yellow'));
+        process.stdout.write(stylize(msg, 'yellow') + "\n");
         failedTests++;
 
         diff(left, right);
@@ -176,7 +176,7 @@ module.exports = function() {
     }
 
     function ok(msg) {
-        console.log(stylize(msg, 'green'));
+        process.stdout.write(stylize(msg, 'green') + "\n");
         passedTests++;
         endTest();
     }
@@ -184,15 +184,15 @@ module.exports = function() {
     function endTest() {
         var leaked = checkGlobalLeaks();
         if (failedTests + passedTests === totalTests) {
-            console.log("");
+            process.stdout.write("\n");
             if (failedTests > 0) {
-                console.error(failedTests + stylize(" Failed", "red") + ", " + passedTests + " passed");
+                process.stdout.write(failedTests + stylize(" Failed", "red") + ", " + passedTests + " passed\n");
             } else {
-                console.log(stylize("All Passed ", "green") + passedTests + " run");
+                process.stdout.write(stylize("All Passed ", "green") + passedTests + " run\n");
             }
             if (leaked.length > 0) {
-                console.log("");
-                console.warn(stylize("Global leak detected: ", "red") + leaked.join(', '));
+                process.stdout.write("\n");
+                process.stdout.write(stylize("Global leak detected: ", "red") + leaked.join(', ') + "\n");
             }
 
             if (leaked.length || failedTests) {
diff --git a/test/less/comments.less b/test/less/comments.less
index e2569b6..34b3dbb 100644
--- a/test/less/comments.less
+++ b/test/less/comments.less
@@ -81,3 +81,8 @@
 //
 
 /*  *//* { *//*  *//*  *//*  */#div { color:#A33; }/* } */
+
+// line immediatly followed
+/*by block */
+ at string_w_comment: ~"/* // Not commented out // */";
+#output-block { comment: @string_w_comment; }
diff --git a/test/less/comments2.less b/test/less/comments2.less
new file mode 100644
index 0000000..19ef5d7
--- /dev/null
+++ b/test/less/comments2.less
@@ -0,0 +1,20 @@
+ at media all and/*! */(max-width:1024px) {}
+ at -webkit-keyframes hover /* Safari and Chrome */{  }
+.bg {
+  background-image: linear-gradient(#333 /*{comment}*/, #111);
+}
+#planadvisor,
+/*comment*//*comment*/
+.first,/*comment*//*comment*/.planning {
+    margin:10px;
+    total-width: @total-width;
+}
+ at base                       :   1;
+ at column-width               :   @base * 6em;                //      Width of column             */
+ at gutter-width               :   2em;                        //      Width of column spacing     */
+ at columns                    :   12;                         //      Number of Columns           */
+ at gridsystem-width           :   (@column-width *            //      For calculating the total   */
+                                    @columns) + (               //      width of the content area.  */
+                                    @gutter-width *             //      We strongly recommend you   */
+                                    @columns);                  //      do not change this formula. */
+ at total-width                :   @gridsystem-width;          //      set to 100% for fluid grid  */
diff --git a/test/less/errors/comment-in-selector.less b/test/less/errors/comment-in-selector.less
deleted file mode 100644
index a7d2639..0000000
--- a/test/less/errors/comment-in-selector.less
+++ /dev/null
@@ -1 +0,0 @@
-#gaga /* Comment */ span { color: red }
\ No newline at end of file
diff --git a/test/less/errors/comment-in-selector.txt b/test/less/errors/comment-in-selector.txt
deleted file mode 100644
index e48f878..0000000
--- a/test/less/errors/comment-in-selector.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ParseError: Unrecognised input in {path}comment-in-selector.less on line 1, column 21:
-1 #gaga /* Comment */ span { color: red }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/less.js.git



More information about the Pkg-javascript-commits mailing list