tokens.mjs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. /* Hand-written tokenizer for LaTeX. */
  2. import { ExternalTokenizer, ContextTracker } from '@lezer/lr'
  3. import {
  4. LiteralArgContent,
  5. SpaceDelimitedLiteralArgContent,
  6. VerbContent,
  7. VerbatimContent,
  8. LstInlineContent,
  9. Begin,
  10. End,
  11. KnownEnvironment,
  12. MathDelimiter,
  13. Csname,
  14. TrailingWhitespaceOnly,
  15. TrailingContent,
  16. RefCtrlSeq,
  17. RefStarrableCtrlSeq,
  18. CiteCtrlSeq,
  19. CiteStarrableCtrlSeq,
  20. LabelCtrlSeq,
  21. MathTextCtrlSeq,
  22. HboxCtrlSeq,
  23. TitleCtrlSeq,
  24. AuthorCtrlSeq,
  25. AffilCtrlSeq,
  26. AffiliationCtrlSeq,
  27. DocumentClassCtrlSeq,
  28. UsePackageCtrlSeq,
  29. HrefCtrlSeq,
  30. UrlCtrlSeq,
  31. VerbCtrlSeq,
  32. LstInlineCtrlSeq,
  33. IncludeGraphicsCtrlSeq,
  34. CaptionCtrlSeq,
  35. DefCtrlSeq,
  36. LeftCtrlSeq,
  37. RightCtrlSeq,
  38. NewCommandCtrlSeq,
  39. RenewCommandCtrlSeq,
  40. NewEnvironmentCtrlSeq,
  41. RenewEnvironmentCtrlSeq,
  42. DocumentEnvName,
  43. TabularEnvName,
  44. EquationEnvName,
  45. EquationArrayEnvName,
  46. VerbatimEnvName,
  47. TikzPictureEnvName,
  48. FigureEnvName,
  49. OpenParenCtrlSym,
  50. CloseParenCtrlSym,
  51. OpenBracketCtrlSym,
  52. CloseBracketCtrlSym,
  53. LineBreakCtrlSym,
  54. // Sectioning commands
  55. BookCtrlSeq,
  56. PartCtrlSeq,
  57. ChapterCtrlSeq,
  58. SectionCtrlSeq,
  59. SubSectionCtrlSeq,
  60. SubSubSectionCtrlSeq,
  61. ParagraphCtrlSeq,
  62. SubParagraphCtrlSeq,
  63. InputCtrlSeq,
  64. IncludeCtrlSeq,
  65. ItemCtrlSeq,
  66. NewTheoremCtrlSeq,
  67. TheoremStyleCtrlSeq,
  68. BibliographyCtrlSeq,
  69. BibliographyStyleCtrlSeq,
  70. CenteringCtrlSeq,
  71. ListEnvName,
  72. MaketitleCtrlSeq,
  73. TextColorCtrlSeq,
  74. ColorBoxCtrlSeq,
  75. HLineCtrlSeq,
  76. TopRuleCtrlSeq,
  77. MidRuleCtrlSeq,
  78. BottomRuleCtrlSeq,
  79. TableEnvName,
  80. MultiColumnCtrlSeq,
  81. } from './latex.terms.mjs'
  82. function nameChar(ch) {
  83. // we accept A-Z a-z 0-9 * + @ in environment names
  84. return (
  85. (ch >= 65 && ch <= 90) ||
  86. (ch >= 97 && ch <= 122) ||
  87. (ch >= 48 && ch <= 57) ||
  88. ch === 42 ||
  89. ch === 43 ||
  90. ch === 64
  91. )
  92. }
  93. // match [a-zA-Z]
  94. function alphaChar(ch) {
  95. return (ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)
  96. }
  97. let cachedName = null
  98. let cachedInput = null
  99. let cachedPos = 0
  100. function envNameAfter(input, offset) {
  101. const pos = input.pos + offset
  102. if (cachedInput === input && cachedPos === pos) {
  103. return cachedName
  104. }
  105. if (input.peek(offset) !== '{'.charCodeAt(0)) return
  106. offset++
  107. let name = ''
  108. for (;;) {
  109. const next = input.peek(offset)
  110. if (!nameChar(next)) break
  111. name += String.fromCharCode(next)
  112. offset++
  113. }
  114. cachedInput = input
  115. cachedPos = pos
  116. return (cachedName = name || null)
  117. }
  118. function ElementContext(name, parent) {
  119. this.name = name
  120. this.parent = parent
  121. this.hash = parent ? parent.hash : 0
  122. for (let i = 0; i < name.length; i++)
  123. this.hash +=
  124. (this.hash << 4) + name.charCodeAt(i) + (name.charCodeAt(i) << 8)
  125. }
  126. export const elementContext = new ContextTracker({
  127. start: null,
  128. shift(context, term, stack, input) {
  129. return term === Begin
  130. ? new ElementContext(envNameAfter(input, '\\begin'.length) || '', context)
  131. : context
  132. },
  133. reduce(context, term) {
  134. return term === KnownEnvironment && context ? context.parent : context
  135. },
  136. reuse(context, node, _stack, input) {
  137. const type = node.type.id
  138. return type === Begin
  139. ? new ElementContext(envNameAfter(input, 0) || '', context)
  140. : context
  141. },
  142. hash(context) {
  143. return context ? context.hash : 0
  144. },
  145. strict: false,
  146. })
  147. // tokenizer for \verb|...| commands
  148. export const verbTokenizer = new ExternalTokenizer(
  149. (input, stack) => {
  150. if (input.next === '*'.charCodeAt(0)) input.advance()
  151. const delimiter = input.next
  152. if (delimiter === -1) return // hit end of file
  153. if (/\s|\*/.test(String.fromCharCode(delimiter))) return // invalid delimiter
  154. input.advance()
  155. for (;;) {
  156. const next = input.next
  157. if (next === -1 || next === CHAR_NEWLINE) return
  158. input.advance()
  159. if (next === delimiter) break
  160. }
  161. return input.acceptToken(VerbContent)
  162. },
  163. { contextual: false }
  164. )
  165. // tokenizer for \lstinline|...| commands
  166. export const lstinlineTokenizer = new ExternalTokenizer(
  167. (input, stack) => {
  168. let delimiter = input.next
  169. if (delimiter === -1) return // hit end of file
  170. if (/\s/.test(String.fromCharCode(delimiter))) {
  171. return // invalid delimiter
  172. }
  173. if (delimiter === CHAR_OPEN_BRACE) {
  174. delimiter = CHAR_CLOSE_BRACE
  175. }
  176. input.advance()
  177. for (;;) {
  178. const next = input.next
  179. if (next === -1 || next === CHAR_NEWLINE) return
  180. input.advance()
  181. if (next === delimiter) break
  182. }
  183. return input.acceptToken(LstInlineContent)
  184. },
  185. { contextual: false }
  186. )
  187. const matchForward = (input, expected, offset = 0) => {
  188. for (let i = 0; i < expected.length; i++) {
  189. if (String.fromCharCode(input.peek(offset + i)) !== expected[i]) {
  190. return false
  191. }
  192. }
  193. return true
  194. }
  195. // tokenizer for \begin{verbatim}...\end{verbatim} environments
  196. export const verbatimTokenizer = new ExternalTokenizer(
  197. (input, stack) => {
  198. const delimiter = '\\end{' + stack.context.name + '}'
  199. for (let offset = 0; ; offset++) {
  200. const next = input.peek(offset)
  201. if (next === -1 || matchForward(input, delimiter, offset)) {
  202. return input.acceptToken(VerbatimContent, offset)
  203. }
  204. }
  205. },
  206. { contextual: false }
  207. )
  208. // tokenizer for \href{...} and similar commands
  209. export const literalArgTokenizer = new ExternalTokenizer(
  210. input => {
  211. for (let offset = 0; ; offset++) {
  212. const next = input.peek(offset)
  213. if (next === -1 || next === CHAR_CLOSE_BRACE) {
  214. return input.acceptToken(LiteralArgContent, offset)
  215. }
  216. }
  217. },
  218. { contextual: false }
  219. )
  220. // tokenizer for literal content delimited by whitespace, such as in `\input foo.tex`
  221. export const spaceDelimitedLiteralArgTokenizer = new ExternalTokenizer(
  222. input => {
  223. for (let offset = 0; ; offset++) {
  224. const next = input.peek(offset)
  225. if (next === -1 || next === CHAR_SPACE || next === CHAR_NEWLINE) {
  226. return input.acceptToken(SpaceDelimitedLiteralArgContent, offset)
  227. }
  228. }
  229. },
  230. { contextual: false }
  231. )
  232. // helper function to look up charCodes
  233. function _char(s) {
  234. return s.charCodeAt(0)
  235. }
  236. // Allowed delimiters, from the LaTeX manual, table 3.10
  237. // ( ) [ ] / | \{ \} \| and additional names below
  238. // The empty delimiter . is also allowed
  239. const CHAR_SLASH = _char('/')
  240. const CHAR_PIPE = _char('|')
  241. const CHAR_OPEN_PAREN = _char('(')
  242. const CHAR_CLOSE_PAREN = _char(')')
  243. const CHAR_OPEN_BRACKET = _char('[')
  244. const CHAR_CLOSE_BRACKET = _char(']')
  245. const CHAR_FULL_STOP = _char('.')
  246. const CHAR_BACKSLASH = _char('\\')
  247. const CHAR_OPEN_BRACE = _char('{')
  248. const CHAR_CLOSE_BRACE = _char('}')
  249. const ALLOWED_DELIMITER_NAMES = [
  250. 'lfloor',
  251. 'rfloor',
  252. 'lceil',
  253. 'rceil',
  254. 'langle',
  255. 'rangle',
  256. 'backslash',
  257. 'uparrow',
  258. 'downarrow',
  259. 'Uparrow',
  260. 'Downarrow',
  261. 'updownarrow',
  262. 'Updownarrow',
  263. 'lvert',
  264. 'rvert',
  265. 'lVert',
  266. 'rVert',
  267. ]
  268. // Given a list of allowed command names, return those with leading characters that are the same as the matchString
  269. function findPartialMatches(list, matchString) {
  270. const size = matchString.length
  271. return list.filter(
  272. entry => entry.length >= size && entry.substring(0, size) === matchString
  273. )
  274. }
  275. // tokenizer for \leftX ... \rightX delimiter tokens
  276. export const mathDelimiterTokenizer = new ExternalTokenizer(
  277. (input, stack) => {
  278. let content = ''
  279. let offset = 0
  280. let end = -1
  281. // look at the first character, we only accept the following /|()[].
  282. let next = input.peek(offset)
  283. if (next === -1) {
  284. return
  285. }
  286. if (
  287. next === CHAR_SLASH ||
  288. next === CHAR_PIPE ||
  289. next === CHAR_OPEN_PAREN ||
  290. next === CHAR_CLOSE_PAREN ||
  291. next === CHAR_OPEN_BRACKET ||
  292. next === CHAR_CLOSE_BRACKET ||
  293. next === CHAR_FULL_STOP
  294. ) {
  295. return input.acceptToken(MathDelimiter, 1)
  296. }
  297. // reject anything else not starting with a backslash,
  298. // we only accept control symbols or control sequences
  299. if (next !== CHAR_BACKSLASH) {
  300. return
  301. }
  302. // look at the second character, we only accept \{ and \} and \| as control symbols
  303. offset++
  304. next = input.peek(offset)
  305. if (next === -1) {
  306. return
  307. }
  308. if (
  309. next === CHAR_OPEN_BRACE ||
  310. next === CHAR_CLOSE_BRACE ||
  311. next === CHAR_PIPE
  312. ) {
  313. return input.acceptToken(MathDelimiter, 2)
  314. }
  315. // We haven't matched any symbols, so now try matching command names.
  316. // Is this character a potential match to the remaining allowed delimiter names?
  317. content = String.fromCharCode(next)
  318. let candidates = findPartialMatches(ALLOWED_DELIMITER_NAMES, content)
  319. if (!candidates.length) return
  320. // we have some candidates, look at subsequent characters
  321. offset++
  322. for (;;) {
  323. const next = input.peek(offset)
  324. // stop when we reach the end of file or a non-alphabetic character
  325. if (next === -1 || !nameChar(next)) {
  326. end = offset - 1
  327. break
  328. }
  329. content += String.fromCharCode(next)
  330. // find how many candidates remain with the new input
  331. candidates = findPartialMatches(candidates, content)
  332. if (!candidates.length) return // no matches remaining
  333. end = offset
  334. offset++
  335. }
  336. if (!candidates.includes(content)) return // not a valid delimiter
  337. // accept the content as a valid delimiter
  338. return input.acceptToken(MathDelimiter, end + 1)
  339. },
  340. { contextual: false }
  341. )
  342. const CHAR_AT_SYMBOL = _char('@')
  343. export const csnameTokenizer = new ExternalTokenizer((input, stack) => {
  344. let offset = 0
  345. let end = -1
  346. // look at the first character, we are looking for acceptable control sequence names
  347. // including @ signs, \\[a-zA-Z@]+
  348. const next = input.peek(offset)
  349. if (next === -1) {
  350. return
  351. }
  352. // reject anything not starting with a backslash,
  353. // we only accept control sequences
  354. if (next !== CHAR_BACKSLASH) {
  355. return
  356. }
  357. offset++
  358. for (;;) {
  359. const next = input.peek(offset)
  360. // stop when we reach the end of file or a non-csname character
  361. if (next === -1 || !(alphaChar(next) || next === CHAR_AT_SYMBOL)) {
  362. end = offset - 1
  363. break
  364. }
  365. end = offset
  366. offset++
  367. }
  368. if (end === -1) return
  369. // accept the content as a valid control sequence
  370. return input.acceptToken(Csname, end + 1)
  371. })
  372. const CHAR_SPACE = _char(' ')
  373. const CHAR_NEWLINE = _char('\n')
  374. const END_DOCUMENT_MARK = '\\end{document}'.split('').reverse()
  375. export const trailingContentTokenizer = new ExternalTokenizer(
  376. (input, stack) => {
  377. if (input.next === -1) return // no trailing content
  378. // Look back for end-document mark, bail out if any characters do not match
  379. for (let i = 1; i < END_DOCUMENT_MARK.length + 1; i++) {
  380. if (String.fromCharCode(input.peek(-i)) !== END_DOCUMENT_MARK[i - 1]) {
  381. return
  382. }
  383. }
  384. while (input.next === CHAR_SPACE || input.next === CHAR_NEWLINE) {
  385. const next = input.advance()
  386. if (next === -1) return input.acceptToken(TrailingWhitespaceOnly) // trailing whitespace only
  387. }
  388. // accept the all content up to the end of the document
  389. while (input.advance() !== -1) {
  390. //
  391. }
  392. return input.acceptToken(TrailingContent)
  393. }
  394. )
  395. const refCommands = new Set([
  396. '\\fullref',
  397. '\\Vref',
  398. '\\autopageref',
  399. '\\autoref',
  400. '\\eqref',
  401. '\\labelcpageref',
  402. '\\labelcref',
  403. '\\lcnamecref',
  404. '\\lcnamecrefs',
  405. '\\namecref',
  406. '\\nameCref',
  407. '\\namecrefs',
  408. '\\nameCrefs',
  409. '\\thnameref',
  410. '\\thref',
  411. '\\titleref',
  412. '\\vrefrange',
  413. '\\Crefrange',
  414. '\\Crefrang',
  415. '\\fref',
  416. '\\pref',
  417. '\\tref',
  418. '\\Aref',
  419. '\\Bref',
  420. '\\Pref',
  421. '\\Sref',
  422. '\\vref',
  423. '\\nameref',
  424. ])
  425. const refStarrableCommands = new Set([
  426. '\\vpageref',
  427. '\\vref',
  428. '\\zcpageref',
  429. '\\zcref',
  430. '\\zfullref',
  431. '\\zref',
  432. '\\zvpageref',
  433. '\\zvref',
  434. '\\cref',
  435. '\\Cref',
  436. '\\pageref',
  437. '\\ref',
  438. '\\Ref',
  439. '\\zpageref',
  440. '\\ztitleref',
  441. '\\vpagerefrange',
  442. '\\zvpagerefrange',
  443. '\\zvrefrange',
  444. '\\crefrange',
  445. ])
  446. const citeCommands = new Set([
  447. '\\autocites',
  448. '\\Autocites',
  449. '\\Cite',
  450. '\\citeA',
  451. '\\citealp',
  452. '\\Citealp',
  453. '\\citealt',
  454. '\\Citealt',
  455. '\\citeauthorNP',
  456. '\\citeauthorp',
  457. '\\Citeauthorp',
  458. '\\citeauthort',
  459. '\\Citeauthort',
  460. '\\citeNP',
  461. '\\citenum',
  462. '\\cites',
  463. '\\Cites',
  464. '\\citeurl',
  465. '\\citeyearpar',
  466. '\\defcitealias',
  467. '\\fnotecite',
  468. '\\footcite',
  469. '\\footcitetext',
  470. '\\footfullcite',
  471. '\\footnotecites',
  472. '\\Footnotecites',
  473. '\\fullcite',
  474. '\\fullciteA',
  475. '\\fullciteauthor',
  476. '\\fullciteauthorNP',
  477. '\\maskcite',
  478. '\\maskciteA',
  479. '\\maskcitealp',
  480. '\\maskCitealp',
  481. '\\maskcitealt',
  482. '\\maskCitealt',
  483. '\\maskciteauthor',
  484. '\\maskciteauthorNP',
  485. '\\maskciteauthorp',
  486. '\\maskCiteauthorp',
  487. '\\maskciteauthort',
  488. '\\maskCiteauthort',
  489. '\\maskciteNP',
  490. '\\maskcitenum',
  491. '\\maskcitep',
  492. '\\maskCitep',
  493. '\\maskcitepalias',
  494. '\\maskcitet',
  495. '\\maskCitet',
  496. '\\maskcitetalias',
  497. '\\maskciteyear',
  498. '\\maskciteyearNP',
  499. '\\maskciteyearpar',
  500. '\\maskfullcite',
  501. '\\maskfullciteA',
  502. '\\maskfullciteauthor',
  503. '\\maskfullciteauthorNP',
  504. '\\masknocite',
  505. '\\maskshortcite',
  506. '\\maskshortciteA',
  507. '\\maskshortciteauthor',
  508. '\\maskshortciteauthorNP',
  509. '\\maskshortciteNP',
  510. '\\mautocite',
  511. '\\Mautocite',
  512. '\\mcite',
  513. '\\Mcite',
  514. '\\mfootcite',
  515. '\\mfootcitetext',
  516. '\\mparencite',
  517. '\\Mparencite',
  518. '\\msupercite',
  519. '\\mtextcite',
  520. '\\Mtextcite',
  521. '\\nocite',
  522. '\\nocitemeta',
  523. '\\notecite',
  524. '\\Parencite',
  525. '\\parencites',
  526. '\\Parencites',
  527. '\\pnotecite',
  528. '\\shortcite',
  529. '\\shortciteA',
  530. '\\shortciteauthor',
  531. '\\shortciteauthorNP',
  532. '\\shortciteNP',
  533. '\\smartcite',
  534. '\\Smartcite',
  535. '\\smartcites',
  536. '\\Smartcites',
  537. '\\supercite',
  538. '\\supercites',
  539. '\\textcite',
  540. '\\Textcite',
  541. '\\textcites',
  542. '\\Textcites',
  543. ])
  544. const citeStarredCommands = new Set([
  545. '\\cite',
  546. '\\citeauthor',
  547. '\\Citeauthor',
  548. '\\citedate',
  549. '\\citep',
  550. '\\Citep',
  551. '\\citetitle',
  552. '\\citeyear',
  553. '\\parencite',
  554. '\\citet',
  555. '\\autocite',
  556. '\\Autocite',
  557. ])
  558. const labelCommands = new Set(['\\label', '\\thlabel', '\\zlabel'])
  559. const mathTextCommands = new Set(['\\text', '\\tag', '\\textrm', '\\intertext'])
  560. const otherKnowncommands = {
  561. '\\hbox': HboxCtrlSeq,
  562. '\\title': TitleCtrlSeq,
  563. '\\author': AuthorCtrlSeq,
  564. '\\affil': AffilCtrlSeq,
  565. '\\affiliation': AffiliationCtrlSeq,
  566. '\\documentclass': DocumentClassCtrlSeq,
  567. '\\usepackage': UsePackageCtrlSeq,
  568. '\\href': HrefCtrlSeq,
  569. '\\url': UrlCtrlSeq,
  570. '\\verb': VerbCtrlSeq,
  571. '\\lstinline': LstInlineCtrlSeq,
  572. '\\includegraphics': IncludeGraphicsCtrlSeq,
  573. '\\caption': CaptionCtrlSeq,
  574. '\\def': DefCtrlSeq,
  575. '\\left': LeftCtrlSeq,
  576. '\\right': RightCtrlSeq,
  577. '\\newcommand': NewCommandCtrlSeq,
  578. '\\renewcommand': RenewCommandCtrlSeq,
  579. '\\newenvironment': NewEnvironmentCtrlSeq,
  580. '\\renewenvironment': RenewEnvironmentCtrlSeq,
  581. '\\book': BookCtrlSeq,
  582. '\\part': PartCtrlSeq,
  583. '\\addpart': PartCtrlSeq,
  584. '\\chapter': ChapterCtrlSeq,
  585. '\\addchap': ChapterCtrlSeq,
  586. '\\section': SectionCtrlSeq,
  587. '\\addseq': SectionCtrlSeq,
  588. '\\subsection': SubSectionCtrlSeq,
  589. '\\subsubsection': SubSubSectionCtrlSeq,
  590. '\\paragraph': ParagraphCtrlSeq,
  591. '\\subparagraph': SubParagraphCtrlSeq,
  592. '\\input': InputCtrlSeq,
  593. '\\include': IncludeCtrlSeq,
  594. '\\item': ItemCtrlSeq,
  595. '\\centering': CenteringCtrlSeq,
  596. '\\newtheorem': NewTheoremCtrlSeq,
  597. '\\theoremstyle': TheoremStyleCtrlSeq,
  598. '\\bibliography': BibliographyCtrlSeq,
  599. '\\bibliographystyle': BibliographyStyleCtrlSeq,
  600. '\\maketitle': MaketitleCtrlSeq,
  601. '\\textcolor': TextColorCtrlSeq,
  602. '\\colorbox': ColorBoxCtrlSeq,
  603. '\\hline': HLineCtrlSeq,
  604. '\\toprule': TopRuleCtrlSeq,
  605. '\\midrule': MidRuleCtrlSeq,
  606. '\\bottomrule': BottomRuleCtrlSeq,
  607. '\\multicolumn': MultiColumnCtrlSeq,
  608. }
  609. // specializer for control sequences
  610. // return new tokens for specific control sequences
  611. export const specializeCtrlSeq = (name, terms) => {
  612. if (name === '\\begin') return Begin
  613. if (name === '\\end') return End
  614. if (refCommands.has(name)) {
  615. return RefCtrlSeq
  616. }
  617. if (refStarrableCommands.has(name)) {
  618. return RefStarrableCtrlSeq
  619. }
  620. if (citeCommands.has(name)) {
  621. return CiteCtrlSeq
  622. }
  623. if (citeStarredCommands.has(name)) {
  624. return CiteStarrableCtrlSeq
  625. }
  626. if (labelCommands.has(name)) {
  627. return LabelCtrlSeq
  628. }
  629. if (mathTextCommands.has(name)) {
  630. return MathTextCtrlSeq
  631. }
  632. return otherKnowncommands[name] || -1
  633. }
  634. const tabularEnvNames = new Set([
  635. 'tabular',
  636. 'xltabular',
  637. 'tabularx',
  638. 'longtable',
  639. ])
  640. const equationEnvNames = new Set([
  641. 'equation',
  642. 'equation*',
  643. 'displaymath',
  644. 'displaymath*',
  645. 'math',
  646. 'math*',
  647. 'multline',
  648. 'multline*',
  649. 'matrix',
  650. 'tikzcd',
  651. ])
  652. const equationArrayEnvNames = new Set([
  653. 'array',
  654. 'eqnarray',
  655. 'eqnarray*',
  656. 'align',
  657. 'align*',
  658. 'alignat',
  659. 'alignat*',
  660. 'flalign',
  661. 'flalign*',
  662. 'gather',
  663. 'gather*',
  664. 'pmatrix',
  665. 'pmatrix*',
  666. 'bmatrix',
  667. 'bmatrix*',
  668. 'Bmatrix',
  669. 'Bmatrix*',
  670. 'vmatrix',
  671. 'vmatrix*',
  672. 'Vmatrix',
  673. 'Vmatrix*',
  674. 'smallmatrix',
  675. 'smallmatrix*',
  676. 'split',
  677. 'split*',
  678. 'gathered',
  679. 'gathered*',
  680. 'aligned',
  681. 'aligned*',
  682. 'alignedat',
  683. 'alignedat*',
  684. 'cases',
  685. 'cases*',
  686. 'dcases',
  687. 'dcases*',
  688. 'rcases',
  689. 'rcases*',
  690. 'IEEEeqnarray',
  691. 'IEEEeqnarray*',
  692. ])
  693. const verbatimEnvNames = new Set([
  694. 'verbatim',
  695. 'boxedverbatim',
  696. 'lstlisting',
  697. 'minted',
  698. 'Verbatim',
  699. 'lstlisting',
  700. 'codeexample',
  701. 'comment',
  702. ])
  703. const otherKnownEnvNames = {
  704. document: DocumentEnvName,
  705. tikzpicture: TikzPictureEnvName,
  706. figure: FigureEnvName,
  707. 'figure*': FigureEnvName,
  708. subfigure: FigureEnvName,
  709. enumerate: ListEnvName,
  710. itemize: ListEnvName,
  711. table: TableEnvName,
  712. }
  713. export const specializeEnvName = (name, terms) => {
  714. if (tabularEnvNames.has(name)) {
  715. return TabularEnvName
  716. }
  717. if (equationEnvNames.has(name)) {
  718. return EquationEnvName
  719. }
  720. if (equationArrayEnvNames.has(name)) {
  721. return EquationArrayEnvName
  722. }
  723. if (verbatimEnvNames.has(name)) {
  724. return VerbatimEnvName
  725. }
  726. return otherKnownEnvNames[name] || -1
  727. }
  728. const otherKnownCtrlSyms = {
  729. '\\(': OpenParenCtrlSym,
  730. '\\)': CloseParenCtrlSym,
  731. '\\[': OpenBracketCtrlSym,
  732. '\\]': CloseBracketCtrlSym,
  733. '\\\\': LineBreakCtrlSym,
  734. }
  735. export const specializeCtrlSym = (name, terms) => {
  736. return otherKnownCtrlSyms[name] || -1
  737. }