Skip to content

Commit

Permalink
[smartymixed mode] Add
Browse files Browse the repository at this point in the history
  • Loading branch information
rosmanov authored and marijnh committed Jul 12, 2013
1 parent 649b848 commit 47655e6
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -2,3 +2,5 @@
/npm-debug.log
test.html
.tern-*
*~
*.swp
107 changes: 107 additions & 0 deletions 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: -->
170 changes: 170 additions & 0 deletions 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

0 comments on commit 47655e6

Please sign in to comment.