| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  | // CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
					
						
							| 
									
										
										
										
											2018-10-07 12:02:07 +02:00
										 |  |  | // Distributed under an MIT license: https://codemirror.net/LICENSE
 | 
					
						
							| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Link to the project's GitHub page: | 
					
						
							|  |  |  |  * https://github.com/pickhardt/coffeescript-codemirror-mode
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | (function(mod) { | 
					
						
							|  |  |  |   if (typeof exports == "object" && typeof module == "object") // CommonJS
 | 
					
						
							|  |  |  |     mod(require("../../lib/codemirror")); | 
					
						
							|  |  |  |   else if (typeof define == "function" && define.amd) // AMD
 | 
					
						
							|  |  |  |     define(["../../lib/codemirror"], mod); | 
					
						
							|  |  |  |   else // Plain browser env
 | 
					
						
							|  |  |  |     mod(CodeMirror); | 
					
						
							|  |  |  | })(function(CodeMirror) { | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CodeMirror.defineMode("coffeescript", function(conf, parserConf) { | 
					
						
							|  |  |  |   var ERRORCLASS = "error"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function wordRegexp(words) { | 
					
						
							|  |  |  |     return new RegExp("^((" + words.join(")|(") + "))\\b"); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/; | 
					
						
							|  |  |  |   var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/; | 
					
						
							|  |  |  |   var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/; | 
					
						
							|  |  |  |   var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var wordOperators = wordRegexp(["and", "or", "not", | 
					
						
							|  |  |  |                                   "is", "isnt", "in", | 
					
						
							|  |  |  |                                   "instanceof", "typeof"]); | 
					
						
							|  |  |  |   var indentKeywords = ["for", "while", "loop", "if", "unless", "else", | 
					
						
							|  |  |  |                         "switch", "try", "catch", "finally", "class"]; | 
					
						
							|  |  |  |   var commonKeywords = ["break", "by", "continue", "debugger", "delete", | 
					
						
							|  |  |  |                         "do", "in", "of", "new", "return", "then", | 
					
						
							|  |  |  |                         "this", "@", "throw", "when", "until", "extends"]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var keywords = wordRegexp(indentKeywords.concat(commonKeywords)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   indentKeywords = wordRegexp(indentKeywords); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var stringPrefixes = /^('{3}|\"{3}|['\"])/; | 
					
						
							|  |  |  |   var regexPrefixes = /^(\/{3}|\/)/; | 
					
						
							|  |  |  |   var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"]; | 
					
						
							|  |  |  |   var constants = wordRegexp(commonConstants); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Tokenizers
 | 
					
						
							|  |  |  |   function tokenBase(stream, state) { | 
					
						
							|  |  |  |     // Handle scope changes
 | 
					
						
							|  |  |  |     if (stream.sol()) { | 
					
						
							|  |  |  |       if (state.scope.align === null) state.scope.align = false; | 
					
						
							|  |  |  |       var scopeOffset = state.scope.offset; | 
					
						
							|  |  |  |       if (stream.eatSpace()) { | 
					
						
							|  |  |  |         var lineOffset = stream.indentation(); | 
					
						
							|  |  |  |         if (lineOffset > scopeOffset && state.scope.type == "coffee") { | 
					
						
							|  |  |  |           return "indent"; | 
					
						
							|  |  |  |         } else if (lineOffset < scopeOffset) { | 
					
						
							|  |  |  |           return "dedent"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         if (scopeOffset > 0) { | 
					
						
							|  |  |  |           dedent(stream, state); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (stream.eatSpace()) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var ch = stream.peek(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle docco title comment (single line)
 | 
					
						
							|  |  |  |     if (stream.match("####")) { | 
					
						
							|  |  |  |       stream.skipToEnd(); | 
					
						
							|  |  |  |       return "comment"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle multi line comments
 | 
					
						
							|  |  |  |     if (stream.match("###")) { | 
					
						
							|  |  |  |       state.tokenize = longComment; | 
					
						
							|  |  |  |       return state.tokenize(stream, state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Single line comment
 | 
					
						
							|  |  |  |     if (ch === "#") { | 
					
						
							|  |  |  |       stream.skipToEnd(); | 
					
						
							|  |  |  |       return "comment"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle number literals
 | 
					
						
							|  |  |  |     if (stream.match(/^-?[0-9\.]/, false)) { | 
					
						
							|  |  |  |       var floatLiteral = false; | 
					
						
							|  |  |  |       // Floats
 | 
					
						
							|  |  |  |       if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) { | 
					
						
							|  |  |  |         floatLiteral = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (stream.match(/^-?\d+\.\d*/)) { | 
					
						
							|  |  |  |         floatLiteral = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (stream.match(/^-?\.\d+/)) { | 
					
						
							|  |  |  |         floatLiteral = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (floatLiteral) { | 
					
						
							|  |  |  |         // prevent from getting extra . on 1..
 | 
					
						
							|  |  |  |         if (stream.peek() == "."){ | 
					
						
							|  |  |  |           stream.backUp(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return "number"; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Integers
 | 
					
						
							|  |  |  |       var intLiteral = false; | 
					
						
							|  |  |  |       // Hex
 | 
					
						
							|  |  |  |       if (stream.match(/^-?0x[0-9a-f]+/i)) { | 
					
						
							|  |  |  |         intLiteral = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Decimal
 | 
					
						
							|  |  |  |       if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) { | 
					
						
							|  |  |  |         intLiteral = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Zero by itself with no other piece of number.
 | 
					
						
							|  |  |  |       if (stream.match(/^-?0(?![\dx])/i)) { | 
					
						
							|  |  |  |         intLiteral = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (intLiteral) { | 
					
						
							|  |  |  |         return "number"; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle strings
 | 
					
						
							|  |  |  |     if (stream.match(stringPrefixes)) { | 
					
						
							|  |  |  |       state.tokenize = tokenFactory(stream.current(), false, "string"); | 
					
						
							|  |  |  |       return state.tokenize(stream, state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Handle regex literals
 | 
					
						
							|  |  |  |     if (stream.match(regexPrefixes)) { | 
					
						
							|  |  |  |       if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
 | 
					
						
							|  |  |  |         state.tokenize = tokenFactory(stream.current(), true, "string-2"); | 
					
						
							|  |  |  |         return state.tokenize(stream, state); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         stream.backUp(1); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle operators and delimiters
 | 
					
						
							|  |  |  |     if (stream.match(operators) || stream.match(wordOperators)) { | 
					
						
							|  |  |  |       return "operator"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (stream.match(delimiters)) { | 
					
						
							|  |  |  |       return "punctuation"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (stream.match(constants)) { | 
					
						
							|  |  |  |       return "atom"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (stream.match(atProp) || state.prop && stream.match(identifiers)) { | 
					
						
							|  |  |  |       return "property"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (stream.match(keywords)) { | 
					
						
							|  |  |  |       return "keyword"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (stream.match(identifiers)) { | 
					
						
							|  |  |  |       return "variable"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle non-detected items
 | 
					
						
							|  |  |  |     stream.next(); | 
					
						
							|  |  |  |     return ERRORCLASS; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function tokenFactory(delimiter, singleline, outclass) { | 
					
						
							|  |  |  |     return function(stream, state) { | 
					
						
							|  |  |  |       while (!stream.eol()) { | 
					
						
							|  |  |  |         stream.eatWhile(/[^'"\/\\]/); | 
					
						
							|  |  |  |         if (stream.eat("\\")) { | 
					
						
							|  |  |  |           stream.next(); | 
					
						
							|  |  |  |           if (singleline && stream.eol()) { | 
					
						
							|  |  |  |             return outclass; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } else if (stream.match(delimiter)) { | 
					
						
							|  |  |  |           state.tokenize = tokenBase; | 
					
						
							|  |  |  |           return outclass; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           stream.eat(/['"\/]/); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (singleline) { | 
					
						
							|  |  |  |         if (parserConf.singleLineStringErrors) { | 
					
						
							|  |  |  |           outclass = ERRORCLASS; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           state.tokenize = tokenBase; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return outclass; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function longComment(stream, state) { | 
					
						
							|  |  |  |     while (!stream.eol()) { | 
					
						
							|  |  |  |       stream.eatWhile(/[^#]/); | 
					
						
							|  |  |  |       if (stream.match("###")) { | 
					
						
							|  |  |  |         state.tokenize = tokenBase; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       stream.eatWhile("#"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return "comment"; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function indent(stream, state, type) { | 
					
						
							|  |  |  |     type = type || "coffee"; | 
					
						
							|  |  |  |     var offset = 0, align = false, alignOffset = null; | 
					
						
							|  |  |  |     for (var scope = state.scope; scope; scope = scope.prev) { | 
					
						
							|  |  |  |       if (scope.type === "coffee" || scope.type == "}") { | 
					
						
							|  |  |  |         offset = scope.offset + conf.indentUnit; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (type !== "coffee") { | 
					
						
							|  |  |  |       align = null; | 
					
						
							|  |  |  |       alignOffset = stream.column() + stream.current().length; | 
					
						
							|  |  |  |     } else if (state.scope.align) { | 
					
						
							|  |  |  |       state.scope.align = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     state.scope = { | 
					
						
							|  |  |  |       offset: offset, | 
					
						
							|  |  |  |       type: type, | 
					
						
							|  |  |  |       prev: state.scope, | 
					
						
							|  |  |  |       align: align, | 
					
						
							|  |  |  |       alignOffset: alignOffset | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function dedent(stream, state) { | 
					
						
							|  |  |  |     if (!state.scope.prev) return; | 
					
						
							|  |  |  |     if (state.scope.type === "coffee") { | 
					
						
							|  |  |  |       var _indent = stream.indentation(); | 
					
						
							|  |  |  |       var matched = false; | 
					
						
							|  |  |  |       for (var scope = state.scope; scope; scope = scope.prev) { | 
					
						
							|  |  |  |         if (_indent === scope.offset) { | 
					
						
							|  |  |  |           matched = true; | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (!matched) { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       while (state.scope.prev && state.scope.offset !== _indent) { | 
					
						
							|  |  |  |         state.scope = state.scope.prev; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       state.scope = state.scope.prev; | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function tokenLexer(stream, state) { | 
					
						
							|  |  |  |     var style = state.tokenize(stream, state); | 
					
						
							|  |  |  |     var current = stream.current(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle scope changes.
 | 
					
						
							|  |  |  |     if (current === "return") { | 
					
						
							|  |  |  |       state.dedent = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (((current === "->" || current === "=>") && stream.eol()) | 
					
						
							|  |  |  |         || style === "indent") { | 
					
						
							|  |  |  |       indent(stream, state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     var delimiter_index = "[({".indexOf(current); | 
					
						
							|  |  |  |     if (delimiter_index !== -1) { | 
					
						
							|  |  |  |       indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (indentKeywords.exec(current)){ | 
					
						
							|  |  |  |       indent(stream, state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (current == "then"){ | 
					
						
							|  |  |  |       dedent(stream, state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (style === "dedent") { | 
					
						
							|  |  |  |       if (dedent(stream, state)) { | 
					
						
							|  |  |  |         return ERRORCLASS; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     delimiter_index = "])}".indexOf(current); | 
					
						
							|  |  |  |     if (delimiter_index !== -1) { | 
					
						
							|  |  |  |       while (state.scope.type == "coffee" && state.scope.prev) | 
					
						
							|  |  |  |         state.scope = state.scope.prev; | 
					
						
							|  |  |  |       if (state.scope.type == current) | 
					
						
							|  |  |  |         state.scope = state.scope.prev; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (state.dedent && stream.eol()) { | 
					
						
							|  |  |  |       if (state.scope.type == "coffee" && state.scope.prev) | 
					
						
							|  |  |  |         state.scope = state.scope.prev; | 
					
						
							|  |  |  |       state.dedent = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return style; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var external = { | 
					
						
							|  |  |  |     startState: function(basecolumn) { | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         tokenize: tokenBase, | 
					
						
							|  |  |  |         scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false}, | 
					
						
							|  |  |  |         prop: false, | 
					
						
							|  |  |  |         dedent: 0 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     token: function(stream, state) { | 
					
						
							|  |  |  |       var fillAlign = state.scope.align === null && state.scope; | 
					
						
							|  |  |  |       if (fillAlign && stream.sol()) fillAlign.align = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var style = tokenLexer(stream, state); | 
					
						
							|  |  |  |       if (style && style != "comment") { | 
					
						
							|  |  |  |         if (fillAlign) fillAlign.align = true; | 
					
						
							|  |  |  |         state.prop = style == "punctuation" && stream.current() == "." | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return style; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     indent: function(state, text) { | 
					
						
							|  |  |  |       if (state.tokenize != tokenBase) return 0; | 
					
						
							|  |  |  |       var scope = state.scope; | 
					
						
							|  |  |  |       var closer = text && "])}".indexOf(text.charAt(0)) > -1; | 
					
						
							|  |  |  |       if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev; | 
					
						
							|  |  |  |       var closes = closer && scope.type === text.charAt(0); | 
					
						
							|  |  |  |       if (scope.align) | 
					
						
							|  |  |  |         return scope.alignOffset - (closes ? 1 : 0); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         return (closes ? scope.prev : scope).offset; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     lineComment: "#", | 
					
						
							|  |  |  |     fold: "indent" | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   return external; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IANA registered media type
 | 
					
						
							|  |  |  | // https://www.iana.org/assignments/media-types/
 | 
					
						
							|  |  |  | CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); | 
					
						
							|  |  |  | CodeMirror.defineMIME("text/coffeescript", "coffeescript"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }); |