much improved comment parsing

This commit is contained in:
Luke Page
2014-08-17 11:30:18 +01:00
parent d7e7ddba44
commit cde9b79b6c
12 changed files with 132 additions and 64 deletions

View File

@@ -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);
if (commentStore.length) {
var comment = commentStore.shift();
return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, 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);
}
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);

View File

@@ -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 () {