4 // full haml mode. This handled embeded ruby and html fragments too
5 CodeMirror.defineMode("haml", function(config) {
6 var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
7 var rubyMode = CodeMirror.getMode(config, "ruby");
9 function rubyInQuote(endQuote) {
10 return function(stream, state) {
11 var ch = stream.peek();
12 if (ch == endQuote && state.rubyState.tokenize.length == 1) {
13 // step out of ruby context as it seems to complete processing all the braces
15 state.tokenize = html;
16 return "closeAttributeTag";
18 return ruby(stream, state);
23 function ruby(stream, state) {
24 if (stream.match("-#")) {
28 return rubyMode.token(stream, state.rubyState);
31 function html(stream, state) {
32 var ch = stream.peek();
34 // handle haml declarations. All declarations that cant be handled here
35 // will be passed to html mode
36 if (state.previousToken.style == "comment" ) {
37 if (state.indented > state.previousToken.indented) {
43 if (state.startOfLine) {
44 if (ch == "!" && stream.match("!!")) {
47 } else if (stream.match(/^%[\w:#\.]+=/)) {
48 state.tokenize = ruby;
50 } else if (stream.match(/^%[\w:]+/)) {
52 } else if (ch == "/" ) {
58 if (state.startOfLine || state.previousToken.style == "hamlTag") {
59 if ( ch == "#" || ch == ".") {
60 stream.match(/[\w-#\.]*/);
61 return "hamlAttribute";
65 // donot handle --> as valid ruby, make it HTML close comment instead
66 if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
67 state.tokenize = ruby;
71 if (state.previousToken.style == "hamlTag" ||
72 state.previousToken.style == "closeAttributeTag" ||
73 state.previousToken.style == "hamlAttribute") {
75 state.tokenize = rubyInQuote(")");
77 } else if (ch == "{") {
78 state.tokenize = rubyInQuote("}");
83 return htmlMode.token(stream, state.htmlState);
87 // default to html mode
88 startState: function() {
89 var htmlState = htmlMode.startState();
90 var rubyState = rubyMode.startState();
95 previousToken: { style: null, indented: 0},
100 copyState: function(state) {
102 htmlState : CodeMirror.copyState(htmlMode, state.htmlState),
103 rubyState: CodeMirror.copyState(rubyMode, state.rubyState),
104 indented: state.indented,
105 previousToken: state.previousToken,
106 tokenize: state.tokenize
110 token: function(stream, state) {
112 state.indented = stream.indentation();
113 state.startOfLine = true;
115 if (stream.eatSpace()) return null;
116 var style = state.tokenize(stream, state);
117 state.startOfLine = false;
118 // dont record comment line as we only want to measure comment line with
119 // the opening comment block
120 if (style && style != "commentLine") {
121 state.previousToken = { style: style, indented: state.indented };
123 // if current state is ruby and the previous token is not `,` reset the
125 if (stream.eol() && state.tokenize == ruby) {
127 var ch = stream.peek();
129 if (ch && ch != ",") {
130 state.tokenize = html;
133 // reprocess some of the specific style tag when finish setting previousToken
134 if (style == "hamlTag") {
136 } else if (style == "commentLine") {
138 } else if (style == "hamlAttribute") {
140 } else if (style == "closeAttributeTag") {
146 indent: function(state) {
147 return state.indented;
150 }, "htmlmixed", "ruby");
152 CodeMirror.defineMIME("text/x-haml", "haml");