tokens.mjs 17 KB

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