UNPKG

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