File modules/editor/codemirror/mode/coffeescript/coffeescript.js
Last commit: Sun Dec 17 01:14:09 2017 +0100 Jan Dankert Integration eines weiteren Code-Editors: Codemirror. Demnächst müssen wir hier mal aufräumen und andere Editoren rauswerfen.
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 /** 5 * Link to the project's GitHub page: 6 * https://github.com/pickhardt/coffeescript-codemirror-mode 7 */ 8 (function(mod) { 9 if (typeof exports == "object" && typeof module == "object") // CommonJS 10 mod(require("../../lib/codemirror")); 11 else if (typeof define == "function" && define.amd) // AMD 12 define(["../../lib/codemirror"], mod); 13 else // Plain browser env 14 mod(CodeMirror); 15 })(function(CodeMirror) { 16 "use strict"; 17 18 CodeMirror.defineMode("coffeescript", function(conf, parserConf) { 19 var ERRORCLASS = "error"; 20 21 function wordRegexp(words) { 22 return new RegExp("^((" + words.join(")|(") + "))\\b"); 23 } 24 25 var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/; 26 var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/; 27 var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/; 28 var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/; 29 30 var wordOperators = wordRegexp(["and", "or", "not", 31 "is", "isnt", "in", 32 "instanceof", "typeof"]); 33 var indentKeywords = ["for", "while", "loop", "if", "unless", "else", 34 "switch", "try", "catch", "finally", "class"]; 35 var commonKeywords = ["break", "by", "continue", "debugger", "delete", 36 "do", "in", "of", "new", "return", "then", 37 "this", "@", "throw", "when", "until", "extends"]; 38 39 var keywords = wordRegexp(indentKeywords.concat(commonKeywords)); 40 41 indentKeywords = wordRegexp(indentKeywords); 42 43 44 var stringPrefixes = /^('{3}|\"{3}|['\"])/; 45 var regexPrefixes = /^(\/{3}|\/)/; 46 var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"]; 47 var constants = wordRegexp(commonConstants); 48 49 // Tokenizers 50 function tokenBase(stream, state) { 51 // Handle scope changes 52 if (stream.sol()) { 53 if (state.scope.align === null) state.scope.align = false; 54 var scopeOffset = state.scope.offset; 55 if (stream.eatSpace()) { 56 var lineOffset = stream.indentation(); 57 if (lineOffset > scopeOffset && state.scope.type == "coffee") { 58 return "indent"; 59 } else if (lineOffset < scopeOffset) { 60 return "dedent"; 61 } 62 return null; 63 } else { 64 if (scopeOffset > 0) { 65 dedent(stream, state); 66 } 67 } 68 } 69 if (stream.eatSpace()) { 70 return null; 71 } 72 73 var ch = stream.peek(); 74 75 // Handle docco title comment (single line) 76 if (stream.match("####")) { 77 stream.skipToEnd(); 78 return "comment"; 79 } 80 81 // Handle multi line comments 82 if (stream.match("###")) { 83 state.tokenize = longComment; 84 return state.tokenize(stream, state); 85 } 86 87 // Single line comment 88 if (ch === "#") { 89 stream.skipToEnd(); 90 return "comment"; 91 } 92 93 // Handle number literals 94 if (stream.match(/^-?[0-9\.]/, false)) { 95 var floatLiteral = false; 96 // Floats 97 if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) { 98 floatLiteral = true; 99 } 100 if (stream.match(/^-?\d+\.\d*/)) { 101 floatLiteral = true; 102 } 103 if (stream.match(/^-?\.\d+/)) { 104 floatLiteral = true; 105 } 106 107 if (floatLiteral) { 108 // prevent from getting extra . on 1.. 109 if (stream.peek() == "."){ 110 stream.backUp(1); 111 } 112 return "number"; 113 } 114 // Integers 115 var intLiteral = false; 116 // Hex 117 if (stream.match(/^-?0x[0-9a-f]+/i)) { 118 intLiteral = true; 119 } 120 // Decimal 121 if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) { 122 intLiteral = true; 123 } 124 // Zero by itself with no other piece of number. 125 if (stream.match(/^-?0(?![\dx])/i)) { 126 intLiteral = true; 127 } 128 if (intLiteral) { 129 return "number"; 130 } 131 } 132 133 // Handle strings 134 if (stream.match(stringPrefixes)) { 135 state.tokenize = tokenFactory(stream.current(), false, "string"); 136 return state.tokenize(stream, state); 137 } 138 // Handle regex literals 139 if (stream.match(regexPrefixes)) { 140 if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division 141 state.tokenize = tokenFactory(stream.current(), true, "string-2"); 142 return state.tokenize(stream, state); 143 } else { 144 stream.backUp(1); 145 } 146 } 147 148 149 150 // Handle operators and delimiters 151 if (stream.match(operators) || stream.match(wordOperators)) { 152 return "operator"; 153 } 154 if (stream.match(delimiters)) { 155 return "punctuation"; 156 } 157 158 if (stream.match(constants)) { 159 return "atom"; 160 } 161 162 if (stream.match(atProp) || state.prop && stream.match(identifiers)) { 163 return "property"; 164 } 165 166 if (stream.match(keywords)) { 167 return "keyword"; 168 } 169 170 if (stream.match(identifiers)) { 171 return "variable"; 172 } 173 174 // Handle non-detected items 175 stream.next(); 176 return ERRORCLASS; 177 } 178 179 function tokenFactory(delimiter, singleline, outclass) { 180 return function(stream, state) { 181 while (!stream.eol()) { 182 stream.eatWhile(/[^'"\/\\]/); 183 if (stream.eat("\\")) { 184 stream.next(); 185 if (singleline && stream.eol()) { 186 return outclass; 187 } 188 } else if (stream.match(delimiter)) { 189 state.tokenize = tokenBase; 190 return outclass; 191 } else { 192 stream.eat(/['"\/]/); 193 } 194 } 195 if (singleline) { 196 if (parserConf.singleLineStringErrors) { 197 outclass = ERRORCLASS; 198 } else { 199 state.tokenize = tokenBase; 200 } 201 } 202 return outclass; 203 }; 204 } 205 206 function longComment(stream, state) { 207 while (!stream.eol()) { 208 stream.eatWhile(/[^#]/); 209 if (stream.match("###")) { 210 state.tokenize = tokenBase; 211 break; 212 } 213 stream.eatWhile("#"); 214 } 215 return "comment"; 216 } 217 218 function indent(stream, state, type) { 219 type = type || "coffee"; 220 var offset = 0, align = false, alignOffset = null; 221 for (var scope = state.scope; scope; scope = scope.prev) { 222 if (scope.type === "coffee" || scope.type == "}") { 223 offset = scope.offset + conf.indentUnit; 224 break; 225 } 226 } 227 if (type !== "coffee") { 228 align = null; 229 alignOffset = stream.column() + stream.current().length; 230 } else if (state.scope.align) { 231 state.scope.align = false; 232 } 233 state.scope = { 234 offset: offset, 235 type: type, 236 prev: state.scope, 237 align: align, 238 alignOffset: alignOffset 239 }; 240 } 241 242 function dedent(stream, state) { 243 if (!state.scope.prev) return; 244 if (state.scope.type === "coffee") { 245 var _indent = stream.indentation(); 246 var matched = false; 247 for (var scope = state.scope; scope; scope = scope.prev) { 248 if (_indent === scope.offset) { 249 matched = true; 250 break; 251 } 252 } 253 if (!matched) { 254 return true; 255 } 256 while (state.scope.prev && state.scope.offset !== _indent) { 257 state.scope = state.scope.prev; 258 } 259 return false; 260 } else { 261 state.scope = state.scope.prev; 262 return false; 263 } 264 } 265 266 function tokenLexer(stream, state) { 267 var style = state.tokenize(stream, state); 268 var current = stream.current(); 269 270 // Handle scope changes. 271 if (current === "return") { 272 state.dedent = true; 273 } 274 if (((current === "->" || current === "=>") && stream.eol()) 275 || style === "indent") { 276 indent(stream, state); 277 } 278 var delimiter_index = "[({".indexOf(current); 279 if (delimiter_index !== -1) { 280 indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); 281 } 282 if (indentKeywords.exec(current)){ 283 indent(stream, state); 284 } 285 if (current == "then"){ 286 dedent(stream, state); 287 } 288 289 290 if (style === "dedent") { 291 if (dedent(stream, state)) { 292 return ERRORCLASS; 293 } 294 } 295 delimiter_index = "])}".indexOf(current); 296 if (delimiter_index !== -1) { 297 while (state.scope.type == "coffee" && state.scope.prev) 298 state.scope = state.scope.prev; 299 if (state.scope.type == current) 300 state.scope = state.scope.prev; 301 } 302 if (state.dedent && stream.eol()) { 303 if (state.scope.type == "coffee" && state.scope.prev) 304 state.scope = state.scope.prev; 305 state.dedent = false; 306 } 307 308 return style; 309 } 310 311 var external = { 312 startState: function(basecolumn) { 313 return { 314 tokenize: tokenBase, 315 scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false}, 316 prop: false, 317 dedent: 0 318 }; 319 }, 320 321 token: function(stream, state) { 322 var fillAlign = state.scope.align === null && state.scope; 323 if (fillAlign && stream.sol()) fillAlign.align = false; 324 325 var style = tokenLexer(stream, state); 326 if (style && style != "comment") { 327 if (fillAlign) fillAlign.align = true; 328 state.prop = style == "punctuation" && stream.current() == "." 329 } 330 331 return style; 332 }, 333 334 indent: function(state, text) { 335 if (state.tokenize != tokenBase) return 0; 336 var scope = state.scope; 337 var closer = text && "])}".indexOf(text.charAt(0)) > -1; 338 if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev; 339 var closes = closer && scope.type === text.charAt(0); 340 if (scope.align) 341 return scope.alignOffset - (closes ? 1 : 0); 342 else 343 return (closes ? scope.prev : scope).offset; 344 }, 345 346 lineComment: "#", 347 fold: "indent" 348 }; 349 return external; 350 }); 351 352 // IANA registered media type 353 // https://www.iana.org/assignments/media-types/ 354 CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript"); 355 356 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); 357 CodeMirror.defineMIME("text/coffeescript", "coffeescript"); 358 359 });
Downloadmodules/editor/codemirror/mode/coffeescript/coffeescript.js
History Sun, 17 Dec 2017 01:14:09 +0100 Jan Dankert Integration eines weiteren Code-Editors: Codemirror. Demnächst müssen wir hier mal aufräumen und andere Editoren rauswerfen.