// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE /** * Smarty 2 and 3 mode. */ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; 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"); });