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