From 47655e693d5d29fc549797aa0cd79800d7828427 Mon Sep 17 00:00:00 2001
From: Ruslan Osmanov <rrosmanov@gmail.com>
Date: Sat, 29 Jun 2013 19:28:16 +0500
Subject: [PATCH] [smartymixed mode] Add

---
 .gitignore                      |   2 +
 mode/smartymixed/index.html     | 107 ++++++++++++++++++++
 mode/smartymixed/smartymixed.js | 170 ++++++++++++++++++++++++++++++++
 3 files changed, 279 insertions(+)
 create mode 100644 mode/smartymixed/index.html
 create mode 100644 mode/smartymixed/smartymixed.js

diff --git a/.gitignore b/.gitignore
index bc20ab58d..b471fe6e6 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 000000000..6567b27d6
--- /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 000000000..c5d008885
--- /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
-- 
GitLab