smartymixed.js
170 lines
| 5.2 KiB
| application/javascript
|
JavascriptLexer
Bradley M. Kuhn
|
r4120 | /** | ||
* @file smartymixed.js | ||||
* @brief Smarty Mixed Codemirror mode (Smarty + Mixed HTML) | ||||
* @author Ruslan Osmanov <rrosmanov at gmail dot com> | ||||
* @version 3.0 | ||||
* @date 05.07.2013 | ||||
*/ | ||||
CodeMirror.defineMode("smartymixed", function(config) { | ||||
var settings, regs, helpers, parsers, | ||||
htmlMixedMode = CodeMirror.getMode(config, "htmlmixed"), | ||||
smartyMode = CodeMirror.getMode(config, "smarty"), | ||||
settings = { | ||||
rightDelimiter: '}', | ||||
leftDelimiter: '{' | ||||
}; | ||||
if (config.hasOwnProperty("leftDelimiter")) { | ||||
settings.leftDelimiter = config.leftDelimiter; | ||||
} | ||||
if (config.hasOwnProperty("rightDelimiter")) { | ||||
settings.rightDelimiter = config.rightDelimiter; | ||||
} | ||||
regs = { | ||||
smartyComment: new RegExp("^" + settings.leftDelimiter + "\\*"), | ||||
literalOpen: new RegExp(settings.leftDelimiter + "literal" + settings.rightDelimiter), | ||||
literalClose: new RegExp(settings.leftDelimiter + "\/literal" + settings.rightDelimiter), | ||||
hasLeftDelimeter: new RegExp(".*" + settings.leftDelimiter), | ||||
htmlHasLeftDelimeter: new RegExp("[^<>]*" + settings.leftDelimiter) | ||||
}; | ||||
helpers = { | ||||
chain: function(stream, state, parser) { | ||||
state.tokenize = parser; | ||||
return parser(stream, state); | ||||
}, | ||||
cleanChain: function(stream, state, parser) { | ||||
state.tokenize = null; | ||||
state.localState = null; | ||||
state.localMode = null; | ||||
return (typeof parser == "string") ? (parser ? parser : null) : parser(stream, state); | ||||
}, | ||||
maybeBackup: function(stream, pat, style) { | ||||
var cur = stream.current(); | ||||
var close = cur.search(pat), | ||||
m; | ||||
if (close > - 1) stream.backUp(cur.length - close); | ||||
else if (m = cur.match(/<\/?$/)) { | ||||
stream.backUp(cur.length); | ||||
if (!stream.match(pat, false)) stream.match(cur[0]); | ||||
} | ||||
return style; | ||||
} | ||||
}; | ||||
parsers = { | ||||
html: function(stream, state) { | ||||
if (!state.inLiteral && stream.match(regs.htmlHasLeftDelimeter, false)) { | ||||
state.tokenize = parsers.smarty; | ||||
state.localMode = smartyMode; | ||||
state.localState = smartyMode.startState(htmlMixedMode.indent(state.htmlMixedState, "")); | ||||
return helpers.maybeBackup(stream, settings.leftDelimiter, smartyMode.token(stream, state.localState)); | ||||
} | ||||
return htmlMixedMode.token(stream, state.htmlMixedState); | ||||
}, | ||||
smarty: function(stream, state) { | ||||
if (stream.match(settings.leftDelimiter, false)) { | ||||
if (stream.match(regs.smartyComment, false)) { | ||||
return helpers.chain(stream, state, parsers.inBlock("comment", "*" + settings.rightDelimiter)); | ||||
} | ||||
} else if (stream.match(settings.rightDelimiter, false)) { | ||||
stream.eat(settings.rightDelimiter); | ||||
state.tokenize = parsers.html; | ||||
state.localMode = htmlMixedMode; | ||||
state.localState = state.htmlMixedState; | ||||
return "tag"; | ||||
} | ||||
return helpers.maybeBackup(stream, settings.rightDelimiter, smartyMode.token(stream, state.localState)); | ||||
}, | ||||
inBlock: function(style, terminator) { | ||||
return function(stream, state) { | ||||
while (!stream.eol()) { | ||||
if (stream.match(terminator)) { | ||||
helpers.cleanChain(stream, state, ""); | ||||
break; | ||||
} | ||||
stream.next(); | ||||
} | ||||
return style; | ||||
}; | ||||
} | ||||
}; | ||||
return { | ||||
startState: function() { | ||||
var state = htmlMixedMode.startState(); | ||||
return { | ||||
token: parsers.html, | ||||
localMode: null, | ||||
localState: null, | ||||
htmlMixedState: state, | ||||
tokenize: null, | ||||
inLiteral: false | ||||
}; | ||||
}, | ||||
copyState: function(state) { | ||||
var local = null, tok = (state.tokenize || state.token); | ||||
if (state.localState) { | ||||
local = CodeMirror.copyState((tok != parsers.html ? smartyMode : htmlMixedMode), state.localState); | ||||
} | ||||
return { | ||||
token: state.token, | ||||
tokenize: state.tokenize, | ||||
localMode: state.localMode, | ||||
localState: local, | ||||
htmlMixedState: CodeMirror.copyState(htmlMixedMode, state.htmlMixedState), | ||||
inLiteral: state.inLiteral | ||||
}; | ||||
}, | ||||
token: function(stream, state) { | ||||
if (stream.match(settings.leftDelimiter, false)) { | ||||
if (!state.inLiteral && stream.match(regs.literalOpen, true)) { | ||||
state.inLiteral = true; | ||||
return "keyword"; | ||||
} else if (state.inLiteral && stream.match(regs.literalClose, true)) { | ||||
state.inLiteral = false; | ||||
return "keyword"; | ||||
} | ||||
} | ||||
if (state.inLiteral && state.localState != state.htmlMixedState) { | ||||
state.tokenize = parsers.html; | ||||
state.localMode = htmlMixedMode; | ||||
state.localState = state.htmlMixedState; | ||||
} | ||||
var style = (state.tokenize || state.token)(stream, state); | ||||
return style; | ||||
}, | ||||
indent: function(state, textAfter) { | ||||
if (state.localMode == smartyMode | ||||
|| (state.inLiteral && !state.localMode) | ||||
|| regs.hasLeftDelimeter.test(textAfter)) { | ||||
return CodeMirror.Pass; | ||||
} | ||||
return htmlMixedMode.indent(state.htmlMixedState, textAfter); | ||||
}, | ||||
electricChars: "/{}:", | ||||
innerMode: function(state) { | ||||
return { | ||||
state: state.localState || state.htmlMixedState, | ||||
mode: state.localMode || htmlMixedMode | ||||
}; | ||||
} | ||||
}; | ||||
}, | ||||
"htmlmixed"); | ||||
CodeMirror.defineMIME("text/x-smarty", "smartymixed"); | ||||
// vim: et ts=2 sts=2 sw=2 | ||||