123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- // CodeMirror, copyright (c) by Marijn Haverbeke and others
- // Distributed under an MIT license: http://codemirror.net/LICENSE
- /**
- * @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
- */
- // Warning: Don't base other modes on this one. This here is a
- // terrible way to write a mixed mode.
- (function(mod) {
- if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../smarty/smarty"));
- else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../smarty/smarty"], mod);
- else // Plain browser env
- mod(CodeMirror);
- })(function(CodeMirror) {
- "use strict";
- CodeMirror.defineMode("smartymixed", function(config) {
- var htmlMixedMode = CodeMirror.getMode(config, "htmlmixed");
- var smartyMode = CodeMirror.getMode(config, "smarty");
- var settings = {
- rightDelimiter: '}',
- leftDelimiter: '{'
- };
- if (config.hasOwnProperty("leftDelimiter")) {
- settings.leftDelimiter = config.leftDelimiter;
- }
- if (config.hasOwnProperty("rightDelimiter")) {
- settings.rightDelimiter = config.rightDelimiter;
- }
- function reEsc(str) { return str.replace(/[^\s\w]/g, "\\$&"); }
- var reLeft = reEsc(settings.leftDelimiter), reRight = reEsc(settings.rightDelimiter);
- var regs = {
- smartyComment: new RegExp("^" + reRight + "\\*"),
- literalOpen: new RegExp(reLeft + "literal" + reRight),
- literalClose: new RegExp(reLeft + "\/literal" + reRight),
- hasLeftDelimeter: new RegExp(".*" + reLeft),
- htmlHasLeftDelimeter: new RegExp("[^<>]*" + reLeft)
- };
- var 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;
- }
- };
- var parsers = {
- html: function(stream, state) {
- var htmlTagName = state.htmlMixedState.htmlState.context && state.htmlMixedState.htmlState.context.tagName
- ? state.htmlMixedState.htmlState.context.tagName
- : null;
- if (!state.inLiteral && stream.match(regs.htmlHasLeftDelimeter, false) && htmlTagName === null) {
- 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));
- } else if (!state.inLiteral && stream.match(settings.leftDelimiter, 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);
- },
- innerMode: function(state) {
- return {
- state: state.localState || state.htmlMixedState,
- mode: state.localMode || htmlMixedMode
- };
- }
- };
- }, "htmlmixed", "smarty");
- CodeMirror.defineMIME("text/x-smarty", "smartymixed");
- // vim: et ts=2 sts=2 sw=2
- });
|