|
|
CodeMirror.defineMode("smarty", function(config, parserConfig) {
|
|
|
var keyFuncs = ["debug", "extends", "function", "include", "literal"];
|
|
|
var last;
|
|
|
var regs = {
|
|
|
operatorChars: /[+\-*&%=<>!?]/,
|
|
|
validIdentifier: /[a-zA-Z0-9\_]/,
|
|
|
stringChar: /[\'\"]/
|
|
|
}
|
|
|
var leftDelim = (typeof config.mode.leftDelimiter != 'undefined') ? config.mode.leftDelimiter : "{";
|
|
|
var rightDelim = (typeof config.mode.rightDelimiter != 'undefined') ? config.mode.rightDelimiter : "}";
|
|
|
function ret(style, lst) { last = lst; return style; }
|
|
|
|
|
|
|
|
|
function tokenizer(stream, state) {
|
|
|
function chain(parser) {
|
|
|
state.tokenize = parser;
|
|
|
return parser(stream, state);
|
|
|
}
|
|
|
|
|
|
if (stream.match(leftDelim, true)) {
|
|
|
if (stream.eat("*")) {
|
|
|
return chain(inBlock("comment", "*" + rightDelim));
|
|
|
}
|
|
|
else {
|
|
|
state.tokenize = inSmarty;
|
|
|
return "tag";
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
// I'd like to do an eatWhile() here, but I can't get it to eat only up to the rightDelim string/char
|
|
|
stream.next();
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function inSmarty(stream, state) {
|
|
|
if (stream.match(rightDelim, true)) {
|
|
|
state.tokenize = tokenizer;
|
|
|
return ret("tag", null);
|
|
|
}
|
|
|
|
|
|
var ch = stream.next();
|
|
|
if (ch == "$") {
|
|
|
stream.eatWhile(regs.validIdentifier);
|
|
|
return ret("variable-2", "variable");
|
|
|
}
|
|
|
else if (ch == ".") {
|
|
|
return ret("operator", "property");
|
|
|
}
|
|
|
else if (regs.stringChar.test(ch)) {
|
|
|
state.tokenize = inAttribute(ch);
|
|
|
return ret("string", "string");
|
|
|
}
|
|
|
else if (regs.operatorChars.test(ch)) {
|
|
|
stream.eatWhile(regs.operatorChars);
|
|
|
return ret("operator", "operator");
|
|
|
}
|
|
|
else if (ch == "[" || ch == "]") {
|
|
|
return ret("bracket", "bracket");
|
|
|
}
|
|
|
else if (/\d/.test(ch)) {
|
|
|
stream.eatWhile(/\d/);
|
|
|
return ret("number", "number");
|
|
|
}
|
|
|
else {
|
|
|
if (state.last == "variable") {
|
|
|
if (ch == "@") {
|
|
|
stream.eatWhile(regs.validIdentifier);
|
|
|
return ret("property", "property");
|
|
|
}
|
|
|
else if (ch == "|") {
|
|
|
stream.eatWhile(regs.validIdentifier);
|
|
|
return ret("qualifier", "modifier");
|
|
|
}
|
|
|
}
|
|
|
else if (state.last == "whitespace") {
|
|
|
stream.eatWhile(regs.validIdentifier);
|
|
|
return ret("attribute", "modifier");
|
|
|
}
|
|
|
else if (state.last == "property") {
|
|
|
stream.eatWhile(regs.validIdentifier);
|
|
|
return ret("property", null);
|
|
|
}
|
|
|
else if (/\s/.test(ch)) {
|
|
|
last = "whitespace";
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
var str = "";
|
|
|
if (ch != "/") {
|
|
|
str += ch;
|
|
|
}
|
|
|
var c = "";
|
|
|
while ((c = stream.eat(regs.validIdentifier))) {
|
|
|
str += c;
|
|
|
}
|
|
|
var i, j;
|
|
|
for (i=0, j=keyFuncs.length; i<j; i++) {
|
|
|
if (keyFuncs[i] == str) {
|
|
|
return ret("keyword", "keyword");
|
|
|
}
|
|
|
}
|
|
|
if (/\s/.test(ch)) {
|
|
|
return null;
|
|
|
}
|
|
|
return ret("tag", "tag");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function inAttribute(quote) {
|
|
|
return function(stream, state) {
|
|
|
while (!stream.eol()) {
|
|
|
if (stream.next() == quote) {
|
|
|
state.tokenize = inSmarty;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
return "string";
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function inBlock(style, terminator) {
|
|
|
return function(stream, state) {
|
|
|
while (!stream.eol()) {
|
|
|
if (stream.match(terminator)) {
|
|
|
state.tokenize = tokenizer;
|
|
|
break;
|
|
|
}
|
|
|
stream.next();
|
|
|
}
|
|
|
return style;
|
|
|
};
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
startState: function() {
|
|
|
return { tokenize: tokenizer, mode: "smarty", last: null };
|
|
|
},
|
|
|
token: function(stream, state) {
|
|
|
var style = state.tokenize(stream, state);
|
|
|
state.last = last;
|
|
|
return style;
|
|
|
},
|
|
|
electricChars: ""
|
|
|
}
|
|
|
});
|
|
|
|
|
|
CodeMirror.defineMIME("text/x-smarty", "smarty");
|