codemirror 3.21

This commit is contained in:
ilvalle
2014-01-18 10:13:56 +01:00
parent 7101762082
commit 45bc26f5eb
46 changed files with 1634 additions and 965 deletions
+347 -303
View File
@@ -3,87 +3,80 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
var indentUnit = config.indentUnit || config.tabSize || 2,
hooks = parserConfig.hooks || {},
atMediaTypes = parserConfig.atMediaTypes || {},
atMediaFeatures = parserConfig.atMediaFeatures || {},
var indentUnit = config.indentUnit,
tokenHooks = parserConfig.tokenHooks,
mediaTypes = parserConfig.mediaTypes || {},
mediaFeatures = parserConfig.mediaFeatures || {},
propertyKeywords = parserConfig.propertyKeywords || {},
colorKeywords = parserConfig.colorKeywords || {},
valueKeywords = parserConfig.valueKeywords || {},
allowNested = !!parserConfig.allowNested,
type = null;
fontProperties = parserConfig.fontProperties || {},
allowNested = parserConfig.allowNested;
var type, override;
function ret(style, tp) { type = tp; return style; }
// Tokenizers
function tokenBase(stream, state) {
var ch = stream.next();
if (hooks[ch]) {
// result[0] is style and result[1] is type
var result = hooks[ch](stream, state);
if (tokenHooks[ch]) {
var result = tokenHooks[ch](stream, state);
if (result !== false) return result;
}
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
else if (ch == "=") ret(null, "compare");
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
else if (ch == "\"" || ch == "'") {
if (ch == "@") {
stream.eatWhile(/[\w\\\-]/);
return ret("def", stream.current());
} else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
return ret(null, "compare");
} else if (ch == "\"" || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
else if (ch == "#") {
} else if (ch == "#") {
stream.eatWhile(/[\w\\\-]/);
return ret("atom", "hash");
}
else if (ch == "!") {
} else if (ch == "!") {
stream.match(/^\s*\w*/);
return ret("keyword", "important");
}
else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
} else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
}
else if (ch === "-") {
if (/\d/.test(stream.peek())) {
} else if (ch === "-") {
if (/[\d.]/.test(stream.peek())) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
} else if (stream.match(/^[^-]+-/)) {
return ret("meta", "meta");
}
}
else if (/[,+>*\/]/.test(ch)) {
} else if (/[,+>*\/]/.test(ch)) {
return ret(null, "select-op");
}
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
} else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
return ret("qualifier", "qualifier");
}
else if (ch == ":") {
return ret("operator", ch);
}
else if (/[;{}\[\]\(\)]/.test(ch)) {
} else if (/[:;{}\[\]\(\)]/.test(ch)) {
return ret(null, ch);
}
else if (ch == "u" && stream.match("rl(")) {
} else if (ch == "u" && stream.match("rl(")) {
stream.backUp(1);
state.tokenize = tokenParenthesized;
return ret("property", "variable");
}
else {
return ret("property", "word");
} else if (/[\w\\\-]/.test(ch)) {
stream.eatWhile(/[\w\\\-]/);
return ret("property", "variable");
return ret("property", "word");
} else {
return ret(null, null);
}
}
function tokenString(quote, nonInclusive) {
function tokenString(quote) {
return function(stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped)
if (ch == quote && !escaped) {
if (quote == ")") stream.backUp(1);
break;
}
escaped = !escaped && ch == "\\";
}
if (!escaped) {
if (nonInclusive) stream.backUp(1);
state.tokenize = tokenBase;
}
if (ch == quote || !escaped && quote != ")") state.tokenize = null;
return ret("string", "string");
};
}
@@ -91,218 +84,238 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
function tokenParenthesized(stream, state) {
stream.next(); // Must be '('
if (!stream.match(/\s*[\"\']/, false))
state.tokenize = tokenString(")", true);
state.tokenize = tokenString(")");
else
state.tokenize = tokenBase;
state.tokenize = null;
return ret(null, "(");
}
// Context management
function Context(type, indent, prev) {
this.type = type;
this.indent = indent;
this.prev = prev;
}
function pushContext(state, stream, type) {
state.context = new Context(type, stream.indentation() + indentUnit, state.context);
return type;
}
function popContext(state) {
state.context = state.context.prev;
return state.context.type;
}
function pass(type, stream, state) {
return states[state.context.type](type, stream, state);
}
function popAndPass(type, stream, state, n) {
for (var i = n || 1; i > 0; i--)
state.context = state.context.prev;
return pass(type, stream, state);
}
// Parser
function wordAsValue(stream) {
var word = stream.current().toLowerCase();
if (valueKeywords.hasOwnProperty(word))
override = "atom";
else if (colorKeywords.hasOwnProperty(word))
override = "keyword";
else
override = "variable";
}
var states = {};
states.top = function(type, stream, state) {
if (type == "{") {
return pushContext(state, stream, "block");
} else if (type == "}" && state.context.prev) {
return popContext(state);
} else if (type == "@media") {
return pushContext(state, stream, "media");
} else if (type == "@font-face") {
return "font_face_before";
} else if (type && type.charAt(0) == "@") {
return pushContext(state, stream, "at");
} else if (type == "hash") {
override = "builtin";
} else if (type == "word") {
override = "tag";
} else if (type == "variable-definition") {
return "maybeprop";
} else if (type == "interpolation") {
return pushContext(state, stream, "interpolation");
} else if (type == ":") {
return "pseudo";
} else if (allowNested && type == "(") {
return pushContext(state, stream, "params");
}
return state.context.type;
};
states.block = function(type, stream, state) {
if (type == "word") {
if (propertyKeywords.hasOwnProperty(stream.current().toLowerCase())) {
override = "property";
return "maybeprop";
} else if (allowNested) {
override = stream.match(/^\s*:/, false) ? "property" : "tag";
return "block";
} else {
override += " error";
return "maybeprop";
}
} else if (type == "meta") {
return "block";
} else if (!allowNested && (type == "hash" || type == "qualifier")) {
override = "error";
return "block";
} else {
return states.top(type, stream, state);
}
};
states.maybeprop = function(type, stream, state) {
if (type == ":") return pushContext(state, stream, "prop");
return pass(type, stream, state);
};
states.prop = function(type, stream, state) {
if (type == ";") return popContext(state);
if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
if (type == "}" || type == "{") return popAndPass(type, stream, state);
if (type == "(") return pushContext(state, stream, "parens");
if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
override += " error";
} else if (type == "word") {
wordAsValue(stream);
} else if (type == "interpolation") {
return pushContext(state, stream, "interpolation");
}
return "prop";
};
states.propBlock = function(type, _stream, state) {
if (type == "}") return popContext(state);
if (type == "word") { override = "property"; return "maybeprop"; }
return state.context.type;
};
states.parens = function(type, stream, state) {
if (type == "{" || type == "}") return popAndPass(type, stream, state);
if (type == ")") return popContext(state);
return "parens";
};
states.pseudo = function(type, stream, state) {
if (type == "word") {
override = "variable-3";
return state.context.type;
}
return pass(type, stream, state);
};
states.media = function(type, stream, state) {
if (type == "(") return pushContext(state, stream, "media_parens");
if (type == "}") return popAndPass(type, stream, state);
if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
if (type == "word") {
var word = stream.current().toLowerCase();
if (word == "only" || word == "not" || word == "and")
override = "keyword";
else if (mediaTypes.hasOwnProperty(word))
override = "attribute";
else if (mediaFeatures.hasOwnProperty(word))
override = "property";
else
override = "error";
}
return state.context.type;
};
states.media_parens = function(type, stream, state) {
if (type == ")") return popContext(state);
if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
return states.media(type, stream, state);
};
states.font_face_before = function(type, stream, state) {
if (type == "{")
return pushContext(state, stream, "font_face");
return pass(type, stream, state);
};
states.font_face = function(type, stream, state) {
if (type == "}") return popContext(state);
if (type == "word") {
if (!fontProperties.hasOwnProperty(stream.current().toLowerCase()))
override = "error";
else
override = "property";
return "maybeprop";
}
return "font_face";
};
states.at = function(type, stream, state) {
if (type == ";") return popContext(state);
if (type == "{" || type == "}") return popAndPass(type, stream, state);
if (type == "word") override = "tag";
else if (type == "hash") override = "builtin";
return "at";
};
states.interpolation = function(type, stream, state) {
if (type == "}") return popContext(state);
if (type == "{" || type == ";") return popAndPass(type, stream, state);
if (type != "variable") override = "error";
return "interpolation";
};
states.params = function(type, stream, state) {
if (type == ")") return popContext(state);
if (type == "{" || type == "}") return popAndPass(type, stream, state);
if (type == "word") wordAsValue(stream);
return "params";
};
return {
startState: function(base) {
return {tokenize: tokenBase,
baseIndent: base || 0,
stack: [],
lastToken: null};
return {tokenize: null,
state: "top",
context: new Context("top", base || 0, null)};
},
token: function(stream, state) {
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
//
// rule** or **ruleset:
// A selector + braces combo, or an at-rule.
//
// declaration block:
// A sequence of declarations.
//
// declaration:
// A property + colon + value combo.
//
// property value:
// The entire value of a property.
//
// component value:
// A single piece of a property value. Like the 5px in
// text-shadow: 0 0 5px blue;. Can also refer to things that are
// multiple terms, like the 1-4 terms that make up the background-size
// portion of the background shorthand.
//
// term:
// The basic unit of author-facing CSS, like a single number (5),
// dimension (5px), string ("foo"), or function. Officially defined
// by the CSS 2.1 grammar (look for the 'term' production)
//
//
// simple selector:
// A single atomic selector, like a type selector, an attr selector, a
// class selector, etc.
//
// compound selector:
// One or more simple selectors without a combinator. div.example is
// compound, div > .example is not.
//
// complex selector:
// One or more compound selectors chained with combinators.
//
// combinator:
// The parts of selectors that express relationships. There are four
// currently - the space (descendant combinator), the greater-than
// bracket (child combinator), the plus sign (next sibling combinator),
// and the tilda (following sibling combinator).
//
// sequence of selectors:
// One or more of the named type of selector chained with commas.
state.tokenize = state.tokenize || tokenBase;
if (state.tokenize == tokenBase && stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (style && typeof style != "string") style = ret(style[0], style[1]);
// Changing style returned based on context
var context = state.stack[state.stack.length-1];
if (style == "variable") {
if (type == "variable-definition") state.stack.push("propertyValue");
return state.lastToken = "variable-2";
} else if (style == "property") {
var word = stream.current().toLowerCase();
if (context == "propertyValue") {
if (valueKeywords.hasOwnProperty(word)) {
style = "string-2";
} else if (colorKeywords.hasOwnProperty(word)) {
style = "keyword";
} else {
style = "variable-2";
}
} else if (context == "rule") {
if (!propertyKeywords.hasOwnProperty(word)) {
style += " error";
}
} else if (context == "block") {
// if a value is present in both property, value, or color, the order
// of preference is property -> color -> value
if (propertyKeywords.hasOwnProperty(word)) {
style = "property";
} else if (colorKeywords.hasOwnProperty(word)) {
style = "keyword";
} else if (valueKeywords.hasOwnProperty(word)) {
style = "string-2";
} else {
style = "tag";
}
} else if (!context || context == "@media{") {
style = "tag";
} else if (context == "@media") {
if (atMediaTypes[stream.current()]) {
style = "attribute"; // Known attribute
} else if (/^(only|not)$/.test(word)) {
style = "keyword";
} else if (word == "and") {
style = "error"; // "and" is only allowed in @mediaType
} else if (atMediaFeatures.hasOwnProperty(word)) {
style = "error"; // Known property, should be in @mediaType(
} else {
// Unknown, expecting keyword or attribute, assuming attribute
style = "attribute error";
}
} else if (context == "@mediaType") {
if (atMediaTypes.hasOwnProperty(word)) {
style = "attribute";
} else if (word == "and") {
style = "operator";
} else if (/^(only|not)$/.test(word)) {
style = "error"; // Only allowed in @media
} else {
// Unknown attribute or property, but expecting property (preceded
// by "and"). Should be in parentheses
style = "error";
}
} else if (context == "@mediaType(") {
if (propertyKeywords.hasOwnProperty(word)) {
// do nothing, remains "property"
} else if (atMediaTypes.hasOwnProperty(word)) {
style = "error"; // Known property, should be in parentheses
} else if (word == "and") {
style = "operator";
} else if (/^(only|not)$/.test(word)) {
style = "error"; // Only allowed in @media
} else {
style += " error";
}
} else if (context == "@import") {
style = "tag";
} else {
style = "error";
}
} else if (style == "atom") {
if(!context || context == "@media{" || context == "block") {
style = "builtin";
} else if (context == "propertyValue") {
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
style += " error";
}
} else {
style = "error";
}
} else if (context == "@media" && type == "{") {
style = "error";
if (!state.tokenize && stream.eatSpace()) return null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style && typeof style == "object") {
type = style[1];
style = style[0];
}
// Push/pop context stack
if (type == "{") {
if (context == "@media" || context == "@mediaType") {
state.stack[state.stack.length-1] = "@media{";
}
else {
var newContext = allowNested ? "block" : "rule";
state.stack.push(newContext);
}
}
else if (type == "}") {
if (context == "interpolation") style = "operator";
// Pop off end of array until { is reached
while(state.stack.length){
var removed = state.stack.pop();
if(removed.indexOf("{") > -1){
break;
}
}
}
else if (type == "interpolation") state.stack.push("interpolation");
else if (type == "@media") state.stack.push("@media");
else if (type == "@import") state.stack.push("@import");
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
state.stack[state.stack.length-1] = "@mediaType";
else if (context == "@mediaType" && stream.current() == ",")
state.stack[state.stack.length-1] = "@media";
else if (type == "(") {
if (context == "@media" || context == "@mediaType") {
// Make sure @mediaType is used to avoid error on {
state.stack[state.stack.length-1] = "@mediaType";
state.stack.push("@mediaType(");
}
else state.stack.push("(");
}
else if (type == ")") {
// Pop off end of array until ( is reached
while(state.stack.length){
var removed = state.stack.pop();
if(removed.indexOf("(") > -1){
break;
}
}
}
else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
else if (context == "propertyValue" && type == ";") state.stack.pop();
else if (context == "@import" && type == ";") state.stack.pop();
return state.lastToken = style;
override = style;
state.state = states[state.state](type, stream, state);
return override;
},
indent: function(state, textAfter) {
var n = state.stack.length;
if (/^\}/.test(textAfter))
n -= state.stack[n-1] == "propertyValue" ? 2 : 1;
return state.baseIndent + n * indentUnit;
var cx = state.context, ch = textAfter && textAfter.charAt(0);
var indent = cx.indent;
if (cx.prev &&
(ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") ||
ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") ||
ch == "{" && (cx.type == "at" || cx.type == "media"))) {
indent = cx.indent - indentUnit;
cx = cx.prev;
}
return indent;
},
electricChars: "}",
@@ -321,12 +334,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return keys;
}
var atMediaTypes = keySet([
var mediaTypes_ = [
"all", "aural", "braille", "handheld", "print", "projection", "screen",
"tty", "tv", "embossed"
]);
], mediaTypes = keySet(mediaTypes_);
var atMediaFeatures = keySet([
var mediaFeatures_ = [
"width", "min-width", "max-width", "height", "min-height", "max-height",
"device-width", "min-device-width", "max-device-width", "device-height",
"min-device-height", "max-device-height", "aspect-ratio",
@@ -335,9 +348,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"max-color", "color-index", "min-color-index", "max-color-index",
"monochrome", "min-monochrome", "max-monochrome", "resolution",
"min-resolution", "max-resolution", "scan", "grid"
]);
], mediaFeatures = keySet(mediaFeatures_);
var propertyKeywords = keySet([
var propertyKeywords_ = [
"align-content", "align-items", "align-self", "alignment-adjust",
"alignment-baseline", "anchor-point", "animation", "animation-delay",
"animation-direction", "animation-duration", "animation-iteration-count",
@@ -425,9 +438,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
"baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
"glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
]);
], propertyKeywords = keySet(propertyKeywords_);
var colorKeywords = keySet([
var colorKeywords_ = [
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
"bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
"burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
@@ -454,9 +467,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
"whitesmoke", "yellow", "yellowgreen"
]);
], colorKeywords = keySet(colorKeywords_);
var valueKeywords = keySet([
var valueKeywords_ = [
"above", "absolute", "activeborder", "activecaption", "afar",
"after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
"always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
@@ -539,7 +552,15 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
"window", "windowframe", "windowtext", "x-large", "x-small", "xor",
"xx-large", "xx-small"
]);
], valueKeywords = keySet(valueKeywords_);
var fontProperties_ = [
"font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
"font-stretch", "font-weight", "font-style"
], fontProperties = keySet(fontProperties_);
var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);
CodeMirror.registerHelper("hintWords", "css", allWords);
function tokenCComment(stream, state) {
var maybeEnd = false, ch;
@@ -553,67 +574,47 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return ["comment", "comment"];
}
function tokenSGMLComment(stream, state) {
if (stream.skipTo("-->")) {
stream.match("-->");
state.tokenize = null;
} else {
stream.skipToEnd();
}
return ["comment", "comment"];
}
CodeMirror.defineMIME("text/css", {
atMediaTypes: atMediaTypes,
atMediaFeatures: atMediaFeatures,
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
hooks: {
fontProperties: fontProperties,
tokenHooks: {
"<": function(stream, state) {
function tokenSGMLComment(stream, state) {
var dashes = 0, ch;
while ((ch = stream.next()) != null) {
if (dashes >= 2 && ch == ">") {
state.tokenize = null;
break;
}
dashes = (ch == "-") ? dashes + 1 : 0;
}
return ["comment", "comment"];
}
if (stream.eat("!")) {
state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state);
}
if (!stream.match("!--")) return false;
state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state);
},
"/": function(stream, state) {
if (stream.eat("*")) {
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
}
return false;
if (!stream.eat("*")) return false;
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
}
},
name: "css"
});
CodeMirror.defineMIME("text/x-scss", {
atMediaTypes: atMediaTypes,
atMediaFeatures: atMediaFeatures,
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
allowNested: true,
hooks: {
":": function(stream) {
if (stream.match(/\s*{/)) {
return [null, "{"];
}
return false;
},
"$": function(stream) {
stream.match(/^[\w-]+/);
if (stream.peek() == ":") {
return ["variable", "variable-definition"];
}
return ["variable", "variable"];
},
",": function(_stream, state) {
if (state.stack[state.stack.length - 1] == "propertyValue") {
return ["operator", ";"];
}
},
tokenHooks: {
"/": function(stream, state) {
if (stream.eat("/")) {
stream.skipToEnd();
@@ -625,15 +626,58 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return ["operator", "operator"];
}
},
":": function(stream) {
if (stream.match(/\s*{/))
return [null, "{"];
return false;
},
"$": function(stream) {
stream.match(/^[\w-]+/);
if (stream.match(/^\s*:/, false))
return ["variable-2", "variable-definition"];
return ["variable-2", "variable"];
},
"#": function(stream) {
if (stream.eat("{")) {
return ["operator", "interpolation"];
} else {
stream.eatWhile(/[\w\\\-]/);
return ["atom", "hash"];
}
if (!stream.eat("{")) return false;
return [null, "interpolation"];
}
},
name: "css"
name: "css",
helperType: "scss"
});
CodeMirror.defineMIME("text/x-less", {
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
allowNested: true,
tokenHooks: {
"/": function(stream, state) {
if (stream.eat("/")) {
stream.skipToEnd();
return ["comment", "comment"];
} else if (stream.eat("*")) {
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
} else {
return ["operator", "operator"];
}
},
"@": function(stream) {
if (stream.match(/^(charset|document|font-face|import|keyframes|media|namespace|page|supports)\b/, false)) return false;
stream.eatWhile(/[\w\\\-]/);
if (stream.match(/^\s*:/, false))
return ["variable-2", "variable-definition"];
return ["variable-2", "variable"];
},
"&": function() {
return ["atom", "atom"];
}
},
name: "css",
helperType: "less"
});
})();
+1 -1
View File
@@ -150,7 +150,7 @@ code {
});
</script>
<p><strong>MIME types defined:</strong> <code>text/scss</code>.</p>
<p>The SCSS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>.</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#scss_*">normal</a>, <a href="../../test/index.html#verbose,scss_*">verbose</a>.</p>
+47 -24
View File
@@ -1,34 +1,33 @@
(function() {
var mode = CodeMirror.getMode({tabSize: 1}, "text/x-scss");
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
function IT(name) { test.indentation(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
MT('url_with_quotation',
"[tag foo] { [property background][operator :][string-2 url]([string test.jpg]) }");
"[tag foo] { [property background]:[atom url]([string test.jpg]) }");
MT('url_with_double_quotes',
"[tag foo] { [property background][operator :][string-2 url]([string \"test.jpg\"]) }");
"[tag foo] { [property background]:[atom url]([string \"test.jpg\"]) }");
MT('url_with_single_quotes',
"[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) }");
"[tag foo] { [property background]:[atom url]([string \'test.jpg\']) }");
MT('string',
"[def @import] [string \"compass/css3\"]");
MT('important_keyword',
"[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) [keyword !important] }");
"[tag foo] { [property background]:[atom url]([string \'test.jpg\']) [keyword !important] }");
MT('variable',
"[variable-2 $blue][operator :][atom #333]");
"[variable-2 $blue]:[atom #333]");
MT('variable_as_attribute',
"[tag foo] { [property color][operator :][variable-2 $blue] }");
"[tag foo] { [property color]:[variable-2 $blue] }");
MT('numbers',
"[tag foo] { [property padding][operator :][number 10px] [number 10] [number 10em] [number 8in] }");
"[tag foo] { [property padding]:[number 10px] [number 10] [number 10em] [number 8in] }");
MT('number_percentage',
"[tag foo] { [property width][operator :][number 80%] }");
"[tag foo] { [property width]:[number 80%] }");
MT('selector',
"[builtin #hello][qualifier .world]{}");
@@ -40,45 +39,69 @@
"[comment /*foobar*/]");
MT('attribute_with_hyphen',
"[tag foo] { [property font-size][operator :][number 10px] }");
"[tag foo] { [property font-size]:[number 10px] }");
MT('string_after_attribute',
"[tag foo] { [property content][operator :][string \"::\"] }");
"[tag foo] { [property content]:[string \"::\"] }");
MT('directives',
"[def @include] [qualifier .mixin]");
MT('basic_structure',
"[tag p] { [property background][operator :][keyword red]; }");
"[tag p] { [property background]:[keyword red]; }");
MT('nested_structure',
"[tag p] { [tag a] { [property color][operator :][keyword red]; } }");
"[tag p] { [tag a] { [property color]:[keyword red]; } }");
MT('mixin',
"[def @mixin] [tag table-base] {}");
MT('number_without_semicolon',
"[tag p] {[property width][operator :][number 12]}",
"[tag a] {[property color][operator :][keyword red];}");
"[tag p] {[property width]:[number 12]}",
"[tag a] {[property color]:[keyword red];}");
MT('atom_in_nested_block',
"[tag p] { [tag a] { [property color][operator :][atom #000]; } }");
"[tag p] { [tag a] { [property color]:[atom #000]; } }");
MT('interpolation_in_property',
"[tag foo] { [operator #{][variable-2 $hello][operator }:][number 2]; }");
"[tag foo] { #{[variable-2 $hello]}:[number 2]; }");
MT('interpolation_in_selector',
"[tag foo][operator #{][variable-2 $hello][operator }] { [property color][operator :][atom #000]; }");
"[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }");
MT('interpolation_error',
"[tag foo][operator #{][error foo][operator }] { [property color][operator :][atom #000]; }");
"[tag foo]#{[error foo]} { [property color]:[atom #000]; }");
MT("divide_operator",
"[tag foo] { [property width][operator :][number 4] [operator /] [number 2] }");
"[tag foo] { [property width]:[number 4] [operator /] [number 2] }");
MT('nested_structure_with_id_selector',
"[tag p] { [builtin #hello] { [property color][operator :][keyword red]; } }");
"[tag p] { [builtin #hello] { [property color]:[keyword red]; } }");
IT('mixin',
"@mixin container[1 (][2 $a: 10][1 , ][2 $b: 10][1 , ][2 $c: 10]) [1 {]}");
MT('indent_mixin',
"[def @mixin] [tag container] (",
" [variable-2 $a]: [number 10],",
" [variable-2 $b]: [number 10])",
"{}");
MT('indent_nested',
"[tag foo] {",
" [tag bar] {",
" }",
"}");
MT('indent_parentheses',
"[tag foo] {",
" [property color]: [variable darken]([variable-2 $blue],",
" [number 9%]);",
"}");
MT('indent_vardef',
"[variable-2 $name]:",
" [string 'val'];",
"[tag tag] {",
" [tag inner] {",
" [property margin]: [number 3px];",
" }",
"}");
})();
+63 -74
View File
@@ -1,68 +1,19 @@
(function() {
var mode = CodeMirror.getMode({tabSize: 1}, "css");
var mode = CodeMirror.getMode({indentUnit: 2}, "css");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
function IT(name) { test.indentation(name, mode, Array.prototype.slice.call(arguments, 1)); }
// Requires at least one media query
MT("atMediaEmpty",
"[def @media] [error {] }");
MT("atMediaMultiple",
"[def @media] [keyword not] [attribute screen] [operator and] ([property color]), [keyword not] [attribute print] [operator and] ([property color]) { }");
MT("atMediaCheckStack",
"[def @media] [attribute screen] { } [tag foo] { }");
MT("atMediaCheckStack",
"[def @media] [attribute screen] ([property color]) { } [tag foo] { }");
MT("atMediaPropertyOnly",
"[def @media] ([property color]) { } [tag foo] { }");
MT("atMediaCheckStackInvalidAttribute",
"[def @media] [attribute&error foobarhello] { [tag foo] { } }");
MT("atMediaCheckStackInvalidAttribute",
"[def @media] [attribute&error foobarhello] { } [tag foo] { }");
// Error, because "and" is only allowed immediately preceding a media expression
MT("atMediaInvalidAttribute",
"[def @media] [attribute&error foobarhello] { }");
// Error, because "and" is only allowed immediately preceding a media expression
MT("atMediaInvalidAnd",
"[def @media] [error and] [attribute screen] { }");
// Error, because "not" is only allowed as the first item in each media query
MT("atMediaInvalidNot",
"[def @media] [attribute screen] [error not] ([error not]) { }");
// Error, because "only" is only allowed as the first item in each media query
MT("atMediaInvalidOnly",
"[def @media] [attribute screen] [error only] ([error only]) { }");
// Error, because "foobarhello" is neither a known type or property, but
// property was expected (after "and"), and it should be in parenthese.
MT("atMediaUnknownType",
"[def @media] [attribute screen] [operator and] [error foobarhello] { }");
// Error, because "color" is not a known type, but is a known property, and
// should be in parentheses.
MT("atMediaInvalidType",
"[def @media] [attribute screen] [operator and] [error color] { }");
// Error, because "print" is not a known property, but is a known type,
// and should not be in parenthese.
MT("atMediaInvalidProperty",
"[def @media] [attribute screen] [operator and] ([error print]) { }");
"[def @media] [attribute screen] [keyword and] [error foobarhello] { }");
// Soft error, because "foobarhello" is not a known property or type.
MT("atMediaUnknownProperty",
"[def @media] [attribute screen] [operator and] ([property&error foobarhello]) { }");
"[def @media] [attribute screen] [keyword and] ([error foobarhello]) { }");
// Make sure nesting works with media queries
MT("atMediaMaxWidthNested",
"[def @media] [attribute screen] [operator and] ([property max-width][operator :] [number 25px]) { [tag foo] { } }");
"[def @media] [attribute screen] [keyword and] ([property max-width]: [number 25px]) { [tag foo] { } }");
MT("tagSelector",
"[tag foo] { }");
@@ -74,57 +25,95 @@
"[builtin #foo] { [error #foo] }");
MT("tagSelectorUnclosed",
"[tag foo] { [property margin][operator :] [number 0] } [tag bar] { }");
"[tag foo] { [property margin]: [number 0] } [tag bar] { }");
MT("tagStringNoQuotes",
"[tag foo] { [property font-family][operator :] [variable-2 hello] [variable-2 world]; }");
"[tag foo] { [property font-family]: [variable hello] [variable world]; }");
MT("tagStringDouble",
"[tag foo] { [property font-family][operator :] [string \"hello world\"]; }");
"[tag foo] { [property font-family]: [string \"hello world\"]; }");
MT("tagStringSingle",
"[tag foo] { [property font-family][operator :] [string 'hello world']; }");
"[tag foo] { [property font-family]: [string 'hello world']; }");
MT("tagColorKeyword",
"[tag foo] {" +
"[property color][operator :] [keyword black];" +
"[property color][operator :] [keyword navy];" +
"[property color][operator :] [keyword yellow];" +
"}");
"[tag foo] {",
" [property color]: [keyword black];",
" [property color]: [keyword navy];",
" [property color]: [keyword yellow];",
"}");
MT("tagColorHex3",
"[tag foo] { [property background][operator :] [atom #fff]; }");
"[tag foo] { [property background]: [atom #fff]; }");
MT("tagColorHex6",
"[tag foo] { [property background][operator :] [atom #ffffff]; }");
"[tag foo] { [property background]: [atom #ffffff]; }");
MT("tagColorHex4",
"[tag foo] { [property background][operator :] [atom&error #ffff]; }");
"[tag foo] { [property background]: [atom&error #ffff]; }");
MT("tagColorHexInvalid",
"[tag foo] { [property background][operator :] [atom&error #ffg]; }");
"[tag foo] { [property background]: [atom&error #ffg]; }");
MT("tagNegativeNumber",
"[tag foo] { [property margin][operator :] [number -5px]; }");
"[tag foo] { [property margin]: [number -5px]; }");
MT("tagPositiveNumber",
"[tag foo] { [property padding][operator :] [number 5px]; }");
"[tag foo] { [property padding]: [number 5px]; }");
MT("tagVendor",
"[tag foo] { [meta -foo-][property box-sizing][operator :] [meta -foo-][string-2 border-box]; }");
"[tag foo] { [meta -foo-][property box-sizing]: [meta -foo-][atom border-box]; }");
MT("tagBogusProperty",
"[tag foo] { [property&error barhelloworld][operator :] [number 0]; }");
"[tag foo] { [property&error barhelloworld]: [number 0]; }");
MT("tagTwoProperties",
"[tag foo] { [property margin][operator :] [number 0]; [property padding][operator :] [number 0]; }");
"[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }");
MT("tagTwoPropertiesURL",
"[tag foo] { [property background][operator :] [string-2 url]([string //example.com/foo.png]); [property padding][operator :] [number 0]; }");
"[tag foo] { [property background]: [atom url]([string //example.com/foo.png]); [property padding]: [number 0]; }");
MT("commentSGML",
"[comment <!--comment-->]");
IT("tagSelector",
"strong, em [1 { background][2 : rgba][3 (255, 255, 0, .2][2 )][1 ;]}");
MT("commentSGML2",
"[comment <!--comment]",
"[comment -->] [tag div] {}");
MT("indent_tagSelector",
"[tag strong], [tag em] {",
" [property background]: [atom rgba](",
" [number 255], [number 255], [number 0], [number .2]",
" );",
"}");
MT("indent_atMedia",
"[def @media] {",
" [tag foo] {",
" [property color]:",
" [keyword yellow];",
" }",
"}");
MT("indent_comma",
"[tag foo] {",
" [property font-family]: [variable verdana],",
" [atom sans-serif];",
"}");
MT("indent_parentheses",
"[tag foo]:[variable-3 before] {",
" [property background]: [atom url](",
"[string blahblah]",
"[string etc]",
"[string ]) [keyword !important];",
"}");
MT("font_face",
"[def @font-face] {",
" [property font-family]: [string 'myfont'];",
" [error nonsense]: [string 'abc'];",
" [property src]: [atom url]([string http://blah]),",
" [atom url]([string http://foo]);",
"}");
})();