all repos — NoPaste @ 3e3c433f026c49e04d0efa0f0f39fc688ed32bb1

Resurrected - The PussTheCat.org fork of NoPaste

scripts/CodeMirror/mode/haml/haml.js (view raw)

  1// CodeMirror, copyright (c) by Marijn Haverbeke and others
  2// Distributed under an MIT license: https://codemirror.net/LICENSE
  3
  4(function(mod) {
  5  if (typeof exports == "object" && typeof module == "object") // CommonJS
  6    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby"));
  7  else if (typeof define == "function" && define.amd) // AMD
  8    define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod);
  9  else // Plain browser env
 10    mod(CodeMirror);
 11})(function(CodeMirror) {
 12"use strict";
 13
 14  // full haml mode. This handled embedded ruby and html fragments too
 15  CodeMirror.defineMode("haml", function(config) {
 16    var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
 17    var rubyMode = CodeMirror.getMode(config, "ruby");
 18
 19    function rubyInQuote(endQuote) {
 20      return function(stream, state) {
 21        var ch = stream.peek();
 22        if (ch == endQuote && state.rubyState.tokenize.length == 1) {
 23          // step out of ruby context as it seems to complete processing all the braces
 24          stream.next();
 25          state.tokenize = html;
 26          return "closeAttributeTag";
 27        } else {
 28          return ruby(stream, state);
 29        }
 30      };
 31    }
 32
 33    function ruby(stream, state) {
 34      if (stream.match("-#")) {
 35        stream.skipToEnd();
 36        return "comment";
 37      }
 38      return rubyMode.token(stream, state.rubyState);
 39    }
 40
 41    function html(stream, state) {
 42      var ch = stream.peek();
 43
 44      // handle haml declarations. All declarations that cant be handled here
 45      // will be passed to html mode
 46      if (state.previousToken.style == "comment" ) {
 47        if (state.indented > state.previousToken.indented) {
 48          stream.skipToEnd();
 49          return "commentLine";
 50        }
 51      }
 52
 53      if (state.startOfLine) {
 54        if (ch == "!" && stream.match("!!")) {
 55          stream.skipToEnd();
 56          return "tag";
 57        } else if (stream.match(/^%[\w:#\.]+=/)) {
 58          state.tokenize = ruby;
 59          return "hamlTag";
 60        } else if (stream.match(/^%[\w:]+/)) {
 61          return "hamlTag";
 62        } else if (ch == "/" ) {
 63          stream.skipToEnd();
 64          return "comment";
 65        }
 66      }
 67
 68      if (state.startOfLine || state.previousToken.style == "hamlTag") {
 69        if ( ch == "#" || ch == ".") {
 70          stream.match(/[\w-#\.]*/);
 71          return "hamlAttribute";
 72        }
 73      }
 74
 75      // donot handle --> as valid ruby, make it HTML close comment instead
 76      if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
 77        state.tokenize = ruby;
 78        return state.tokenize(stream, state);
 79      }
 80
 81      if (state.previousToken.style == "hamlTag" ||
 82          state.previousToken.style == "closeAttributeTag" ||
 83          state.previousToken.style == "hamlAttribute") {
 84        if (ch == "(") {
 85          state.tokenize = rubyInQuote(")");
 86          return state.tokenize(stream, state);
 87        } else if (ch == "{") {
 88          if (!stream.match(/^\{%.*/)) {
 89            state.tokenize = rubyInQuote("}");
 90            return state.tokenize(stream, state);
 91          }
 92        }
 93      }
 94
 95      return htmlMode.token(stream, state.htmlState);
 96    }
 97
 98    return {
 99      // default to html mode
100      startState: function() {
101        var htmlState = CodeMirror.startState(htmlMode);
102        var rubyState = CodeMirror.startState(rubyMode);
103        return {
104          htmlState: htmlState,
105          rubyState: rubyState,
106          indented: 0,
107          previousToken: { style: null, indented: 0},
108          tokenize: html
109        };
110      },
111
112      copyState: function(state) {
113        return {
114          htmlState : CodeMirror.copyState(htmlMode, state.htmlState),
115          rubyState: CodeMirror.copyState(rubyMode, state.rubyState),
116          indented: state.indented,
117          previousToken: state.previousToken,
118          tokenize: state.tokenize
119        };
120      },
121
122      token: function(stream, state) {
123        if (stream.sol()) {
124          state.indented = stream.indentation();
125          state.startOfLine = true;
126        }
127        if (stream.eatSpace()) return null;
128        var style = state.tokenize(stream, state);
129        state.startOfLine = false;
130        // dont record comment line as we only want to measure comment line with
131        // the opening comment block
132        if (style && style != "commentLine") {
133          state.previousToken = { style: style, indented: state.indented };
134        }
135        // if current state is ruby and the previous token is not `,` reset the
136        // tokenize to html
137        if (stream.eol() && state.tokenize == ruby) {
138          stream.backUp(1);
139          var ch = stream.peek();
140          stream.next();
141          if (ch && ch != ",") {
142            state.tokenize = html;
143          }
144        }
145        // reprocess some of the specific style tag when finish setting previousToken
146        if (style == "hamlTag") {
147          style = "tag";
148        } else if (style == "commentLine") {
149          style = "comment";
150        } else if (style == "hamlAttribute") {
151          style = "attribute";
152        } else if (style == "closeAttributeTag") {
153          style = null;
154        }
155        return style;
156      }
157    };
158  }, "htmlmixed", "ruby");
159
160  CodeMirror.defineMIME("text/x-haml", "haml");
161});