javascript.js 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function (mod) {
  4. if (typeof exports == 'object' && typeof module == 'object')
  5. // CommonJS
  6. mod(require('../../lib/codemirror'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.defineMode('javascript', function (config, parserConfig) {
  15. var indentUnit = config.indentUnit
  16. var statementIndent = parserConfig.statementIndent
  17. var jsonldMode = parserConfig.jsonld
  18. var jsonMode = parserConfig.json || jsonldMode
  19. var trackScope = parserConfig.trackScope !== false
  20. var isTS = parserConfig.typescript
  21. var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/
  22. // Tokenizer
  23. var keywords = (function () {
  24. function kw(type) {
  25. return { type: type, style: 'keyword' }
  26. }
  27. var A = kw('keyword a'),
  28. B = kw('keyword b'),
  29. C = kw('keyword c'),
  30. D = kw('keyword d')
  31. var operator = kw('operator'),
  32. atom = { type: 'atom', style: 'atom' }
  33. return {
  34. if: kw('if'),
  35. while: A,
  36. with: A,
  37. else: B,
  38. do: B,
  39. try: B,
  40. finally: B,
  41. return: D,
  42. break: D,
  43. continue: D,
  44. new: kw('new'),
  45. delete: C,
  46. void: C,
  47. throw: C,
  48. debugger: kw('debugger'),
  49. var: kw('var'),
  50. const: kw('var'),
  51. let: kw('var'),
  52. function: kw('function'),
  53. catch: kw('catch'),
  54. for: kw('for'),
  55. switch: kw('switch'),
  56. case: kw('case'),
  57. default: kw('default'),
  58. in: operator,
  59. typeof: operator,
  60. instanceof: operator,
  61. true: atom,
  62. false: atom,
  63. null: atom,
  64. undefined: atom,
  65. NaN: atom,
  66. Infinity: atom,
  67. this: kw('this'),
  68. class: kw('class'),
  69. super: kw('atom'),
  70. yield: C,
  71. export: kw('export'),
  72. import: kw('import'),
  73. extends: C,
  74. await: C,
  75. }
  76. })()
  77. var isOperatorChar = /[+\-*&%=<>!?|~^@]/
  78. var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/
  79. function readRegexp(stream) {
  80. var escaped = false,
  81. next,
  82. inSet = false
  83. while ((next = stream.next()) != null) {
  84. if (!escaped) {
  85. if (next == '/' && !inSet) return
  86. if (next == '[') inSet = true
  87. else if (inSet && next == ']') inSet = false
  88. }
  89. escaped = !escaped && next == '\\'
  90. }
  91. }
  92. // Used as scratch variables to communicate multiple values without
  93. // consing up tons of objects.
  94. var type, content
  95. function ret(tp, style, cont) {
  96. type = tp
  97. content = cont
  98. return style
  99. }
  100. function tokenBase(stream, state) {
  101. var ch = stream.next()
  102. if (ch == '"' || ch == "'") {
  103. state.tokenize = tokenString(ch)
  104. return state.tokenize(stream, state)
  105. } else if (ch == '.' && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
  106. return ret('number', 'number')
  107. } else if (ch == '.' && stream.match('..')) {
  108. return ret('spread', 'meta')
  109. } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
  110. return ret(ch)
  111. } else if (ch == '=' && stream.eat('>')) {
  112. return ret('=>', 'operator')
  113. } else if (ch == '0' && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
  114. return ret('number', 'number')
  115. } else if (/\d/.test(ch)) {
  116. stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/)
  117. return ret('number', 'number')
  118. } else if (ch == '/') {
  119. if (stream.eat('*')) {
  120. state.tokenize = tokenComment
  121. return tokenComment(stream, state)
  122. } else if (stream.eat('/')) {
  123. stream.skipToEnd()
  124. return ret('comment', 'comment')
  125. } else if (expressionAllowed(stream, state, 1)) {
  126. readRegexp(stream)
  127. stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/)
  128. return ret('regexp', 'string-2')
  129. } else {
  130. stream.eat('=')
  131. return ret('operator', 'operator', stream.current())
  132. }
  133. } else if (ch == '`') {
  134. state.tokenize = tokenQuasi
  135. return tokenQuasi(stream, state)
  136. } else if (ch == '#' && stream.peek() == '!') {
  137. stream.skipToEnd()
  138. return ret('meta', 'meta')
  139. } else if (ch == '#' && stream.eatWhile(wordRE)) {
  140. return ret('variable', 'property')
  141. } else if ((ch == '<' && stream.match('!--')) || (ch == '-' && stream.match('->') && !/\S/.test(stream.string.slice(0, stream.start)))) {
  142. stream.skipToEnd()
  143. return ret('comment', 'comment')
  144. } else if (isOperatorChar.test(ch)) {
  145. if (ch != '>' || !state.lexical || state.lexical.type != '>') {
  146. if (stream.eat('=')) {
  147. if (ch == '!' || ch == '=') stream.eat('=')
  148. } else if (/[<>*+\-|&?]/.test(ch)) {
  149. stream.eat(ch)
  150. if (ch == '>') stream.eat(ch)
  151. }
  152. }
  153. if (ch == '?' && stream.eat('.')) return ret('.')
  154. return ret('operator', 'operator', stream.current())
  155. } else if (wordRE.test(ch)) {
  156. stream.eatWhile(wordRE)
  157. var word = stream.current()
  158. if (state.lastType != '.') {
  159. if (keywords.propertyIsEnumerable(word)) {
  160. var kw = keywords[word]
  161. return ret(kw.type, kw.style, word)
  162. }
  163. if (word == 'async' && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) return ret('async', 'keyword', word)
  164. }
  165. return ret('variable', 'variable', word)
  166. }
  167. }
  168. function tokenString(quote) {
  169. return function (stream, state) {
  170. var escaped = false,
  171. next
  172. if (jsonldMode && stream.peek() == '@' && stream.match(isJsonldKeyword)) {
  173. state.tokenize = tokenBase
  174. return ret('jsonld-keyword', 'meta')
  175. }
  176. while ((next = stream.next()) != null) {
  177. if (next == quote && !escaped) break
  178. escaped = !escaped && next == '\\'
  179. }
  180. if (!escaped) state.tokenize = tokenBase
  181. return ret('string', 'string')
  182. }
  183. }
  184. function tokenComment(stream, state) {
  185. var maybeEnd = false,
  186. ch
  187. while ((ch = stream.next())) {
  188. if (ch == '/' && maybeEnd) {
  189. state.tokenize = tokenBase
  190. break
  191. }
  192. maybeEnd = ch == '*'
  193. }
  194. return ret('comment', 'comment')
  195. }
  196. function tokenQuasi(stream, state) {
  197. var escaped = false,
  198. next
  199. while ((next = stream.next()) != null) {
  200. if (!escaped && (next == '`' || (next == '$' && stream.eat('{')))) {
  201. state.tokenize = tokenBase
  202. break
  203. }
  204. escaped = !escaped && next == '\\'
  205. }
  206. return ret('quasi', 'string-2', stream.current())
  207. }
  208. var brackets = '([{}])'
  209. // This is a crude lookahead trick to try and notice that we're
  210. // parsing the argument patterns for a fat-arrow function before we
  211. // actually hit the arrow token. It only works if the arrow is on
  212. // the same line as the arguments and there's no strange noise
  213. // (comments) in between. Fallback is to only notice when we hit the
  214. // arrow, and not declare the arguments as locals for the arrow
  215. // body.
  216. function findFatArrow(stream, state) {
  217. if (state.fatArrowAt) state.fatArrowAt = null
  218. var arrow = stream.string.indexOf('=>', stream.start)
  219. if (arrow < 0) return
  220. if (isTS) {
  221. // Try to skip TypeScript return type declarations after the arguments
  222. var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
  223. if (m) arrow = m.index
  224. }
  225. var depth = 0,
  226. sawSomething = false
  227. for (var pos = arrow - 1; pos >= 0; --pos) {
  228. var ch = stream.string.charAt(pos)
  229. var bracket = brackets.indexOf(ch)
  230. if (bracket >= 0 && bracket < 3) {
  231. if (!depth) {
  232. ++pos
  233. break
  234. }
  235. if (--depth == 0) {
  236. if (ch == '(') sawSomething = true
  237. break
  238. }
  239. } else if (bracket >= 3 && bracket < 6) {
  240. ++depth
  241. } else if (wordRE.test(ch)) {
  242. sawSomething = true
  243. } else if (/["'\/`]/.test(ch)) {
  244. for (; ; --pos) {
  245. if (pos == 0) return
  246. var next = stream.string.charAt(pos - 1)
  247. if (next == ch && stream.string.charAt(pos - 2) != '\\') {
  248. pos--
  249. break
  250. }
  251. }
  252. } else if (sawSomething && !depth) {
  253. ++pos
  254. break
  255. }
  256. }
  257. if (sawSomething && !depth) state.fatArrowAt = pos
  258. }
  259. // Parser
  260. var atomicTypes = { atom: true, number: true, variable: true, string: true, regexp: true, this: true, import: true, 'jsonld-keyword': true }
  261. function JSLexical(indented, column, type, align, prev, info) {
  262. this.indented = indented
  263. this.column = column
  264. this.type = type
  265. this.prev = prev
  266. this.info = info
  267. if (align != null) this.align = align
  268. }
  269. function inScope(state, varname) {
  270. if (!trackScope) return false
  271. for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true
  272. for (var cx = state.context; cx; cx = cx.prev) {
  273. for (var v = cx.vars; v; v = v.next) if (v.name == varname) return true
  274. }
  275. }
  276. function parseJS(state, style, type, content, stream) {
  277. var cc = state.cc
  278. // Communicate our context to the combinators.
  279. // (Less wasteful than consing up a hundred closures on every call.)
  280. cx.state = state
  281. cx.stream = stream
  282. ;(cx.marked = null), (cx.cc = cc)
  283. cx.style = style
  284. if (!state.lexical.hasOwnProperty('align')) state.lexical.align = true
  285. while (true) {
  286. var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement
  287. if (combinator(type, content)) {
  288. while (cc.length && cc[cc.length - 1].lex) cc.pop()()
  289. if (cx.marked) return cx.marked
  290. if (type == 'variable' && inScope(state, content)) return 'variable-2'
  291. return style
  292. }
  293. }
  294. }
  295. // Combinator utils
  296. var cx = { state: null, column: null, marked: null, cc: null }
  297. function pass() {
  298. for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i])
  299. }
  300. function cont() {
  301. pass.apply(null, arguments)
  302. return true
  303. }
  304. function inList(name, list) {
  305. for (var v = list; v; v = v.next) if (v.name == name) return true
  306. return false
  307. }
  308. function register(varname) {
  309. var state = cx.state
  310. cx.marked = 'def'
  311. if (!trackScope) return
  312. if (state.context) {
  313. if (state.lexical.info == 'var' && state.context && state.context.block) {
  314. // FIXME function decls are also not block scoped
  315. var newContext = registerVarScoped(varname, state.context)
  316. if (newContext != null) {
  317. state.context = newContext
  318. return
  319. }
  320. } else if (!inList(varname, state.localVars)) {
  321. state.localVars = new Var(varname, state.localVars)
  322. return
  323. }
  324. }
  325. // Fall through means this is global
  326. if (parserConfig.globalVars && !inList(varname, state.globalVars)) state.globalVars = new Var(varname, state.globalVars)
  327. }
  328. function registerVarScoped(varname, context) {
  329. if (!context) {
  330. return null
  331. } else if (context.block) {
  332. var inner = registerVarScoped(varname, context.prev)
  333. if (!inner) return null
  334. if (inner == context.prev) return context
  335. return new Context(inner, context.vars, true)
  336. } else if (inList(varname, context.vars)) {
  337. return context
  338. } else {
  339. return new Context(context.prev, new Var(varname, context.vars), false)
  340. }
  341. }
  342. function isModifier(name) {
  343. return name == 'public' || name == 'private' || name == 'protected' || name == 'abstract' || name == 'readonly'
  344. }
  345. // Combinators
  346. function Context(prev, vars, block) {
  347. this.prev = prev
  348. this.vars = vars
  349. this.block = block
  350. }
  351. function Var(name, next) {
  352. this.name = name
  353. this.next = next
  354. }
  355. var defaultVars = new Var('this', new Var('arguments', null))
  356. function pushcontext() {
  357. cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
  358. cx.state.localVars = defaultVars
  359. }
  360. function pushblockcontext() {
  361. cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
  362. cx.state.localVars = null
  363. }
  364. pushcontext.lex = pushblockcontext.lex = true
  365. function popcontext() {
  366. cx.state.localVars = cx.state.context.vars
  367. cx.state.context = cx.state.context.prev
  368. }
  369. popcontext.lex = true
  370. function pushlex(type, info) {
  371. var result = function () {
  372. var state = cx.state,
  373. indent = state.indented
  374. if (state.lexical.type == 'stat') indent = state.lexical.indented
  375. else for (var outer = state.lexical; outer && outer.type == ')' && outer.align; outer = outer.prev) indent = outer.indented
  376. state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info)
  377. }
  378. result.lex = true
  379. return result
  380. }
  381. function poplex() {
  382. var state = cx.state
  383. if (state.lexical.prev) {
  384. if (state.lexical.type == ')') state.indented = state.lexical.indented
  385. state.lexical = state.lexical.prev
  386. }
  387. }
  388. poplex.lex = true
  389. function expect(wanted) {
  390. function exp(type) {
  391. if (type == wanted) return cont()
  392. else if (wanted == ';' || type == '}' || type == ')' || type == ']') return pass()
  393. else return cont(exp)
  394. }
  395. return exp
  396. }
  397. function statement(type, value) {
  398. if (type == 'var') return cont(pushlex('vardef', value), vardef, expect(';'), poplex)
  399. if (type == 'keyword a') return cont(pushlex('form'), parenExpr, statement, poplex)
  400. if (type == 'keyword b') return cont(pushlex('form'), statement, poplex)
  401. if (type == 'keyword d') return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex('stat'), maybeexpression, expect(';'), poplex)
  402. if (type == 'debugger') return cont(expect(';'))
  403. if (type == '{') return cont(pushlex('}'), pushblockcontext, block, poplex, popcontext)
  404. if (type == ';') return cont()
  405. if (type == 'if') {
  406. if (cx.state.lexical.info == 'else' && cx.state.cc[cx.state.cc.length - 1] == poplex) cx.state.cc.pop()()
  407. return cont(pushlex('form'), parenExpr, statement, poplex, maybeelse)
  408. }
  409. if (type == 'function') return cont(functiondef)
  410. if (type == 'for') return cont(pushlex('form'), pushblockcontext, forspec, statement, popcontext, poplex)
  411. if (type == 'class' || (isTS && value == 'interface')) {
  412. cx.marked = 'keyword'
  413. return cont(pushlex('form', type == 'class' ? type : value), className, poplex)
  414. }
  415. if (type == 'variable') {
  416. if (isTS && value == 'declare') {
  417. cx.marked = 'keyword'
  418. return cont(statement)
  419. } else if (isTS && (value == 'module' || value == 'enum' || value == 'type') && cx.stream.match(/^\s*\w/, false)) {
  420. cx.marked = 'keyword'
  421. if (value == 'enum') return cont(enumdef)
  422. else if (value == 'type') return cont(typename, expect('operator'), typeexpr, expect(';'))
  423. else return cont(pushlex('form'), pattern, expect('{'), pushlex('}'), block, poplex, poplex)
  424. } else if (isTS && value == 'namespace') {
  425. cx.marked = 'keyword'
  426. return cont(pushlex('form'), expression, statement, poplex)
  427. } else if (isTS && value == 'abstract') {
  428. cx.marked = 'keyword'
  429. return cont(statement)
  430. } else {
  431. return cont(pushlex('stat'), maybelabel)
  432. }
  433. }
  434. if (type == 'switch') return cont(pushlex('form'), parenExpr, expect('{'), pushlex('}', 'switch'), pushblockcontext, block, poplex, poplex, popcontext)
  435. if (type == 'case') return cont(expression, expect(':'))
  436. if (type == 'default') return cont(expect(':'))
  437. if (type == 'catch') return cont(pushlex('form'), pushcontext, maybeCatchBinding, statement, poplex, popcontext)
  438. if (type == 'export') return cont(pushlex('stat'), afterExport, poplex)
  439. if (type == 'import') return cont(pushlex('stat'), afterImport, poplex)
  440. if (type == 'async') return cont(statement)
  441. if (value == '@') return cont(expression, statement)
  442. return pass(pushlex('stat'), expression, expect(';'), poplex)
  443. }
  444. function maybeCatchBinding(type) {
  445. if (type == '(') return cont(funarg, expect(')'))
  446. }
  447. function expression(type, value) {
  448. return expressionInner(type, value, false)
  449. }
  450. function expressionNoComma(type, value) {
  451. return expressionInner(type, value, true)
  452. }
  453. function parenExpr(type) {
  454. if (type != '(') return pass()
  455. return cont(pushlex(')'), maybeexpression, expect(')'), poplex)
  456. }
  457. function expressionInner(type, value, noComma) {
  458. if (cx.state.fatArrowAt == cx.stream.start) {
  459. var body = noComma ? arrowBodyNoComma : arrowBody
  460. if (type == '(') return cont(pushcontext, pushlex(')'), commasep(funarg, ')'), poplex, expect('=>'), body, popcontext)
  461. else if (type == 'variable') return pass(pushcontext, pattern, expect('=>'), body, popcontext)
  462. }
  463. var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma
  464. if (atomicTypes.hasOwnProperty(type)) return cont(maybeop)
  465. if (type == 'function') return cont(functiondef, maybeop)
  466. if (type == 'class' || (isTS && value == 'interface')) {
  467. cx.marked = 'keyword'
  468. return cont(pushlex('form'), classExpression, poplex)
  469. }
  470. if (type == 'keyword c' || type == 'async') return cont(noComma ? expressionNoComma : expression)
  471. if (type == '(') return cont(pushlex(')'), maybeexpression, expect(')'), poplex, maybeop)
  472. if (type == 'operator' || type == 'spread') return cont(noComma ? expressionNoComma : expression)
  473. if (type == '[') return cont(pushlex(']'), arrayLiteral, poplex, maybeop)
  474. if (type == '{') return contCommasep(objprop, '}', null, maybeop)
  475. if (type == 'quasi') return pass(quasi, maybeop)
  476. if (type == 'new') return cont(maybeTarget(noComma))
  477. return cont()
  478. }
  479. function maybeexpression(type) {
  480. if (type.match(/[;\}\)\],]/)) return pass()
  481. return pass(expression)
  482. }
  483. function maybeoperatorComma(type, value) {
  484. if (type == ',') return cont(maybeexpression)
  485. return maybeoperatorNoComma(type, value, false)
  486. }
  487. function maybeoperatorNoComma(type, value, noComma) {
  488. var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma
  489. var expr = noComma == false ? expression : expressionNoComma
  490. if (type == '=>') return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext)
  491. if (type == 'operator') {
  492. if (/\+\+|--/.test(value) || (isTS && value == '!')) return cont(me)
  493. if (isTS && value == '<' && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) return cont(pushlex('>'), commasep(typeexpr, '>'), poplex, me)
  494. if (value == '?') return cont(expression, expect(':'), expr)
  495. return cont(expr)
  496. }
  497. if (type == 'quasi') {
  498. return pass(quasi, me)
  499. }
  500. if (type == ';') return
  501. if (type == '(') return contCommasep(expressionNoComma, ')', 'call', me)
  502. if (type == '.') return cont(property, me)
  503. if (type == '[') return cont(pushlex(']'), maybeexpression, expect(']'), poplex, me)
  504. if (isTS && value == 'as') {
  505. cx.marked = 'keyword'
  506. return cont(typeexpr, me)
  507. }
  508. if (type == 'regexp') {
  509. cx.state.lastType = cx.marked = 'operator'
  510. cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
  511. return cont(expr)
  512. }
  513. }
  514. function quasi(type, value) {
  515. if (type != 'quasi') return pass()
  516. if (value.slice(value.length - 2) != '${') return cont(quasi)
  517. return cont(maybeexpression, continueQuasi)
  518. }
  519. function continueQuasi(type) {
  520. if (type == '}') {
  521. cx.marked = 'string-2'
  522. cx.state.tokenize = tokenQuasi
  523. return cont(quasi)
  524. }
  525. }
  526. function arrowBody(type) {
  527. findFatArrow(cx.stream, cx.state)
  528. return pass(type == '{' ? statement : expression)
  529. }
  530. function arrowBodyNoComma(type) {
  531. findFatArrow(cx.stream, cx.state)
  532. return pass(type == '{' ? statement : expressionNoComma)
  533. }
  534. function maybeTarget(noComma) {
  535. return function (type) {
  536. if (type == '.') return cont(noComma ? targetNoComma : target)
  537. else if (type == 'variable' && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
  538. else return pass(noComma ? expressionNoComma : expression)
  539. }
  540. }
  541. function target(_, value) {
  542. if (value == 'target') {
  543. cx.marked = 'keyword'
  544. return cont(maybeoperatorComma)
  545. }
  546. }
  547. function targetNoComma(_, value) {
  548. if (value == 'target') {
  549. cx.marked = 'keyword'
  550. return cont(maybeoperatorNoComma)
  551. }
  552. }
  553. function maybelabel(type) {
  554. if (type == ':') return cont(poplex, statement)
  555. return pass(maybeoperatorComma, expect(';'), poplex)
  556. }
  557. function property(type) {
  558. if (type == 'variable') {
  559. cx.marked = 'property'
  560. return cont()
  561. }
  562. }
  563. function objprop(type, value) {
  564. if (type == 'async') {
  565. cx.marked = 'property'
  566. return cont(objprop)
  567. } else if (type == 'variable' || cx.style == 'keyword') {
  568. cx.marked = 'property'
  569. if (value == 'get' || value == 'set') return cont(getterSetter)
  570. var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
  571. if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) cx.state.fatArrowAt = cx.stream.pos + m[0].length
  572. return cont(afterprop)
  573. } else if (type == 'number' || type == 'string') {
  574. cx.marked = jsonldMode ? 'property' : cx.style + ' property'
  575. return cont(afterprop)
  576. } else if (type == 'jsonld-keyword') {
  577. return cont(afterprop)
  578. } else if (isTS && isModifier(value)) {
  579. cx.marked = 'keyword'
  580. return cont(objprop)
  581. } else if (type == '[') {
  582. return cont(expression, maybetype, expect(']'), afterprop)
  583. } else if (type == 'spread') {
  584. return cont(expressionNoComma, afterprop)
  585. } else if (value == '*') {
  586. cx.marked = 'keyword'
  587. return cont(objprop)
  588. } else if (type == ':') {
  589. return pass(afterprop)
  590. }
  591. }
  592. function getterSetter(type) {
  593. if (type != 'variable') return pass(afterprop)
  594. cx.marked = 'property'
  595. return cont(functiondef)
  596. }
  597. function afterprop(type) {
  598. if (type == ':') return cont(expressionNoComma)
  599. if (type == '(') return pass(functiondef)
  600. }
  601. function commasep(what, end, sep) {
  602. function proceed(type, value) {
  603. if (sep ? sep.indexOf(type) > -1 : type == ',') {
  604. var lex = cx.state.lexical
  605. if (lex.info == 'call') lex.pos = (lex.pos || 0) + 1
  606. return cont(function (type, value) {
  607. if (type == end || value == end) return pass()
  608. return pass(what)
  609. }, proceed)
  610. }
  611. if (type == end || value == end) return cont()
  612. if (sep && sep.indexOf(';') > -1) return pass(what)
  613. return cont(expect(end))
  614. }
  615. return function (type, value) {
  616. if (type == end || value == end) return cont()
  617. return pass(what, proceed)
  618. }
  619. }
  620. function contCommasep(what, end, info) {
  621. for (var i = 3; i < arguments.length; i++) cx.cc.push(arguments[i])
  622. return cont(pushlex(end, info), commasep(what, end), poplex)
  623. }
  624. function block(type) {
  625. if (type == '}') return cont()
  626. return pass(statement, block)
  627. }
  628. function maybetype(type, value) {
  629. if (isTS) {
  630. if (type == ':') return cont(typeexpr)
  631. if (value == '?') return cont(maybetype)
  632. }
  633. }
  634. function maybetypeOrIn(type, value) {
  635. if (isTS && (type == ':' || value == 'in')) return cont(typeexpr)
  636. }
  637. function mayberettype(type) {
  638. if (isTS && type == ':') {
  639. if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
  640. else return cont(typeexpr)
  641. }
  642. }
  643. function isKW(_, value) {
  644. if (value == 'is') {
  645. cx.marked = 'keyword'
  646. return cont()
  647. }
  648. }
  649. function typeexpr(type, value) {
  650. if (value == 'keyof' || value == 'typeof' || value == 'infer' || value == 'readonly') {
  651. cx.marked = 'keyword'
  652. return cont(value == 'typeof' ? expressionNoComma : typeexpr)
  653. }
  654. if (type == 'variable' || value == 'void') {
  655. cx.marked = 'type'
  656. return cont(afterType)
  657. }
  658. if (value == '|' || value == '&') return cont(typeexpr)
  659. if (type == 'string' || type == 'number' || type == 'atom') return cont(afterType)
  660. if (type == '[') return cont(pushlex(']'), commasep(typeexpr, ']', ','), poplex, afterType)
  661. if (type == '{') return cont(pushlex('}'), typeprops, poplex, afterType)
  662. if (type == '(') return cont(commasep(typearg, ')'), maybeReturnType, afterType)
  663. if (type == '<') return cont(commasep(typeexpr, '>'), typeexpr)
  664. if (type == 'quasi') {
  665. return pass(quasiType, afterType)
  666. }
  667. }
  668. function maybeReturnType(type) {
  669. if (type == '=>') return cont(typeexpr)
  670. }
  671. function typeprops(type) {
  672. if (type.match(/[\}\)\]]/)) return cont()
  673. if (type == ',' || type == ';') return cont(typeprops)
  674. return pass(typeprop, typeprops)
  675. }
  676. function typeprop(type, value) {
  677. if (type == 'variable' || cx.style == 'keyword') {
  678. cx.marked = 'property'
  679. return cont(typeprop)
  680. } else if (value == '?' || type == 'number' || type == 'string') {
  681. return cont(typeprop)
  682. } else if (type == ':') {
  683. return cont(typeexpr)
  684. } else if (type == '[') {
  685. return cont(expect('variable'), maybetypeOrIn, expect(']'), typeprop)
  686. } else if (type == '(') {
  687. return pass(functiondecl, typeprop)
  688. } else if (!type.match(/[;\}\)\],]/)) {
  689. return cont()
  690. }
  691. }
  692. function quasiType(type, value) {
  693. if (type != 'quasi') return pass()
  694. if (value.slice(value.length - 2) != '${') return cont(quasiType)
  695. return cont(typeexpr, continueQuasiType)
  696. }
  697. function continueQuasiType(type) {
  698. if (type == '}') {
  699. cx.marked = 'string-2'
  700. cx.state.tokenize = tokenQuasi
  701. return cont(quasiType)
  702. }
  703. }
  704. function typearg(type, value) {
  705. if ((type == 'variable' && cx.stream.match(/^\s*[?:]/, false)) || value == '?') return cont(typearg)
  706. if (type == ':') return cont(typeexpr)
  707. if (type == 'spread') return cont(typearg)
  708. return pass(typeexpr)
  709. }
  710. function afterType(type, value) {
  711. if (value == '<') return cont(pushlex('>'), commasep(typeexpr, '>'), poplex, afterType)
  712. if (value == '|' || type == '.' || value == '&') return cont(typeexpr)
  713. if (type == '[') return cont(typeexpr, expect(']'), afterType)
  714. if (value == 'extends' || value == 'implements') {
  715. cx.marked = 'keyword'
  716. return cont(typeexpr)
  717. }
  718. if (value == '?') return cont(typeexpr, expect(':'), typeexpr)
  719. }
  720. function maybeTypeArgs(_, value) {
  721. if (value == '<') return cont(pushlex('>'), commasep(typeexpr, '>'), poplex, afterType)
  722. }
  723. function typeparam() {
  724. return pass(typeexpr, maybeTypeDefault)
  725. }
  726. function maybeTypeDefault(_, value) {
  727. if (value == '=') return cont(typeexpr)
  728. }
  729. function vardef(_, value) {
  730. if (value == 'enum') {
  731. cx.marked = 'keyword'
  732. return cont(enumdef)
  733. }
  734. return pass(pattern, maybetype, maybeAssign, vardefCont)
  735. }
  736. function pattern(type, value) {
  737. if (isTS && isModifier(value)) {
  738. cx.marked = 'keyword'
  739. return cont(pattern)
  740. }
  741. if (type == 'variable') {
  742. register(value)
  743. return cont()
  744. }
  745. if (type == 'spread') return cont(pattern)
  746. if (type == '[') return contCommasep(eltpattern, ']')
  747. if (type == '{') return contCommasep(proppattern, '}')
  748. }
  749. function proppattern(type, value) {
  750. if (type == 'variable' && !cx.stream.match(/^\s*:/, false)) {
  751. register(value)
  752. return cont(maybeAssign)
  753. }
  754. if (type == 'variable') cx.marked = 'property'
  755. if (type == 'spread') return cont(pattern)
  756. if (type == '}') return pass()
  757. if (type == '[') return cont(expression, expect(']'), expect(':'), proppattern)
  758. return cont(expect(':'), pattern, maybeAssign)
  759. }
  760. function eltpattern() {
  761. return pass(pattern, maybeAssign)
  762. }
  763. function maybeAssign(_type, value) {
  764. if (value == '=') return cont(expressionNoComma)
  765. }
  766. function vardefCont(type) {
  767. if (type == ',') return cont(vardef)
  768. }
  769. function maybeelse(type, value) {
  770. if (type == 'keyword b' && value == 'else') return cont(pushlex('form', 'else'), statement, poplex)
  771. }
  772. function forspec(type, value) {
  773. if (value == 'await') return cont(forspec)
  774. if (type == '(') return cont(pushlex(')'), forspec1, poplex)
  775. }
  776. function forspec1(type) {
  777. if (type == 'var') return cont(vardef, forspec2)
  778. if (type == 'variable') return cont(forspec2)
  779. return pass(forspec2)
  780. }
  781. function forspec2(type, value) {
  782. if (type == ')') return cont()
  783. if (type == ';') return cont(forspec2)
  784. if (value == 'in' || value == 'of') {
  785. cx.marked = 'keyword'
  786. return cont(expression, forspec2)
  787. }
  788. return pass(expression, forspec2)
  789. }
  790. function functiondef(type, value) {
  791. if (value == '*') {
  792. cx.marked = 'keyword'
  793. return cont(functiondef)
  794. }
  795. if (type == 'variable') {
  796. register(value)
  797. return cont(functiondef)
  798. }
  799. if (type == '(') return cont(pushcontext, pushlex(')'), commasep(funarg, ')'), poplex, mayberettype, statement, popcontext)
  800. if (isTS && value == '<') return cont(pushlex('>'), commasep(typeparam, '>'), poplex, functiondef)
  801. }
  802. function functiondecl(type, value) {
  803. if (value == '*') {
  804. cx.marked = 'keyword'
  805. return cont(functiondecl)
  806. }
  807. if (type == 'variable') {
  808. register(value)
  809. return cont(functiondecl)
  810. }
  811. if (type == '(') return cont(pushcontext, pushlex(')'), commasep(funarg, ')'), poplex, mayberettype, popcontext)
  812. if (isTS && value == '<') return cont(pushlex('>'), commasep(typeparam, '>'), poplex, functiondecl)
  813. }
  814. function typename(type, value) {
  815. if (type == 'keyword' || type == 'variable') {
  816. cx.marked = 'type'
  817. return cont(typename)
  818. } else if (value == '<') {
  819. return cont(pushlex('>'), commasep(typeparam, '>'), poplex)
  820. }
  821. }
  822. function funarg(type, value) {
  823. if (value == '@') cont(expression, funarg)
  824. if (type == 'spread') return cont(funarg)
  825. if (isTS && isModifier(value)) {
  826. cx.marked = 'keyword'
  827. return cont(funarg)
  828. }
  829. if (isTS && type == 'this') return cont(maybetype, maybeAssign)
  830. return pass(pattern, maybetype, maybeAssign)
  831. }
  832. function classExpression(type, value) {
  833. // Class expressions may have an optional name.
  834. if (type == 'variable') return className(type, value)
  835. return classNameAfter(type, value)
  836. }
  837. function className(type, value) {
  838. if (type == 'variable') {
  839. register(value)
  840. return cont(classNameAfter)
  841. }
  842. }
  843. function classNameAfter(type, value) {
  844. if (value == '<') return cont(pushlex('>'), commasep(typeparam, '>'), poplex, classNameAfter)
  845. if (value == 'extends' || value == 'implements' || (isTS && type == ',')) {
  846. if (value == 'implements') cx.marked = 'keyword'
  847. return cont(isTS ? typeexpr : expression, classNameAfter)
  848. }
  849. if (type == '{') return cont(pushlex('}'), classBody, poplex)
  850. }
  851. function classBody(type, value) {
  852. if (type == 'async' || (type == 'variable' && (value == 'static' || value == 'get' || value == 'set' || (isTS && isModifier(value))) && cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
  853. cx.marked = 'keyword'
  854. return cont(classBody)
  855. }
  856. if (type == 'variable' || cx.style == 'keyword') {
  857. cx.marked = 'property'
  858. return cont(classfield, classBody)
  859. }
  860. if (type == 'number' || type == 'string') return cont(classfield, classBody)
  861. if (type == '[') return cont(expression, maybetype, expect(']'), classfield, classBody)
  862. if (value == '*') {
  863. cx.marked = 'keyword'
  864. return cont(classBody)
  865. }
  866. if (isTS && type == '(') return pass(functiondecl, classBody)
  867. if (type == ';' || type == ',') return cont(classBody)
  868. if (type == '}') return cont()
  869. if (value == '@') return cont(expression, classBody)
  870. }
  871. function classfield(type, value) {
  872. if (value == '!') return cont(classfield)
  873. if (value == '?') return cont(classfield)
  874. if (type == ':') return cont(typeexpr, maybeAssign)
  875. if (value == '=') return cont(expressionNoComma)
  876. var context = cx.state.lexical.prev,
  877. isInterface = context && context.info == 'interface'
  878. return pass(isInterface ? functiondecl : functiondef)
  879. }
  880. function afterExport(type, value) {
  881. if (value == '*') {
  882. cx.marked = 'keyword'
  883. return cont(maybeFrom, expect(';'))
  884. }
  885. if (value == 'default') {
  886. cx.marked = 'keyword'
  887. return cont(expression, expect(';'))
  888. }
  889. if (type == '{') return cont(commasep(exportField, '}'), maybeFrom, expect(';'))
  890. return pass(statement)
  891. }
  892. function exportField(type, value) {
  893. if (value == 'as') {
  894. cx.marked = 'keyword'
  895. return cont(expect('variable'))
  896. }
  897. if (type == 'variable') return pass(expressionNoComma, exportField)
  898. }
  899. function afterImport(type) {
  900. if (type == 'string') return cont()
  901. if (type == '(') return pass(expression)
  902. if (type == '.') return pass(maybeoperatorComma)
  903. return pass(importSpec, maybeMoreImports, maybeFrom)
  904. }
  905. function importSpec(type, value) {
  906. if (type == '{') return contCommasep(importSpec, '}')
  907. if (type == 'variable') register(value)
  908. if (value == '*') cx.marked = 'keyword'
  909. return cont(maybeAs)
  910. }
  911. function maybeMoreImports(type) {
  912. if (type == ',') return cont(importSpec, maybeMoreImports)
  913. }
  914. function maybeAs(_type, value) {
  915. if (value == 'as') {
  916. cx.marked = 'keyword'
  917. return cont(importSpec)
  918. }
  919. }
  920. function maybeFrom(_type, value) {
  921. if (value == 'from') {
  922. cx.marked = 'keyword'
  923. return cont(expression)
  924. }
  925. }
  926. function arrayLiteral(type) {
  927. if (type == ']') return cont()
  928. return pass(commasep(expressionNoComma, ']'))
  929. }
  930. function enumdef() {
  931. return pass(pushlex('form'), pattern, expect('{'), pushlex('}'), commasep(enummember, '}'), poplex, poplex)
  932. }
  933. function enummember() {
  934. return pass(pattern, maybeAssign)
  935. }
  936. function isContinuedStatement(state, textAfter) {
  937. return state.lastType == 'operator' || state.lastType == ',' || isOperatorChar.test(textAfter.charAt(0)) || /[,.]/.test(textAfter.charAt(0))
  938. }
  939. function expressionAllowed(stream, state, backUp) {
  940. return (
  941. (state.tokenize == tokenBase && /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType)) ||
  942. (state.lastType == 'quasi' && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
  943. )
  944. }
  945. // Interface
  946. return {
  947. startState: function (basecolumn) {
  948. var state = {
  949. tokenize: tokenBase,
  950. lastType: 'sof',
  951. cc: [],
  952. lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, 'block', false),
  953. localVars: parserConfig.localVars,
  954. context: parserConfig.localVars && new Context(null, null, false),
  955. indented: basecolumn || 0,
  956. }
  957. if (parserConfig.globalVars && typeof parserConfig.globalVars == 'object') state.globalVars = parserConfig.globalVars
  958. return state
  959. },
  960. token: function (stream, state) {
  961. if (stream.sol()) {
  962. if (!state.lexical.hasOwnProperty('align')) state.lexical.align = false
  963. state.indented = stream.indentation()
  964. findFatArrow(stream, state)
  965. }
  966. if (state.tokenize != tokenComment && stream.eatSpace()) return null
  967. var style = state.tokenize(stream, state)
  968. if (type == 'comment') return style
  969. state.lastType = type == 'operator' && (content == '++' || content == '--') ? 'incdec' : type
  970. return parseJS(state, style, type, content, stream)
  971. },
  972. indent: function (state, textAfter) {
  973. if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass
  974. if (state.tokenize != tokenBase) return 0
  975. var firstChar = textAfter && textAfter.charAt(0),
  976. lexical = state.lexical,
  977. top
  978. // Kludge to prevent 'maybelse' from blocking lexical scope pops
  979. if (!/^\s*else\b/.test(textAfter))
  980. for (var i = state.cc.length - 1; i >= 0; --i) {
  981. var c = state.cc[i]
  982. if (c == poplex) lexical = lexical.prev
  983. else if (c != maybeelse && c != popcontext) break
  984. }
  985. while (
  986. (lexical.type == 'stat' || lexical.type == 'form') &&
  987. (firstChar == '}' || ((top = state.cc[state.cc.length - 1]) && (top == maybeoperatorComma || top == maybeoperatorNoComma) && !/^[,\.=+\-*:?[\(]/.test(textAfter)))
  988. )
  989. lexical = lexical.prev
  990. if (statementIndent && lexical.type == ')' && lexical.prev.type == 'stat') lexical = lexical.prev
  991. var type = lexical.type,
  992. closing = firstChar == type
  993. if (type == 'vardef') return lexical.indented + (state.lastType == 'operator' || state.lastType == ',' ? lexical.info.length + 1 : 0)
  994. else if (type == 'form' && firstChar == '{') return lexical.indented
  995. else if (type == 'form') return lexical.indented + indentUnit
  996. else if (type == 'stat') return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0)
  997. else if (lexical.info == 'switch' && !closing && parserConfig.doubleIndentSwitch != false)
  998. return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit)
  999. else if (lexical.align) return lexical.column + (closing ? 0 : 1)
  1000. else return lexical.indented + (closing ? 0 : indentUnit)
  1001. },
  1002. electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
  1003. blockCommentStart: jsonMode ? null : '/*',
  1004. blockCommentEnd: jsonMode ? null : '*/',
  1005. blockCommentContinue: jsonMode ? null : ' * ',
  1006. lineComment: jsonMode ? null : '//',
  1007. fold: 'brace',
  1008. closeBrackets: '()[]{}\'\'""``',
  1009. helperType: jsonMode ? 'json' : 'javascript',
  1010. jsonldMode: jsonldMode,
  1011. jsonMode: jsonMode,
  1012. expressionAllowed: expressionAllowed,
  1013. skipExpression: function (state) {
  1014. parseJS(state, 'atom', 'atom', 'true', new CodeMirror.StringStream('', 2, null))
  1015. },
  1016. }
  1017. })
  1018. CodeMirror.registerHelper('wordChars', 'javascript', /[\w$]/)
  1019. CodeMirror.defineMIME('text/javascript', 'javascript')
  1020. CodeMirror.defineMIME('text/ecmascript', 'javascript')
  1021. CodeMirror.defineMIME('application/javascript', 'javascript')
  1022. CodeMirror.defineMIME('application/x-javascript', 'javascript')
  1023. CodeMirror.defineMIME('application/ecmascript', 'javascript')
  1024. CodeMirror.defineMIME('application/json', { name: 'javascript', json: true })
  1025. CodeMirror.defineMIME('application/x-json', { name: 'javascript', json: true })
  1026. CodeMirror.defineMIME('application/manifest+json', { name: 'javascript', json: true })
  1027. CodeMirror.defineMIME('application/ld+json', { name: 'javascript', jsonld: true })
  1028. CodeMirror.defineMIME('text/typescript', { name: 'javascript', typescript: true })
  1029. CodeMirror.defineMIME('application/typescript', { name: 'javascript', typescript: true })
  1030. })