diff --git a/.gitignore b/.gitignore index bc20ab58d525acc977fcf9cdab3e9ef8e464edf5..b471fe6e63490a5aaac5f80e6e02e8e388876b37 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /npm-debug.log test.html .tern-* +*~ +*.swp diff --git a/mode/smartymixed/index.html b/mode/smartymixed/index.html new file mode 100644 index 0000000000000000000000000000000000000000..6567b27d6b12c0ed1f45ec9c8d8ed2d1630de257 --- /dev/null +++ b/mode/smartymixed/index.html @@ -0,0 +1,107 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>CodeMirror: Smarty mixed mode</title> + <link rel="stylesheet" href="../../lib/codemirror.css"> + <script src="../../lib/codemirror.js"></script> + <link rel="stylesheet" href="../../doc/docs.css"> + + <!-- smartymixed dependencies --> + <script src="../../mode/xml/xml.js"></script> + <script src="../../mode/javascript/javascript.js"></script> + <script src="../../mode/css/css.js"></script> + <script src="../../mode/htmlmixed/htmlmixed.js"></script> + <script src="../../mode/smarty/smarty.js"></script> + + <!-- smartymixed --> + <script src="../../mode/smartymixed/smartymixed.js"></script> + </head> + <body> + <h1>CodeMirror: Smarty mixed mode</h1> + <form><textarea id="code" name="code"> +{** +* @brief Smarty mixed mode +* @author Ruslan Osmanov +* @date 29.06.2013 +*} +<html> +<head> + <title>{$title|htmlspecialchars|truncate:30}</title> +</head> +<body> + {* Multiline smarty + * comment, no {$variables} here + *} + {literal} + {literal} is just an HTML text. + <script type="text/javascript">//<![CDATA[ + var a = {$just_a_normal_js_object : "value"}; + var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("code"), { + mode : "smartymixed", + tabSize : 2, + indentUnit : 2, + indentWithTabs : false, + lineNumbers : true, + smartyVersion : 3 + }); + // ]]> + </script> + <style> + /* CSS content + {$no_smarty} */ + .some-class { font-weight: bolder; color: "orange"; } + </style> + {/literal} + + {extends file="parent.tpl"} + {include file="template.tpl"} + + {* some example Smarty content *} + {if isset($name) && $name == 'Blog'} + This is a {$var}. + {$integer = 4511}, {$array[] = "a"}, {$stringvar = "string"} + {$integer = 4512} {$array[] = "a"} {$stringvar = "string"} + {assign var='bob' value=$var.prop} + {elseif $name == $foo} + {function name=menu level=0} + {foreach $data as $entry} + {if is_array($entry)} + - {$entry@key} + {menu data=$entry level=$level+1} + {else} + {$entry} + {* One + * Two + * Three + *} + {/if} + {/foreach} + {/function} + {/if} + </body> + <!-- R.O. --> +</html> +</textarea></form> + + <script type="text/javascript"> + var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("code"), { + mode : "smartymixed", + tabSize : 2, + indentUnit : 2, + indentWithTabs : false, + lineNumbers : true, + smartyVersion : 3, + matchBrackets : true, + }); + </script> + + <p>The Smarty mixed mode depends on the Smarty and HTML mixed modes. HTML + mixed mode itself depends on XML, JavaScript, and CSS modes.</p> + + <p>It takes the same options, as Smarty and HTML mixed modes.</p> + + <p><strong>MIME types defined:</strong> <code>text/x-smarty</code>.</p> + </body> +</html> +<!-- vim: set ft=html ts=2 sts=2 sw=2 et: --> diff --git a/mode/smartymixed/smartymixed.js b/mode/smartymixed/smartymixed.js new file mode 100644 index 0000000000000000000000000000000000000000..c5d008885abad9121aef327d5e5590760fc85cf9 --- /dev/null +++ b/mode/smartymixed/smartymixed.js @@ -0,0 +1,170 @@ +/** +* @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