smarty.js
205 lines
| 5.8 KiB
| application/javascript
|
JavascriptLexer
r4026 | /** | |||
* Smarty 2 and 3 mode. | ||||
*/ | ||||
CodeMirror.defineMode("smarty", function(config) { | ||||
"use strict"; | ||||
// our default settings; check to see if they're overridden | ||||
var settings = { | ||||
rightDelimiter: '}', | ||||
leftDelimiter: '{', | ||||
smartyVersion: 2 // for backward compatibility | ||||
}; | ||||
if (config.hasOwnProperty("leftDelimiter")) { | ||||
settings.leftDelimiter = config.leftDelimiter; | ||||
} | ||||
if (config.hasOwnProperty("rightDelimiter")) { | ||||
settings.rightDelimiter = config.rightDelimiter; | ||||
} | ||||
if (config.hasOwnProperty("smartyVersion") && config.smartyVersion === 3) { | ||||
settings.smartyVersion = 3; | ||||
} | ||||
var keyFunctions = ["debug", "extends", "function", "include", "literal"]; | ||||
var last; | ||||
var regs = { | ||||
operatorChars: /[+\-*&%=<>!?]/, | ||||
validIdentifier: /[a-zA-Z0-9_]/, | ||||
stringChar: /['"]/ | ||||
}; | ||||
var helpers = { | ||||
cont: function(style, lastType) { | ||||
last = lastType; | ||||
return style; | ||||
}, | ||||
chain: function(stream, state, parser) { | ||||
state.tokenize = parser; | ||||
return parser(stream, state); | ||||
} | ||||
}; | ||||
// our various parsers | ||||
var parsers = { | ||||
// the main tokenizer | ||||
tokenizer: function(stream, state) { | ||||
if (stream.match(settings.leftDelimiter, true)) { | ||||
if (stream.eat("*")) { | ||||
return helpers.chain(stream, state, parsers.inBlock("comment", "*" + settings.rightDelimiter)); | ||||
} else { | ||||
// Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode | ||||
state.depth++; | ||||
var isEol = stream.eol(); | ||||
var isFollowedByWhitespace = /\s/.test(stream.peek()); | ||||
if (settings.smartyVersion === 3 && settings.leftDelimiter === "{" && (isEol || isFollowedByWhitespace)) { | ||||
state.depth--; | ||||
return null; | ||||
} else { | ||||
state.tokenize = parsers.smarty; | ||||
last = "startTag"; | ||||
return "tag"; | ||||
} | ||||
} | ||||
} else { | ||||
stream.next(); | ||||
return null; | ||||
} | ||||
}, | ||||
// parsing Smarty content | ||||
smarty: function(stream, state) { | ||||
if (stream.match(settings.rightDelimiter, true)) { | ||||
if (settings.smartyVersion === 3) { | ||||
state.depth--; | ||||
if (state.depth <= 0) { | ||||
state.tokenize = parsers.tokenizer; | ||||
} | ||||
} else { | ||||
state.tokenize = parsers.tokenizer; | ||||
} | ||||
return helpers.cont("tag", null); | ||||
} | ||||
if (stream.match(settings.leftDelimiter, true)) { | ||||
state.depth++; | ||||
return helpers.cont("tag", "startTag"); | ||||
} | ||||
var ch = stream.next(); | ||||
if (ch == "$") { | ||||
stream.eatWhile(regs.validIdentifier); | ||||
return helpers.cont("variable-2", "variable"); | ||||
} else if (ch == "|") { | ||||
return helpers.cont("operator", "pipe"); | ||||
} else if (ch == ".") { | ||||
return helpers.cont("operator", "property"); | ||||
} else if (regs.stringChar.test(ch)) { | ||||
state.tokenize = parsers.inAttribute(ch); | ||||
return helpers.cont("string", "string"); | ||||
} else if (regs.operatorChars.test(ch)) { | ||||
stream.eatWhile(regs.operatorChars); | ||||
return helpers.cont("operator", "operator"); | ||||
} else if (ch == "[" || ch == "]") { | ||||
return helpers.cont("bracket", "bracket"); | ||||
} else if (ch == "(" || ch == ")") { | ||||
return helpers.cont("bracket", "operator"); | ||||
} else if (/\d/.test(ch)) { | ||||
stream.eatWhile(/\d/); | ||||
return helpers.cont("number", "number"); | ||||
} else { | ||||
if (state.last == "variable") { | ||||
if (ch == "@") { | ||||
stream.eatWhile(regs.validIdentifier); | ||||
return helpers.cont("property", "property"); | ||||
} else if (ch == "|") { | ||||
stream.eatWhile(regs.validIdentifier); | ||||
return helpers.cont("qualifier", "modifier"); | ||||
} | ||||
} else if (state.last == "pipe") { | ||||
stream.eatWhile(regs.validIdentifier); | ||||
return helpers.cont("qualifier", "modifier"); | ||||
} else if (state.last == "whitespace") { | ||||
stream.eatWhile(regs.validIdentifier); | ||||
return helpers.cont("attribute", "modifier"); | ||||
} if (state.last == "property") { | ||||
stream.eatWhile(regs.validIdentifier); | ||||
return helpers.cont("property", null); | ||||
} else if (/\s/.test(ch)) { | ||||
last = "whitespace"; | ||||
return null; | ||||
} | ||||
var str = ""; | ||||
if (ch != "/") { | ||||
str += ch; | ||||
} | ||||
var c = null; | ||||
while (c = stream.eat(regs.validIdentifier)) { | ||||
str += c; | ||||
} | ||||
for (var i=0, j=keyFunctions.length; i<j; i++) { | ||||
if (keyFunctions[i] == str) { | ||||
return helpers.cont("keyword", "keyword"); | ||||
} | ||||
} | ||||
if (/\s/.test(ch)) { | ||||
return null; | ||||
} | ||||
return helpers.cont("tag", "tag"); | ||||
} | ||||
}, | ||||
inAttribute: function(quote) { | ||||
return function(stream, state) { | ||||
var prevChar = null; | ||||
var currChar = null; | ||||
while (!stream.eol()) { | ||||
currChar = stream.peek(); | ||||
if (stream.next() == quote && prevChar !== '\\') { | ||||
state.tokenize = parsers.smarty; | ||||
break; | ||||
} | ||||
prevChar = currChar; | ||||
} | ||||
return "string"; | ||||
}; | ||||
}, | ||||
inBlock: function(style, terminator) { | ||||
return function(stream, state) { | ||||
while (!stream.eol()) { | ||||
if (stream.match(terminator)) { | ||||
state.tokenize = parsers.tokenizer; | ||||
break; | ||||
} | ||||
stream.next(); | ||||
} | ||||
return style; | ||||
}; | ||||
} | ||||
}; | ||||
// the public API for CodeMirror | ||||
return { | ||||
startState: function() { | ||||
return { | ||||
tokenize: parsers.tokenizer, | ||||
mode: "smarty", | ||||
last: null, | ||||
depth: 0 | ||||
}; | ||||
}, | ||||
token: function(stream, state) { | ||||
var style = state.tokenize(stream, state); | ||||
state.last = last; | ||||
return style; | ||||
}, | ||||
electricChars: "" | ||||
}; | ||||
}); | ||||
CodeMirror.defineMIME("text/x-smarty", "smarty"); | ||||