160 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			160 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|   | // CodeMirror, copyright (c) by Marijn Haverbeke and others
 | ||
|  | // Distributed under an MIT license: http://codemirror.net/LICENSE
 | ||
|  | 
 | ||
|  | (function(mod) { | ||
|  |   if (typeof exports == "object" && typeof module == "object") // CommonJS
 | ||
|  |     mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby")); | ||
|  |   else if (typeof define == "function" && define.amd) // AMD
 | ||
|  |     define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod); | ||
|  |   else // Plain browser env
 | ||
|  |     mod(CodeMirror); | ||
|  | })(function(CodeMirror) { | ||
|  | "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 state.tokenize(stream, state); | ||
|  |       } | ||
|  | 
 | ||
|  |       if (state.previousToken.style == "hamlTag" || | ||
|  |           state.previousToken.style == "closeAttributeTag" || | ||
|  |           state.previousToken.style == "hamlAttribute") { | ||
|  |         if (ch == "(") { | ||
|  |           state.tokenize = rubyInQuote(")"); | ||
|  |           return state.tokenize(stream, state); | ||
|  |         } else if (ch == "{") { | ||
|  |           state.tokenize = rubyInQuote("}"); | ||
|  |           return state.tokenize(stream, state); | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       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; | ||
|  |       } | ||
|  |     }; | ||
|  |   }, "htmlmixed", "ruby"); | ||
|  | 
 | ||
|  |   CodeMirror.defineMIME("text/x-haml", "haml"); | ||
|  | }); |