tokens.mjs 18 KB

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