[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