move a few things away in to-be-integrated/
[myslice.git] / to-be-integrated / third-party / codemirror-3.15 / mode / haml / haml.js
diff --git a/to-be-integrated/third-party/codemirror-3.15/mode/haml/haml.js b/to-be-integrated/third-party/codemirror-3.15/mode/haml/haml.js
new file mode 100644 (file)
index 0000000..793308f
--- /dev/null
@@ -0,0 +1,153 @@
+(function() {
+  "use strict";
+
+  // full haml mode. This handled embeded ruby and html fragments too
+  CodeMirror.defineMode("haml", function(config) {
+    var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
+    var rubyMode = CodeMirror.getMode(config, "ruby");
+
+    function rubyInQuote(endQuote) {
+      return function(stream, state) {
+        var ch = stream.peek();
+        if (ch == endQuote && state.rubyState.tokenize.length == 1) {
+          // step out of ruby context as it seems to complete processing all the braces
+          stream.next();
+          state.tokenize = html;
+          return "closeAttributeTag";
+        } else {
+          return ruby(stream, state);
+        }
+      };
+    }
+
+    function ruby(stream, state) {
+      if (stream.match("-#")) {
+        stream.skipToEnd();
+        return "comment";
+      }
+      return rubyMode.token(stream, state.rubyState);
+    }
+
+    function html(stream, state) {
+      var ch = stream.peek();
+
+      // handle haml declarations. All declarations that cant be handled here
+      // will be passed to html mode
+      if (state.previousToken.style == "comment" ) {
+        if (state.indented > state.previousToken.indented) {
+          stream.skipToEnd();
+          return "commentLine";
+        }
+      }
+
+      if (state.startOfLine) {
+        if (ch == "!" && stream.match("!!")) {
+          stream.skipToEnd();
+          return "tag";
+        } else if (stream.match(/^%[\w:#\.]+=/)) {
+          state.tokenize = ruby;
+          return "hamlTag";
+        } else if (stream.match(/^%[\w:]+/)) {
+          return "hamlTag";
+        } else if (ch == "/" ) {
+          stream.skipToEnd();
+          return "comment";
+        }
+      }
+
+      if (state.startOfLine || state.previousToken.style == "hamlTag") {
+        if ( ch == "#" || ch == ".") {
+          stream.match(/[\w-#\.]*/);
+          return "hamlAttribute";
+        }
+      }
+
+      // donot handle --> as valid ruby, make it HTML close comment instead
+      if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
+        state.tokenize = ruby;
+        return null;
+      }
+
+      if (state.previousToken.style == "hamlTag" ||
+          state.previousToken.style == "closeAttributeTag" ||
+          state.previousToken.style == "hamlAttribute") {
+        if (ch == "(") {
+          state.tokenize = rubyInQuote(")");
+          return null;
+        } else if (ch == "{") {
+          state.tokenize = rubyInQuote("}");
+          return null;
+        }
+      }
+
+      return htmlMode.token(stream, state.htmlState);
+    }
+
+    return {
+      // default to html mode
+      startState: function() {
+        var htmlState = htmlMode.startState();
+        var rubyState = rubyMode.startState();
+        return {
+          htmlState: htmlState,
+          rubyState: rubyState,
+          indented: 0,
+          previousToken: { style: null, indented: 0},
+          tokenize: html
+        };
+      },
+
+      copyState: function(state) {
+        return {
+          htmlState : CodeMirror.copyState(htmlMode, state.htmlState),
+          rubyState: CodeMirror.copyState(rubyMode, state.rubyState),
+          indented: state.indented,
+          previousToken: state.previousToken,
+          tokenize: state.tokenize
+        };
+      },
+
+      token: function(stream, state) {
+        if (stream.sol()) {
+          state.indented = stream.indentation();
+          state.startOfLine = true;
+        }
+        if (stream.eatSpace()) return null;
+        var style = state.tokenize(stream, state);
+        state.startOfLine = false;
+        // dont record comment line as we only want to measure comment line with
+        // the opening comment block
+        if (style && style != "commentLine") {
+          state.previousToken = { style: style, indented: state.indented };
+        }
+        // if current state is ruby and the previous token is not `,` reset the
+        // tokenize to html
+        if (stream.eol() && state.tokenize == ruby) {
+          stream.backUp(1);
+          var ch = stream.peek();
+          stream.next();
+          if (ch && ch != ",") {
+            state.tokenize = html;
+          }
+        }
+        // reprocess some of the specific style tag when finish setting previousToken
+        if (style == "hamlTag") {
+          style = "tag";
+        } else if (style == "commentLine") {
+          style = "comment";
+        } else if (style == "hamlAttribute") {
+          style = "attribute";
+        } else if (style == "closeAttributeTag") {
+          style = null;
+        }
+        return style;
+      },
+
+      indent: function(state) {
+        return state.indented;
+      }
+    };
+  }, "htmlmixed", "ruby");
+
+  CodeMirror.defineMIME("text/x-haml", "haml");
+})();