1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
4 // TODO actually recognize syntax of TypeScript constructs
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
13 })(function(CodeMirror
) {
16 CodeMirror
.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 var wordRE
= parserConfig
.wordCharacters
|| /[\w$\xa1-\uffff]/;
26 var keywords = function(){
27 function kw(type
) {return {type
: type
, style
: "keyword"};}
28 var A
= kw("keyword a"), B
= kw("keyword b"), C
= kw("keyword c");
29 var operator
= kw("operator"), atom
= {type
: "atom", style
: "atom"};
32 "if": kw("if"), "while": A
, "with": A
, "else": B
, "do": B
, "try": B
, "finally": B
,
33 "return": C
, "break": C
, "continue": C
, "new": C
, "delete": C
, "throw": C
, "debugger": C
,
34 "var": kw("var"), "const": kw("var"), "let": kw("var"),
35 "function": kw("function"), "catch": kw("catch"),
36 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
37 "in": operator
, "typeof": operator
, "instanceof": operator
,
38 "true": atom
, "false": atom
, "null": atom
, "undefined": atom
, "NaN": atom
, "Infinity": atom
,
39 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
40 "yield": C
, "export": kw("export"), "import": kw("import"), "extends": C
43 // Extend the 'normal' keywords with the TypeScript language extensions
45 var type
= {type
: "variable", style
: "variable-3"};
48 "interface": kw("interface"),
49 "extends": kw("extends"),
50 "constructor": kw("constructor"),
53 "public": kw("public"),
54 "private": kw("private"),
55 "protected": kw("protected"),
56 "static": kw("static"),
59 "string": type
, "number": type
, "bool": type
, "any": type
62 for (var attr
in tsKeywords
) {
63 jsKeywords
[attr
] = tsKeywords
[attr
];
70 var isOperatorChar
= /[+\-*&%=<>!?|~^]/;
71 var isJsonldKeyword
= /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
73 function readRegexp(stream
) {
74 var escaped
= false, next
, inSet
= false;
75 while ((next
= stream
.next()) != null) {
77 if (next
== "/" && !inSet
) return;
78 if (next
== "[") inSet
= true;
79 else if (inSet
&& next
== "]") inSet
= false;
81 escaped
= !escaped
&& next
== "\\";
85 // Used as scratch variables to communicate multiple values without
86 // consing up tons of objects.
88 function ret(tp
, style
, cont
) {
89 type
= tp
; content
= cont
;
92 function tokenBase(stream
, state
) {
93 var ch
= stream
.next();
94 if (ch
== '"' || ch
== "'") {
95 state
.tokenize
= tokenString(ch
);
96 return state
.tokenize(stream
, state
);
97 } else if (ch
== "." && stream
.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
98 return ret("number", "number");
99 } else if (ch
== "." && stream
.match("..")) {
100 return ret("spread", "meta");
101 } else if (/[\[\]{}\(\),;\:\.]/.test(ch
)) {
103 } else if (ch
== "=" && stream
.eat(">")) {
104 return ret("=>", "operator");
105 } else if (ch
== "0" && stream
.eat(/x
/i
)) {
106 stream
.eatWhile(/[\da-f]/i);
107 return ret("number", "number");
108 } else if (/\d/.test(ch
)) {
109 stream
.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
110 return ret("number", "number");
111 } else if (ch
== "/") {
112 if (stream
.eat("*")) {
113 state
.tokenize
= tokenComment
;
114 return tokenComment(stream
, state
);
115 } else if (stream
.eat("/")) {
117 return ret("comment", "comment");
118 } else if (state
.lastType
== "operator" || state
.lastType
== "keyword c" ||
119 state
.lastType
== "sof" || /^[\[{}\(,;:]$/.test(state
.lastType
)) {
121 stream
.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
122 return ret("regexp", "string-2");
124 stream
.eatWhile(isOperatorChar
);
125 return ret("operator", "operator", stream
.current());
127 } else if (ch
== "`") {
128 state
.tokenize
= tokenQuasi
;
129 return tokenQuasi(stream
, state
);
130 } else if (ch
== "#") {
132 return ret("error", "error");
133 } else if (isOperatorChar
.test(ch
)) {
134 stream
.eatWhile(isOperatorChar
);
135 return ret("operator", "operator", stream
.current());
136 } else if (wordRE
.test(ch
)) {
137 stream
.eatWhile(wordRE
);
138 var word
= stream
.current(), known
= keywords
.propertyIsEnumerable(word
) && keywords
[word
];
139 return (known
&& state
.lastType
!= ".") ? ret(known
.type
, known
.style
, word
) :
140 ret("variable", "variable", word
);
144 function tokenString(quote
) {
145 return function(stream
, state
) {
146 var escaped
= false, next
;
147 if (jsonldMode
&& stream
.peek() == "@" && stream
.match(isJsonldKeyword
)){
148 state
.tokenize
= tokenBase
;
149 return ret("jsonld-keyword", "meta");
151 while ((next
= stream
.next()) != null) {
152 if (next
== quote
&& !escaped
) break;
153 escaped
= !escaped
&& next
== "\\";
155 if (!escaped
) state
.tokenize
= tokenBase
;
156 return ret("string", "string");
160 function tokenComment(stream
, state
) {
161 var maybeEnd
= false, ch
;
162 while (ch
= stream
.next()) {
163 if (ch
== "/" && maybeEnd
) {
164 state
.tokenize
= tokenBase
;
167 maybeEnd
= (ch
== "*");
169 return ret("comment", "comment");
172 function tokenQuasi(stream
, state
) {
173 var escaped
= false, next
;
174 while ((next
= stream
.next()) != null) {
175 if (!escaped
&& (next
== "`" || next
== "$" && stream
.eat("{"))) {
176 state
.tokenize
= tokenBase
;
179 escaped
= !escaped
&& next
== "\\";
181 return ret("quasi", "string-2", stream
.current());
184 var brackets
= "([{}])";
185 // This is a crude lookahead trick to try and notice that we're
186 // parsing the argument patterns for a fat-arrow function before we
187 // actually hit the arrow token. It only works if the arrow is on
188 // the same line as the arguments and there's no strange noise
189 // (comments) in between. Fallback is to only notice when we hit the
190 // arrow, and not declare the arguments as locals for the arrow
192 function findFatArrow(stream
, state
) {
193 if (state
.fatArrowAt
) state
.fatArrowAt
= null;
194 var arrow
= stream
.string
.indexOf("=>", stream
.start
);
195 if (arrow
< 0) return;
197 var depth
= 0, sawSomething
= false;
198 for (var pos
= arrow
- 1; pos
>= 0; --pos
) {
199 var ch
= stream
.string
.charAt(pos
);
200 var bracket
= brackets
.indexOf(ch
);
201 if (bracket
>= 0 && bracket
< 3) {
202 if (!depth
) { ++pos
; break; }
203 if (--depth
== 0) break;
204 } else if (bracket
>= 3 && bracket
< 6) {
206 } else if (wordRE
.test(ch
)) {
208 } else if (/["'\/]/.test(ch
)) {
210 } else if (sawSomething
&& !depth
) {
215 if (sawSomething
&& !depth
) state
.fatArrowAt
= pos
;
220 var atomicTypes
= {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
222 function JSLexical(indented
, column
, type
, align
, prev
, info
) {
223 this.indented
= indented
;
224 this.column
= column
;
228 if (align
!= null) this.align
= align
;
231 function inScope(state
, varname
) {
232 for (var v
= state
.localVars
; v
; v
= v
.next
)
233 if (v
.name
== varname
) return true;
234 for (var cx
= state
.context
; cx
; cx
= cx
.prev
) {
235 for (var v
= cx
.vars
; v
; v
= v
.next
)
236 if (v
.name
== varname
) return true;
240 function parseJS(state
, style
, type
, content
, stream
) {
242 // Communicate our context to the combinators.
243 // (Less wasteful than consing up a hundred closures on every call.)
244 cx
.state
= state
; cx
.stream
= stream
; cx
.marked
= null, cx
.cc
= cc
; cx
.style
= style
;
246 if (!state
.lexical
.hasOwnProperty("align"))
247 state
.lexical
.align
= true;
250 var combinator
= cc
.length
? cc
.pop() : jsonMode
? expression
: statement
;
251 if (combinator(type
, content
)) {
252 while(cc
.length
&& cc
[cc
.length
- 1].lex
)
254 if (cx
.marked
) return cx
.marked
;
255 if (type
== "variable" && inScope(state
, content
)) return "variable-2";
263 var cx
= {state
: null, column
: null, marked
: null, cc
: null};
265 for (var i
= arguments
.length
- 1; i
>= 0; i
--) cx
.cc
.push(arguments
[i
]);
268 pass
.apply(null, arguments
);
271 function register(varname
) {
272 function inList(list
) {
273 for (var v
= list
; v
; v
= v
.next
)
274 if (v
.name
== varname
) return true;
277 var state
= cx
.state
;
280 if (inList(state
.localVars
)) return;
281 state
.localVars
= {name
: varname
, next
: state
.localVars
};
283 if (inList(state
.globalVars
)) return;
284 if (parserConfig
.globalVars
)
285 state
.globalVars
= {name
: varname
, next
: state
.globalVars
};
291 var defaultVars
= {name
: "this", next
: {name
: "arguments"}};
292 function pushcontext() {
293 cx
.state
.context
= {prev
: cx
.state
.context
, vars
: cx
.state
.localVars
};
294 cx
.state
.localVars
= defaultVars
;
296 function popcontext() {
297 cx
.state
.localVars
= cx
.state
.context
.vars
;
298 cx
.state
.context
= cx
.state
.context
.prev
;
300 function pushlex(type
, info
) {
301 var result = function() {
302 var state
= cx
.state
, indent
= state
.indented
;
303 if (state
.lexical
.type
== "stat") indent
= state
.lexical
.indented
;
304 else for (var outer
= state
.lexical
; outer
&& outer
.type
== ")" && outer
.align
; outer
= outer
.prev
)
305 indent
= outer
.indented
;
306 state
.lexical
= new JSLexical(indent
, cx
.stream
.column(), type
, null, state
.lexical
, info
);
312 var state
= cx
.state
;
313 if (state
.lexical
.prev
) {
314 if (state
.lexical
.type
== ")")
315 state
.indented
= state
.lexical
.indented
;
316 state
.lexical
= state
.lexical
.prev
;
321 function expect(wanted
) {
323 if (type
== wanted
) return cont();
324 else if (wanted
== ";") return pass();
325 else return cont(exp
);
330 function statement(type
, value
) {
331 if (type
== "var") return cont(pushlex("vardef", value
.length
), vardef
, expect(";"), poplex
);
332 if (type
== "keyword a") return cont(pushlex("form"), expression
, statement
, poplex
);
333 if (type
== "keyword b") return cont(pushlex("form"), statement
, poplex
);
334 if (type
== "{") return cont(pushlex("}"), block
, poplex
);
335 if (type
== ";") return cont();
337 if (cx
.state
.lexical
.info
== "else" && cx
.state
.cc
[cx
.state
.cc
.length
- 1] == poplex
)
339 return cont(pushlex("form"), expression
, statement
, poplex
, maybeelse
);
341 if (type
== "function") return cont(functiondef
);
342 if (type
== "for") return cont(pushlex("form"), forspec
, statement
, poplex
);
343 if (type
== "variable") return cont(pushlex("stat"), maybelabel
);
344 if (type
== "switch") return cont(pushlex("form"), expression
, pushlex("}", "switch"), expect("{"),
345 block
, poplex
, poplex
);
346 if (type
== "case") return cont(expression
, expect(":"));
347 if (type
== "default") return cont(expect(":"));
348 if (type
== "catch") return cont(pushlex("form"), pushcontext
, expect("("), funarg
, expect(")"),
349 statement
, poplex
, popcontext
);
350 if (type
== "class") return cont(pushlex("form"), className
, poplex
);
351 if (type
== "export") return cont(pushlex("form"), afterExport
, poplex
);
352 if (type
== "import") return cont(pushlex("form"), afterImport
, poplex
);
353 return pass(pushlex("stat"), expression
, expect(";"), poplex
);
355 function expression(type
) {
356 return expressionInner(type
, false);
358 function expressionNoComma(type
) {
359 return expressionInner(type
, true);
361 function expressionInner(type
, noComma
) {
362 if (cx
.state
.fatArrowAt
== cx
.stream
.start
) {
363 var body
= noComma
? arrowBodyNoComma
: arrowBody
;
364 if (type
== "(") return cont(pushcontext
, pushlex(")"), commasep(pattern
, ")"), poplex
, expect("=>"), body
, popcontext
);
365 else if (type
== "variable") return pass(pushcontext
, pattern
, expect("=>"), body
, popcontext
);
368 var maybeop
= noComma
? maybeoperatorNoComma
: maybeoperatorComma
;
369 if (atomicTypes
.hasOwnProperty(type
)) return cont(maybeop
);
370 if (type
== "function") return cont(functiondef
, maybeop
);
371 if (type
== "keyword c") return cont(noComma
? maybeexpressionNoComma
: maybeexpression
);
372 if (type
== "(") return cont(pushlex(")"), maybeexpression
, comprehension
, expect(")"), poplex
, maybeop
);
373 if (type
== "operator" || type
== "spread") return cont(noComma
? expressionNoComma
: expression
);
374 if (type
== "[") return cont(pushlex("]"), arrayLiteral
, poplex
, maybeop
);
375 if (type
== "{") return contCommasep(objprop
, "}", null, maybeop
);
376 if (type
== "quasi") { return pass(quasi
, maybeop
); }
379 function maybeexpression(type
) {
380 if (type
.match(/[;\}\)\],]/)) return pass();
381 return pass(expression
);
383 function maybeexpressionNoComma(type
) {
384 if (type
.match(/[;\}\)\],]/)) return pass();
385 return pass(expressionNoComma
);
388 function maybeoperatorComma(type
, value
) {
389 if (type
== ",") return cont(expression
);
390 return maybeoperatorNoComma(type
, value
, false);
392 function maybeoperatorNoComma(type
, value
, noComma
) {
393 var me
= noComma
== false ? maybeoperatorComma
: maybeoperatorNoComma
;
394 var expr
= noComma
== false ? expression
: expressionNoComma
;
395 if (type
== "=>") return cont(pushcontext
, noComma
? arrowBodyNoComma
: arrowBody
, popcontext
);
396 if (type
== "operator") {
397 if (/\+\+|--/.test(value
)) return cont(me
);
398 if (value
== "?") return cont(expression
, expect(":"), expr
);
401 if (type
== "quasi") { return pass(quasi
, me
); }
402 if (type
== ";") return;
403 if (type
== "(") return contCommasep(expressionNoComma
, ")", "call", me
);
404 if (type
== ".") return cont(property
, me
);
405 if (type
== "[") return cont(pushlex("]"), maybeexpression
, expect("]"), poplex
, me
);
407 function quasi(type
, value
) {
408 if (type
!= "quasi") return pass();
409 if (value
.slice(value
.length
- 2) != "${") return cont(quasi
);
410 return cont(expression
, continueQuasi
);
412 function continueQuasi(type
) {
414 cx
.marked
= "string-2";
415 cx
.state
.tokenize
= tokenQuasi
;
419 function arrowBody(type
) {
420 findFatArrow(cx
.stream
, cx
.state
);
421 return pass(type
== "{" ? statement
: expression
);
423 function arrowBodyNoComma(type
) {
424 findFatArrow(cx
.stream
, cx
.state
);
425 return pass(type
== "{" ? statement
: expressionNoComma
);
427 function maybelabel(type
) {
428 if (type
== ":") return cont(poplex
, statement
);
429 return pass(maybeoperatorComma
, expect(";"), poplex
);
431 function property(type
) {
432 if (type
== "variable") {cx
.marked
= "property"; return cont();}
434 function objprop(type
, value
) {
435 if (type
== "variable" || cx
.style
== "keyword") {
436 cx
.marked
= "property";
437 if (value
== "get" || value
== "set") return cont(getterSetter
);
438 return cont(afterprop
);
439 } else if (type
== "number" || type
== "string") {
440 cx
.marked
= jsonldMode
? "property" : (cx
.style
+ " property");
441 return cont(afterprop
);
442 } else if (type
== "jsonld-keyword") {
443 return cont(afterprop
);
444 } else if (type
== "[") {
445 return cont(expression
, expect("]"), afterprop
);
448 function getterSetter(type
) {
449 if (type
!= "variable") return pass(afterprop
);
450 cx
.marked
= "property";
451 return cont(functiondef
);
453 function afterprop(type
) {
454 if (type
== ":") return cont(expressionNoComma
);
455 if (type
== "(") return pass(functiondef
);
457 function commasep(what
, end
) {
458 function proceed(type
) {
460 var lex
= cx
.state
.lexical
;
461 if (lex
.info
== "call") lex
.pos
= (lex
.pos
|| 0) + 1;
462 return cont(what
, proceed
);
464 if (type
== end
) return cont();
465 return cont(expect(end
));
467 return function(type
) {
468 if (type
== end
) return cont();
469 return pass(what
, proceed
);
472 function contCommasep(what
, end
, info
) {
473 for (var i
= 3; i
< arguments
.length
; i
++)
474 cx
.cc
.push(arguments
[i
]);
475 return cont(pushlex(end
, info
), commasep(what
, end
), poplex
);
477 function block(type
) {
478 if (type
== "}") return cont();
479 return pass(statement
, block
);
481 function maybetype(type
) {
482 if (isTS
&& type
== ":") return cont(typedef
);
484 function maybedefault(_
, value
) {
485 if (value
== "=") return cont(expressionNoComma
);
487 function typedef(type
) {
488 if (type
== "variable") {cx
.marked
= "variable-3"; return cont();}
491 return pass(pattern
, maybetype
, maybeAssign
, vardefCont
);
493 function pattern(type
, value
) {
494 if (type
== "variable") { register(value
); return cont(); }
495 if (type
== "[") return contCommasep(pattern
, "]");
496 if (type
== "{") return contCommasep(proppattern
, "}");
498 function proppattern(type
, value
) {
499 if (type
== "variable" && !cx
.stream
.match(/^\s*:/, false)) {
501 return cont(maybeAssign
);
503 if (type
== "variable") cx
.marked
= "property";
504 return cont(expect(":"), pattern
, maybeAssign
);
506 function maybeAssign(_type
, value
) {
507 if (value
== "=") return cont(expressionNoComma
);
509 function vardefCont(type
) {
510 if (type
== ",") return cont(vardef
);
512 function maybeelse(type
, value
) {
513 if (type
== "keyword b" && value
== "else") return cont(pushlex("form", "else"), statement
, poplex
);
515 function forspec(type
) {
516 if (type
== "(") return cont(pushlex(")"), forspec1
, expect(")"), poplex
);
518 function forspec1(type
) {
519 if (type
== "var") return cont(vardef
, expect(";"), forspec2
);
520 if (type
== ";") return cont(forspec2
);
521 if (type
== "variable") return cont(formaybeinof
);
522 return pass(expression
, expect(";"), forspec2
);
524 function formaybeinof(_type
, value
) {
525 if (value
== "in" || value
== "of") { cx
.marked
= "keyword"; return cont(expression
); }
526 return cont(maybeoperatorComma
, forspec2
);
528 function forspec2(type
, value
) {
529 if (type
== ";") return cont(forspec3
);
530 if (value
== "in" || value
== "of") { cx
.marked
= "keyword"; return cont(expression
); }
531 return pass(expression
, expect(";"), forspec3
);
533 function forspec3(type
) {
534 if (type
!= ")") cont(expression
);
536 function functiondef(type
, value
) {
537 if (value
== "*") {cx
.marked
= "keyword"; return cont(functiondef
);}
538 if (type
== "variable") {register(value
); return cont(functiondef
);}
539 if (type
== "(") return cont(pushcontext
, pushlex(")"), commasep(funarg
, ")"), poplex
, statement
, popcontext
);
541 function funarg(type
) {
542 if (type
== "spread") return cont(funarg
);
543 return pass(pattern
, maybetype
, maybedefault
);
545 function className(type
, value
) {
546 if (type
== "variable") {register(value
); return cont(classNameAfter
);}
548 function classNameAfter(type
, value
) {
549 if (value
== "extends") return cont(expression
, classNameAfter
);
550 if (type
== "{") return cont(pushlex("}"), classBody
, poplex
);
552 function classBody(type
, value
) {
553 if (type
== "variable" || cx
.style
== "keyword") {
554 if (value
== "static") {
555 cx
.marked
= "keyword";
556 return cont(classBody
);
558 cx
.marked
= "property";
559 if (value
== "get" || value
== "set") return cont(classGetterSetter
, functiondef
, classBody
);
560 return cont(functiondef
, classBody
);
563 cx
.marked
= "keyword";
564 return cont(classBody
);
566 if (type
== ";") return cont(classBody
);
567 if (type
== "}") return cont();
569 function classGetterSetter(type
) {
570 if (type
!= "variable") return pass();
571 cx
.marked
= "property";
574 function afterExport(_type
, value
) {
575 if (value
== "*") { cx
.marked
= "keyword"; return cont(maybeFrom
, expect(";")); }
576 if (value
== "default") { cx
.marked
= "keyword"; return cont(expression
, expect(";")); }
577 return pass(statement
);
579 function afterImport(type
) {
580 if (type
== "string") return cont();
581 return pass(importSpec
, maybeFrom
);
583 function importSpec(type
, value
) {
584 if (type
== "{") return contCommasep(importSpec
, "}");
585 if (type
== "variable") register(value
);
586 if (value
== "*") cx
.marked
= "keyword";
587 return cont(maybeAs
);
589 function maybeAs(_type
, value
) {
590 if (value
== "as") { cx
.marked
= "keyword"; return cont(importSpec
); }
592 function maybeFrom(_type
, value
) {
593 if (value
== "from") { cx
.marked
= "keyword"; return cont(expression
); }
595 function arrayLiteral(type
) {
596 if (type
== "]") return cont();
597 return pass(expressionNoComma
, maybeArrayComprehension
);
599 function maybeArrayComprehension(type
) {
600 if (type
== "for") return pass(comprehension
, expect("]"));
601 if (type
== ",") return cont(commasep(maybeexpressionNoComma
, "]"));
602 return pass(commasep(expressionNoComma
, "]"));
604 function comprehension(type
) {
605 if (type
== "for") return cont(forspec
, comprehension
);
606 if (type
== "if") return cont(expression
, comprehension
);
609 function isContinuedStatement(state
, textAfter
) {
610 return state
.lastType
== "operator" || state
.lastType
== "," ||
611 isOperatorChar
.test(textAfter
.charAt(0)) ||
612 /[,.]/.test(textAfter
.charAt(0));
618 startState: function(basecolumn
) {
623 lexical
: new JSLexical((basecolumn
|| 0) - indentUnit
, 0, "block", false),
624 localVars
: parserConfig
.localVars
,
625 context
: parserConfig
.localVars
&& {vars
: parserConfig
.localVars
},
628 if (parserConfig
.globalVars
&& typeof parserConfig
.globalVars
== "object")
629 state
.globalVars
= parserConfig
.globalVars
;
633 token: function(stream
, state
) {
635 if (!state
.lexical
.hasOwnProperty("align"))
636 state
.lexical
.align
= false;
637 state
.indented
= stream
.indentation();
638 findFatArrow(stream
, state
);
640 if (state
.tokenize
!= tokenComment
&& stream
.eatSpace()) return null;
641 var style
= state
.tokenize(stream
, state
);
642 if (type
== "comment") return style
;
643 state
.lastType
= type
== "operator" && (content
== "++" || content
== "--") ? "incdec" : type
;
644 return parseJS(state
, style
, type
, content
, stream
);
647 indent: function(state
, textAfter
) {
648 if (state
.tokenize
== tokenComment
) return CodeMirror
.Pass
;
649 if (state
.tokenize
!= tokenBase
) return 0;
650 var firstChar
= textAfter
&& textAfter
.charAt(0), lexical
= state
.lexical
;
651 // Kludge to prevent 'maybelse' from blocking lexical scope pops
652 if (!/^\s*else\b/.test(textAfter
)) for (var i
= state
.cc
.length
- 1; i
>= 0; --i
) {
654 if (c
== poplex
) lexical
= lexical
.prev
;
655 else if (c
!= maybeelse
) break;
657 if (lexical
.type
== "stat" && firstChar
== "}") lexical
= lexical
.prev
;
658 if (statementIndent
&& lexical
.type
== ")" && lexical
.prev
.type
== "stat")
659 lexical
= lexical
.prev
;
660 var type
= lexical
.type
, closing
= firstChar
== type
;
662 if (type
== "vardef") return lexical
.indented
+ (state
.lastType
== "operator" || state
.lastType
== "," ? lexical
.info
+ 1 : 0);
663 else if (type
== "form" && firstChar
== "{") return lexical
.indented
;
664 else if (type
== "form") return lexical
.indented
+ indentUnit
;
665 else if (type
== "stat")
666 return lexical
.indented
+ (isContinuedStatement(state
, textAfter
) ? statementIndent
|| indentUnit
: 0);
667 else if (lexical
.info
== "switch" && !closing
&& parserConfig
.doubleIndentSwitch
!= false)
668 return lexical
.indented
+ (/^(?:case|default)\b/.test(textAfter
) ? indentUnit
: 2 * indentUnit
);
669 else if (lexical
.align
) return lexical
.column
+ (closing
? 0 : 1);
670 else return lexical
.indented
+ (closing
? 0 : indentUnit
);
673 electricInput
: /^\s*(?:case .*?:|default:|\{|\})$/,
674 blockCommentStart
: jsonMode
? null : "/*",
675 blockCommentEnd
: jsonMode
? null : "*/",
676 lineComment
: jsonMode
? null : "//",
678 closeBrackets
: "()[]{}''\"\"``",
680 helperType
: jsonMode
? "json" : "javascript",
681 jsonldMode
: jsonldMode
,
686 CodeMirror
.registerHelper("wordChars", "javascript", /[\w$]/);
688 CodeMirror
.defineMIME("text/javascript", "javascript");
689 CodeMirror
.defineMIME("text/ecmascript", "javascript");
690 CodeMirror
.defineMIME("application/javascript", "javascript");
691 CodeMirror
.defineMIME("application/x-javascript", "javascript");
692 CodeMirror
.defineMIME("application/ecmascript", "javascript");
693 CodeMirror
.defineMIME("application/json", {name
: "javascript", json
: true});
694 CodeMirror
.defineMIME("application/x-json", {name
: "javascript", json
: true});
695 CodeMirror
.defineMIME("application/ld+json", {name
: "javascript", jsonld
: true});
696 CodeMirror
.defineMIME("text/typescript", { name
: "javascript", typescript
: true });
697 CodeMirror
.defineMIME("application/typescript", { name
: "javascript", typescript
: true });