162 lines
4.4 KiB
JavaScript
162 lines
4.4 KiB
JavaScript
|
/* Simple parser for CSS */
|
||
|
|
||
|
var CSSParser = Editor.Parser = (function() {
|
||
|
var tokenizeCSS = (function() {
|
||
|
function normal(source, setState) {
|
||
|
var ch = source.next();
|
||
|
if (ch == "@") {
|
||
|
source.nextWhileMatches(/\w/);
|
||
|
return "css-at";
|
||
|
}
|
||
|
else if (ch == "/" && source.equals("*")) {
|
||
|
setState(inCComment);
|
||
|
return null;
|
||
|
}
|
||
|
else if (ch == "<" && source.equals("!")) {
|
||
|
setState(inSGMLComment);
|
||
|
return null;
|
||
|
}
|
||
|
else if (ch == "=") {
|
||
|
return "css-compare";
|
||
|
}
|
||
|
else if (source.equals("=") && (ch == "~" || ch == "|")) {
|
||
|
source.next();
|
||
|
return "css-compare";
|
||
|
}
|
||
|
else if (ch == "\"" || ch == "'") {
|
||
|
setState(inString(ch));
|
||
|
return null;
|
||
|
}
|
||
|
else if (ch == "#") {
|
||
|
source.nextWhileMatches(/\w/);
|
||
|
return "css-hash";
|
||
|
}
|
||
|
else if (ch == "!") {
|
||
|
source.nextWhileMatches(/[ \t]/);
|
||
|
source.nextWhileMatches(/\w/);
|
||
|
return "css-important";
|
||
|
}
|
||
|
else if (/\d/.test(ch)) {
|
||
|
source.nextWhileMatches(/[\w.%]/);
|
||
|
return "css-unit";
|
||
|
}
|
||
|
else if (/[,.+>*\/]/.test(ch)) {
|
||
|
return "css-select-op";
|
||
|
}
|
||
|
else if (/[;{}:\[\]]/.test(ch)) {
|
||
|
return "css-punctuation";
|
||
|
}
|
||
|
else {
|
||
|
source.nextWhileMatches(/[\w\\\-_]/);
|
||
|
return "css-identifier";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function inCComment(source, setState) {
|
||
|
var maybeEnd = false;
|
||
|
while (!source.endOfLine()) {
|
||
|
var ch = source.next();
|
||
|
if (maybeEnd && ch == "/") {
|
||
|
setState(normal);
|
||
|
break;
|
||
|
}
|
||
|
maybeEnd = (ch == "*");
|
||
|
}
|
||
|
return "css-comment";
|
||
|
}
|
||
|
|
||
|
function inSGMLComment(source, setState) {
|
||
|
var dashes = 0;
|
||
|
while (!source.endOfLine()) {
|
||
|
var ch = source.next();
|
||
|
if (dashes >= 2 && ch == ">") {
|
||
|
setState(normal);
|
||
|
break;
|
||
|
}
|
||
|
dashes = (ch == "-") ? dashes + 1 : 0;
|
||
|
}
|
||
|
return "css-comment";
|
||
|
}
|
||
|
|
||
|
function inString(quote) {
|
||
|
return function(source, setState) {
|
||
|
var escaped = false;
|
||
|
while (!source.endOfLine()) {
|
||
|
var ch = source.next();
|
||
|
if (ch == quote && !escaped)
|
||
|
break;
|
||
|
escaped = !escaped && ch == "\\";
|
||
|
}
|
||
|
if (!escaped)
|
||
|
setState(normal);
|
||
|
return "css-string";
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return function(source, startState) {
|
||
|
return tokenizer(source, startState || normal);
|
||
|
};
|
||
|
})();
|
||
|
|
||
|
function indentCSS(inBraces, inRule, base) {
|
||
|
return function(nextChars) {
|
||
|
if (!inBraces || /^\}/.test(nextChars)) return base;
|
||
|
else if (inRule) return base + indentUnit * 2;
|
||
|
else return base + indentUnit;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// This is a very simplistic parser -- since CSS does not really
|
||
|
// nest, it works acceptably well, but some nicer colouroing could
|
||
|
// be provided with a more complicated parser.
|
||
|
function parseCSS(source, basecolumn) {
|
||
|
basecolumn = basecolumn || 0;
|
||
|
var tokens = tokenizeCSS(source);
|
||
|
var inBraces = false, inRule = false, inDecl = false;;
|
||
|
|
||
|
var iter = {
|
||
|
next: function() {
|
||
|
var token = tokens.next(), style = token.style, content = token.content;
|
||
|
|
||
|
if (style == "css-hash")
|
||
|
style = token.style = inRule ? "css-colorcode" : "css-identifier";
|
||
|
if (style == "css-identifier") {
|
||
|
if (inRule) token.style = "css-value";
|
||
|
else if (!inBraces && !inDecl) token.style = "css-selector";
|
||
|
}
|
||
|
|
||
|
if (content == "\n")
|
||
|
token.indentation = indentCSS(inBraces, inRule, basecolumn);
|
||
|
|
||
|
if (content == "{" && inDecl == "@media")
|
||
|
inDecl = false;
|
||
|
else if (content == "{")
|
||
|
inBraces = true;
|
||
|
else if (content == "}")
|
||
|
inBraces = inRule = inDecl = false;
|
||
|
else if (content == ";")
|
||
|
inRule = inDecl = false;
|
||
|
else if (inBraces && style != "css-comment" && style != "whitespace")
|
||
|
inRule = true;
|
||
|
else if (!inBraces && style == "css-at")
|
||
|
inDecl = content;
|
||
|
|
||
|
return token;
|
||
|
},
|
||
|
|
||
|
copy: function() {
|
||
|
var _inBraces = inBraces, _inRule = inRule, _tokenState = tokens.state;
|
||
|
return function(source) {
|
||
|
tokens = tokenizeCSS(source, _tokenState);
|
||
|
inBraces = _inBraces;
|
||
|
inRule = _inRule;
|
||
|
return iter;
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
return iter;
|
||
|
}
|
||
|
|
||
|
return {make: parseCSS, electricChars: "}"};
|
||
|
})();
|