1// CodeMirror, copyright (c) by Marijn Haverbeke and others 2// Distributed under an MIT license: http://codemirror.net/LICENSE 3 4// TODO actually recognize syntax of TypeScript constructs 5 6(function(mod) { 7 if (typeof exports == "object" && typeof module == "object") // CommonJS 8 mod(require("../../lib/codemirror")); 9 else if (typeof define == "function" && define.amd) // AMD 10 define(["../../lib/codemirror"], mod); 11 else // Plain browser env 12 mod(CodeMirror); 13})(function(CodeMirror) { 14"use strict"; 15 16CodeMirror.defineMode("javascript", function(config, parserConfig) { 17 var indentUnit = config.indentUnit; 18 var statementIndent = parserConfig.statementIndent; 19 var jsonldMode = parserConfig.jsonld; 20 var jsonMode = parserConfig.json || jsonldMode; 21 var isTS = parserConfig.typescript; 22 23 // Tokenizer 24 25 var keywords = function(){ 26 function kw(type) {return {type: type, style: "keyword"};} 27 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); 28 var operator = kw("operator"), atom = {type: "atom", style: "atom"}; 29 30 var jsKeywords = { 31 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, 32 "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C, 33 "var": kw("var"), "const": kw("var"), "let": kw("var"), 34 "function": kw("function"), "catch": kw("catch"), 35 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), 36 "in": operator, "typeof": operator, "instanceof": operator, 37 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, 38 "this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"), 39 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C 40 }; 41 42 // Extend the 'normal' keywords with the TypeScript language extensions 43 if (isTS) { 44 var type = {type: "variable", style: "variable-3"}; 45 var tsKeywords = { 46 // object-like things 47 "interface": kw("interface"), 48 "extends": kw("extends"), 49 "constructor": kw("constructor"), 50 51 // scope modifiers 52 "public": kw("public"), 53 "private": kw("private"), 54 "protected": kw("protected"), 55 "static": kw("static"), 56 57 // types 58 "string": type, "number": type, "bool": type, "any": type 59 }; 60 61 for (var attr in tsKeywords) { 62 jsKeywords[attr] = tsKeywords[attr]; 63 } 64 } 65 66 return jsKeywords; 67 }(); 68 69 var isOperatorChar = /[+\-*&%=<>!?|~^]/; 70 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; 71 72 function readRegexp(stream) { 73 var escaped = false, next, inSet = false; 74 while ((next = stream.next()) != null) { 75 if (!escaped) { 76 if (next == "/" && !inSet) return; 77 if (next == "[") inSet = true; 78 else if (inSet && next == "]") inSet = false; 79 } 80 escaped = !escaped && next == "\\"; 81 } 82 } 83 84 // Used as scratch variables to communicate multiple values without 85 // consing up tons of objects. 86 var type, content; 87 function ret(tp, style, cont) { 88 type = tp; content = cont; 89 return style; 90 } 91 function tokenBase(stream, state) { 92 var ch = stream.next(); 93 if (ch == '"' || ch == "'") { 94 state.tokenize = tokenString(ch); 95 return state.tokenize(stream, state); 96 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { 97 return ret("number", "number"); 98 } else if (ch == "." && stream.match("..")) { 99 return ret("spread", "meta"); 100 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { 101 return ret(ch); 102 } else if (ch == "=" && stream.eat(">")) { 103 return ret("=>", "operator"); 104 } else if (ch == "0" && stream.eat(/x/i)) { 105 stream.eatWhile(/[\da-f]/i); 106 return ret("number", "number"); 107 } else if (/\d/.test(ch)) { 108 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); 109 return ret("number", "number"); 110 } else if (ch == "/") { 111 if (stream.eat("*")) { 112 state.tokenize = tokenComment; 113 return tokenComment(stream, state); 114 } else if (stream.eat("/")) { 115 stream.skipToEnd(); 116 return ret("comment", "comment"); 117 } else if (state.lastType == "operator" || state.lastType == "keyword c" || 118 state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) { 119 readRegexp(stream); 120 stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla 121 return ret("regexp", "string-2"); 122 } else { 123 stream.eatWhile(isOperatorChar); 124 return ret("operator", "operator", stream.current()); 125 } 126 } else if (ch == "`") { 127 state.tokenize = tokenQuasi; 128 return tokenQuasi(stream, state); 129 } else if (ch == "#") { 130 stream.skipToEnd(); 131 return ret("error", "error"); 132 } else if (isOperatorChar.test(ch)) { 133 stream.eatWhile(isOperatorChar); 134 return ret("operator", "operator", stream.current()); 135 } else { 136 stream.eatWhile(/[\w\$_]/); 137 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; 138 return (known && state.lastType != ".") ? ret(known.type, known.style, word) : 139 ret("variable", "variable", word); 140 } 141 } 142 143 function tokenString(quote) { 144 return function(stream, state) { 145 var escaped = false, next; 146 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ 147 state.tokenize = tokenBase; 148 return ret("jsonld-keyword", "meta"); 149 } 150 while ((next = stream.next()) != null) { 151 if (next == quote && !escaped) break; 152 escaped = !escaped && next == "\\"; 153 } 154 if (!escaped) state.tokenize = tokenBase; 155 return ret("string", "string"); 156 }; 157 } 158 159 function tokenComment(stream, state) { 160 var maybeEnd = false, ch; 161 while (ch = stream.next()) { 162 if (ch == "/" && maybeEnd) { 163 state.tokenize = tokenBase; 164 break; 165 } 166 maybeEnd = (ch == "*"); 167 } 168 return ret("comment", "comment"); 169 } 170 171 function tokenQuasi(stream, state) { 172 var escaped = false, next; 173 while ((next = stream.next()) != null) { 174 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { 175 state.tokenize = tokenBase; 176 break; 177 } 178 escaped = !escaped && next == "\\"; 179 } 180 return ret("quasi", "string-2", stream.current()); 181 } 182 183 var brackets = "([{}])"; 184 // This is a crude lookahead trick to try and notice that we're 185 // parsing the argument patterns for a fat-arrow function before we 186 // actually hit the arrow token. It only works if the arrow is on 187 // the same line as the arguments and there's no strange noise 188 // (comments) in between. Fallback is to only notice when we hit the 189 // arrow, and not declare the arguments as locals for the arrow 190 // body. 191 function findFatArrow(stream, state) { 192 if (state.fatArrowAt) state.fatArrowAt = null; 193 var arrow = stream.string.indexOf("=>", stream.start); 194 if (arrow < 0) return; 195 196 var depth = 0, sawSomething = false; 197 for (var pos = arrow - 1; pos >= 0; --pos) { 198 var ch = stream.string.charAt(pos); 199 var bracket = brackets.indexOf(ch); 200 if (bracket >= 0 && bracket < 3) { 201 if (!depth) { ++pos; break; } 202 if (--depth == 0) break; 203 } else if (bracket >= 3 && bracket < 6) { 204 ++depth; 205 } else if (/[$\w]/.test(ch)) { 206 sawSomething = true; 207 } else if (sawSomething && !depth) { 208 ++pos; 209 break; 210 } 211 } 212 if (sawSomething && !depth) state.fatArrowAt = pos; 213 } 214 215 // Parser 216 217 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; 218 219 function JSLexical(indented, column, type, align, prev, info) { 220 this.indented = indented; 221 this.column = column; 222 this.type = type; 223 this.prev = prev; 224 this.info = info; 225 if (align != null) this.align = align; 226 } 227 228 function inScope(state, varname) { 229 for (var v = state.localVars; v; v = v.next) 230 if (v.name == varname) return true; 231 for (var cx = state.context; cx; cx = cx.prev) { 232 for (var v = cx.vars; v; v = v.next) 233 if (v.name == varname) return true; 234 } 235 } 236 237 function parseJS(state, style, type, content, stream) { 238 var cc = state.cc; 239 // Communicate our context to the combinators. 240 // (Less wasteful than consing up a hundred closures on every call.) 241 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; 242 243 if (!state.lexical.hasOwnProperty("align")) 244 state.lexical.align = true; 245 246 while(true) { 247 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; 248 if (combinator(type, content)) { 249 while(cc.length && cc[cc.length - 1].lex) 250 cc.pop()(); 251 if (cx.marked) return cx.marked; 252 if (type == "variable" && inScope(state, content)) return "variable-2"; 253 return style; 254 } 255 } 256 } 257 258 // Combinator utils 259 260 var cx = {state: null, column: null, marked: null, cc: null}; 261 function pass() { 262 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); 263 } 264 function cont() { 265 pass.apply(null, arguments); 266 return true; 267 } 268 function register(varname) { 269 function inList(list) { 270 for (var v = list; v; v = v.next) 271 if (v.name == varname) return true; 272 return false; 273 } 274 var state = cx.state; 275 if (state.context) { 276 cx.marked = "def"; 277 if (inList(state.localVars)) return; 278 state.localVars = {name: varname, next: state.localVars}; 279 } else { 280 if (inList(state.globalVars)) return; 281 if (parserConfig.globalVars) 282 state.globalVars = {name: varname, next: state.globalVars}; 283 } 284 } 285 286 // Combinators 287 288 var defaultVars = {name: "this", next: {name: "arguments"}}; 289 function pushcontext() { 290 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; 291 cx.state.localVars = defaultVars; 292 } 293 function popcontext() { 294 cx.state.localVars = cx.state.context.vars; 295 cx.state.context = cx.state.context.prev; 296 } 297 function pushlex(type, info) { 298 var result = function() { 299 var state = cx.state, indent = state.indented; 300 if (state.lexical.type == "stat") indent = state.lexical.indented; 301 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); 302 }; 303 result.lex = true; 304 return result; 305 } 306 function poplex() { 307 var state = cx.state; 308 if (state.lexical.prev) { 309 if (state.lexical.type == ")") 310 state.indented = state.lexical.indented; 311 state.lexical = state.lexical.prev; 312 } 313 } 314 poplex.lex = true; 315 316 function expect(wanted) { 317 function exp(type) { 318 if (type == wanted) return cont(); 319 else if (wanted == ";") return pass(); 320 else return cont(exp); 321 }; 322 return exp; 323 } 324 325 function statement(type, value) { 326 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); 327 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); 328 if (type == "keyword b") return cont(pushlex("form"), statement, poplex); 329 if (type == "{") return cont(pushlex("}"), block, poplex); 330 if (type == ";") return cont(); 331 if (type == "if") { 332 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) 333 cx.state.cc.pop()(); 334 return cont(pushlex("form"), expression, statement, poplex, maybeelse); 335 } 336 if (type == "function") return cont(functiondef); 337 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); 338 if (type == "variable") return cont(pushlex("stat"), maybelabel); 339 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), 340 block, poplex, poplex); 341 if (type == "case") return cont(expression, expect(":")); 342 if (type == "default") return cont(expect(":")); 343 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), 344 statement, poplex, popcontext); 345 if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex); 346 if (type == "class") return cont(pushlex("form"), className, poplex); 347 if (type == "export") return cont(pushlex("form"), afterExport, poplex); 348 if (type == "import") return cont(pushlex("form"), afterImport, poplex); 349 return pass(pushlex("stat"), expression, expect(";"), poplex); 350 } 351 function expression(type) { 352 return expressionInner(type, false); 353 } 354 function expressionNoComma(type) { 355 return expressionInner(type, true); 356 } 357 function expressionInner(type, noComma) { 358 if (cx.state.fatArrowAt == cx.stream.start) { 359 var body = noComma ? arrowBodyNoComma : arrowBody; 360 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); 361 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); 362 } 363 364 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; 365 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); 366 if (type == "function") return cont(functiondef, maybeop); 367 if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression); 368 if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop); 369 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); 370 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); 371 if (type == "{") return contCommasep(objprop, "}", null, maybeop); 372 if (type == "quasi") { return pass(quasi, maybeop); } 373 return cont(); 374 } 375 function maybeexpression(type) { 376 if (type.match(/[;\}\)\],]/)) return pass(); 377 return pass(expression); 378 } 379 function maybeexpressionNoComma(type) { 380 if (type.match(/[;\}\)\],]/)) return pass(); 381 return pass(expressionNoComma); 382 } 383 384 function maybeoperatorComma(type, value) { 385 if (type == ",") return cont(expression); 386 return maybeoperatorNoComma(type, value, false); 387 } 388 function maybeoperatorNoComma(type, value, noComma) { 389 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; 390 var expr = noComma == false ? expression : expressionNoComma; 391 if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); 392 if (type == "operator") { 393 if (/\+\+|--/.test(value)) return cont(me); 394 if (value == "?") return cont(expression, expect(":"), expr); 395 return cont(expr); 396 } 397 if (type == "quasi") { return pass(quasi, me); } 398 if (type == ";") return; 399 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); 400 if (type == ".") return cont(property, me); 401 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); 402 } 403 function quasi(type, value) { 404 if (type != "quasi") return pass(); 405 if (value.slice(value.length - 2) != "${") return cont(quasi); 406 return cont(expression, continueQuasi); 407 } 408 function continueQuasi(type) { 409 if (type == "}") { 410 cx.marked = "string-2"; 411 cx.state.tokenize = tokenQuasi; 412 return cont(quasi); 413 } 414 } 415 function arrowBody(type) { 416 findFatArrow(cx.stream, cx.state); 417 if (type == "{") return pass(statement); 418 return pass(expression); 419 } 420 function arrowBodyNoComma(type) { 421 findFatArrow(cx.stream, cx.state); 422 if (type == "{") return pass(statement); 423 return pass(expressionNoComma); 424 } 425 function maybelabel(type) { 426 if (type == ":") return cont(poplex, statement); 427 return pass(maybeoperatorComma, expect(";"), poplex); 428 } 429 function property(type) { 430 if (type == "variable") {cx.marked = "property"; return cont();} 431 } 432 function objprop(type, value) { 433 if (type == "variable" || cx.style == "keyword") { 434 cx.marked = "property"; 435 if (value == "get" || value == "set") return cont(getterSetter); 436 return cont(afterprop); 437 } else if (type == "number" || type == "string") { 438 cx.marked = jsonldMode ? "property" : (cx.style + " property"); 439 return cont(afterprop); 440 } else if (type == "jsonld-keyword") { 441 return cont(afterprop); 442 } else if (type == "[") { 443 return cont(expression, expect("]"), afterprop); 444 } 445 } 446 function getterSetter(type) { 447 if (type != "variable") return pass(afterprop); 448 cx.marked = "property"; 449 return cont(functiondef); 450 } 451 function afterprop(type) { 452 if (type == ":") return cont(expressionNoComma); 453 if (type == "(") return pass(functiondef); 454 } 455 function commasep(what, end) { 456 function proceed(type) { 457 if (type == ",") { 458 var lex = cx.state.lexical; 459 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; 460 return cont(what, proceed); 461 } 462 if (type == end) return cont(); 463 return cont(expect(end)); 464 } 465 return function(type) { 466 if (type == end) return cont(); 467 return pass(what, proceed); 468 }; 469 } 470 function contCommasep(what, end, info) { 471 for (var i = 3; i < arguments.length; i++) 472 cx.cc.push(arguments[i]); 473 return cont(pushlex(end, info), commasep(what, end), poplex); 474 } 475 function block(type) { 476 if (type == "}") return cont(); 477 return pass(statement, block); 478 } 479 function maybetype(type) { 480 if (isTS && type == ":") return cont(typedef); 481 } 482 function typedef(type) { 483 if (type == "variable"){cx.marked = "variable-3"; return cont();} 484 } 485 function vardef() { 486 return pass(pattern, maybetype, maybeAssign, vardefCont); 487 } 488 function pattern(type, value) { 489 if (type == "variable") { register(value); return cont(); } 490 if (type == "[") return contCommasep(pattern, "]"); 491 if (type == "{") return contCommasep(proppattern, "}"); 492 } 493 function proppattern(type, value) { 494 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { 495 register(value); 496 return cont(maybeAssign); 497 } 498 if (type == "variable") cx.marked = "property"; 499 return cont(expect(":"), pattern, maybeAssign); 500 } 501 function maybeAssign(_type, value) { 502 if (value == "=") return cont(expressionNoComma); 503 } 504 function vardefCont(type) { 505 if (type == ",") return cont(vardef); 506 } 507 function maybeelse(type, value) { 508 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); 509 } 510 function forspec(type) { 511 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); 512 } 513 function forspec1(type) { 514 if (type == "var") return cont(vardef, expect(";"), forspec2); 515 if (type == ";") return cont(forspec2); 516 if (type == "variable") return cont(formaybeinof); 517 return pass(expression, expect(";"), forspec2); 518 } 519 function formaybeinof(_type, value) { 520 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } 521 return cont(maybeoperatorComma, forspec2); 522 } 523 function forspec2(type, value) { 524 if (type == ";") return cont(forspec3); 525 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } 526 return pass(expression, expect(";"), forspec3); 527 } 528 function forspec3(type) { 529 if (type != ")") cont(expression); 530 } 531 function functiondef(type, value) { 532 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} 533 if (type == "variable") {register(value); return cont(functiondef);} 534 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext); 535 } 536 function funarg(type) { 537 if (type == "spread") return cont(funarg); 538 return pass(pattern, maybetype); 539 } 540 function className(type, value) { 541 if (type == "variable") {register(value); return cont(classNameAfter);} 542 } 543 function classNameAfter(type, value) { 544 if (value == "extends") return cont(expression, classNameAfter); 545 if (type == "{") return cont(pushlex("}"), classBody, poplex); 546 } 547 function classBody(type, value) { 548 if (type == "variable" || cx.style == "keyword") { 549 cx.marked = "property"; 550 if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody); 551 return cont(functiondef, classBody); 552 } 553 if (value == "*") { 554 cx.marked = "keyword"; 555 return cont(classBody); 556 } 557 if (type == ";") return cont(classBody); 558 if (type == "}") return cont(); 559 } 560 function classGetterSetter(type) { 561 if (type != "variable") return pass(); 562 cx.marked = "property"; 563 return cont(); 564 } 565 function afterModule(type, value) { 566 if (type == "string") return cont(statement); 567 if (type == "variable") { register(value); return cont(maybeFrom); } 568 } 569 function afterExport(_type, value) { 570 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } 571 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } 572 return pass(statement); 573 } 574 function afterImport(type) { 575 if (type == "string") return cont(); 576 return pass(importSpec, maybeFrom); 577 } 578 function importSpec(type, value) { 579 if (type == "{") return contCommasep(importSpec, "}"); 580 if (type == "variable") register(value); 581 return cont(); 582 } 583 function maybeFrom(_type, value) { 584 if (value == "from") { cx.marked = "keyword"; return cont(expression); } 585 } 586 function arrayLiteral(type) { 587 if (type == "]") return cont(); 588 return pass(expressionNoComma, maybeArrayComprehension); 589 } 590 function maybeArrayComprehension(type) { 591 if (type == "for") return pass(comprehension, expect("]")); 592 if (type == ",") return cont(commasep(expressionNoComma, "]")); 593 return pass(commasep(expressionNoComma, "]")); 594 } 595 function comprehension(type) { 596 if (type == "for") return cont(forspec, comprehension); 597 if (type == "if") return cont(expression, comprehension); 598 } 599 600 // Interface 601 602 return { 603 startState: function(basecolumn) { 604 var state = { 605 tokenize: tokenBase, 606 lastType: "sof", 607 cc: [], 608 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), 609 localVars: parserConfig.localVars, 610 context: parserConfig.localVars && {vars: parserConfig.localVars}, 611 indented: 0 612 }; 613 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") 614 state.globalVars = parserConfig.globalVars; 615 return state; 616 }, 617 618 token: function(stream, state) { 619 if (stream.sol()) { 620 if (!state.lexical.hasOwnProperty("align")) 621 state.lexical.align = false; 622 state.indented = stream.indentation(); 623 findFatArrow(stream, state); 624 } 625 if (state.tokenize != tokenComment && stream.eatSpace()) return null; 626 var style = state.tokenize(stream, state); 627 if (type == "comment") return style; 628 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; 629 return parseJS(state, style, type, content, stream); 630 }, 631 632 indent: function(state, textAfter) { 633 if (state.tokenize == tokenComment) return CodeMirror.Pass; 634 if (state.tokenize != tokenBase) return 0; 635 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; 636 // Kludge to prevent 'maybelse' from blocking lexical scope pops 637 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { 638 var c = state.cc[i]; 639 if (c == poplex) lexical = lexical.prev; 640 else if (c != maybeelse) break; 641 } 642 if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; 643 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") 644 lexical = lexical.prev; 645 var type = lexical.type, closing = firstChar == type; 646 647 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); 648 else if (type == "form" && firstChar == "{") return lexical.indented; 649 else if (type == "form") return lexical.indented + indentUnit; 650 else if (type == "stat") 651 return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0); 652 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) 653 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); 654 else if (lexical.align) return lexical.column + (closing ? 0 : 1); 655 else return lexical.indented + (closing ? 0 : indentUnit); 656 }, 657 658 electricChars: ":{}", 659 blockCommentStart: jsonMode ? null : "/*", 660 blockCommentEnd: jsonMode ? null : "*/", 661 lineComment: jsonMode ? null : "//", 662 fold: "brace", 663 664 helperType: jsonMode ? "json" : "javascript", 665 jsonldMode: jsonldMode, 666 jsonMode: jsonMode 667 }; 668}); 669 670CodeMirror.registerHelper("wordChars", "javascript", /[\\w$]/); 671 672CodeMirror.defineMIME("text/javascript", "javascript"); 673CodeMirror.defineMIME("text/ecmascript", "javascript"); 674CodeMirror.defineMIME("application/javascript", "javascript"); 675CodeMirror.defineMIME("application/x-javascript", "javascript"); 676CodeMirror.defineMIME("application/ecmascript", "javascript"); 677CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); 678CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); 679CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); 680CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); 681CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); 682 683}); 684