latex.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. Language: LaTeX
  3. Author: Benedikt Wilde <bwilde@posteo.de>
  4. Website: https://www.latex-project.org
  5. Category: markup
  6. */
  7. /** @type LanguageFn */
  8. function latex(hljs) {
  9. const regex = hljs.regex;
  10. const KNOWN_CONTROL_WORDS = regex.either(...[
  11. '(?:NeedsTeXFormat|RequirePackage|GetIdInfo)',
  12. 'Provides(?:Expl)?(?:Package|Class|File)',
  13. '(?:DeclareOption|ProcessOptions)',
  14. '(?:documentclass|usepackage|input|include)',
  15. 'makeat(?:letter|other)',
  16. 'ExplSyntax(?:On|Off)',
  17. '(?:new|renew|provide)?command',
  18. '(?:re)newenvironment',
  19. '(?:New|Renew|Provide|Declare)(?:Expandable)?DocumentCommand',
  20. '(?:New|Renew|Provide|Declare)DocumentEnvironment',
  21. '(?:(?:e|g|x)?def|let)',
  22. '(?:begin|end)',
  23. '(?:part|chapter|(?:sub){0,2}section|(?:sub)?paragraph)',
  24. 'caption',
  25. '(?:label|(?:eq|page|name)?ref|(?:paren|foot|super)?cite)',
  26. '(?:alpha|beta|[Gg]amma|[Dd]elta|(?:var)?epsilon|zeta|eta|[Tt]heta|vartheta)',
  27. '(?:iota|(?:var)?kappa|[Ll]ambda|mu|nu|[Xx]i|[Pp]i|varpi|(?:var)rho)',
  28. '(?:[Ss]igma|varsigma|tau|[Uu]psilon|[Pp]hi|varphi|chi|[Pp]si|[Oo]mega)',
  29. '(?:frac|sum|prod|lim|infty|times|sqrt|leq|geq|left|right|middle|[bB]igg?)',
  30. '(?:[lr]angle|q?quad|[lcvdi]?dots|d?dot|hat|tilde|bar)'
  31. ].map(word => word + '(?![a-zA-Z@:_])'));
  32. const L3_REGEX = new RegExp([
  33. // A function \module_function_name:signature or \__module_function_name:signature,
  34. // where both module and function_name need at least two characters and
  35. // function_name may contain single underscores.
  36. '(?:__)?[a-zA-Z]{2,}_[a-zA-Z](?:_?[a-zA-Z])+:[a-zA-Z]*',
  37. // A variable \scope_module_and_name_type or \scope__module_ane_name_type,
  38. // where scope is one of l, g or c, type needs at least two characters
  39. // and module_and_name may contain single underscores.
  40. '[lgc]__?[a-zA-Z](?:_?[a-zA-Z])*_[a-zA-Z]{2,}',
  41. // A quark \q_the_name or \q__the_name or
  42. // scan mark \s_the_name or \s__vthe_name,
  43. // where variable_name needs at least two characters and
  44. // may contain single underscores.
  45. '[qs]__?[a-zA-Z](?:_?[a-zA-Z])+',
  46. // Other LaTeX3 macro names that are not covered by the three rules above.
  47. 'use(?:_i)?:[a-zA-Z]*',
  48. '(?:else|fi|or):',
  49. '(?:if|cs|exp):w',
  50. '(?:hbox|vbox):n',
  51. '::[a-zA-Z]_unbraced',
  52. '::[a-zA-Z:]'
  53. ].map(pattern => pattern + '(?![a-zA-Z:_])').join('|'));
  54. const L2_VARIANTS = [
  55. { begin: /[a-zA-Z@]+/ }, // control word
  56. { begin: /[^a-zA-Z@]?/ } // control symbol
  57. ];
  58. const DOUBLE_CARET_VARIANTS = [
  59. { begin: /\^{6}[0-9a-f]{6}/ },
  60. { begin: /\^{5}[0-9a-f]{5}/ },
  61. { begin: /\^{4}[0-9a-f]{4}/ },
  62. { begin: /\^{3}[0-9a-f]{3}/ },
  63. { begin: /\^{2}[0-9a-f]{2}/ },
  64. { begin: /\^{2}[\u0000-\u007f]/ }
  65. ];
  66. const CONTROL_SEQUENCE = {
  67. className: 'keyword',
  68. begin: /\\/,
  69. relevance: 0,
  70. contains: [
  71. {
  72. endsParent: true,
  73. begin: KNOWN_CONTROL_WORDS
  74. },
  75. {
  76. endsParent: true,
  77. begin: L3_REGEX
  78. },
  79. {
  80. endsParent: true,
  81. variants: DOUBLE_CARET_VARIANTS
  82. },
  83. {
  84. endsParent: true,
  85. relevance: 0,
  86. variants: L2_VARIANTS
  87. }
  88. ]
  89. };
  90. const MACRO_PARAM = {
  91. className: 'params',
  92. relevance: 0,
  93. begin: /#+\d?/
  94. };
  95. const DOUBLE_CARET_CHAR = {
  96. // relevance: 1
  97. variants: DOUBLE_CARET_VARIANTS };
  98. const SPECIAL_CATCODE = {
  99. className: 'built_in',
  100. relevance: 0,
  101. begin: /[$&^_]/
  102. };
  103. const MAGIC_COMMENT = {
  104. className: 'meta',
  105. begin: /% ?!(T[eE]X|tex|BIB|bib)/,
  106. end: '$',
  107. relevance: 10
  108. };
  109. const COMMENT = hljs.COMMENT(
  110. '%',
  111. '$',
  112. { relevance: 0 }
  113. );
  114. const EVERYTHING_BUT_VERBATIM = [
  115. CONTROL_SEQUENCE,
  116. MACRO_PARAM,
  117. DOUBLE_CARET_CHAR,
  118. SPECIAL_CATCODE,
  119. MAGIC_COMMENT,
  120. COMMENT
  121. ];
  122. const BRACE_GROUP_NO_VERBATIM = {
  123. begin: /\{/,
  124. end: /\}/,
  125. relevance: 0,
  126. contains: [
  127. 'self',
  128. ...EVERYTHING_BUT_VERBATIM
  129. ]
  130. };
  131. const ARGUMENT_BRACES = hljs.inherit(
  132. BRACE_GROUP_NO_VERBATIM,
  133. {
  134. relevance: 0,
  135. endsParent: true,
  136. contains: [
  137. BRACE_GROUP_NO_VERBATIM,
  138. ...EVERYTHING_BUT_VERBATIM
  139. ]
  140. }
  141. );
  142. const ARGUMENT_BRACKETS = {
  143. begin: /\[/,
  144. end: /\]/,
  145. endsParent: true,
  146. relevance: 0,
  147. contains: [
  148. BRACE_GROUP_NO_VERBATIM,
  149. ...EVERYTHING_BUT_VERBATIM
  150. ]
  151. };
  152. const SPACE_GOBBLER = {
  153. begin: /\s+/,
  154. relevance: 0
  155. };
  156. const ARGUMENT_M = [ ARGUMENT_BRACES ];
  157. const ARGUMENT_O = [ ARGUMENT_BRACKETS ];
  158. const ARGUMENT_AND_THEN = function(arg, starts_mode) {
  159. return {
  160. contains: [ SPACE_GOBBLER ],
  161. starts: {
  162. relevance: 0,
  163. contains: arg,
  164. starts: starts_mode
  165. }
  166. };
  167. };
  168. const CSNAME = function(csname, starts_mode) {
  169. return {
  170. begin: '\\\\' + csname + '(?![a-zA-Z@:_])',
  171. keywords: {
  172. $pattern: /\\[a-zA-Z]+/,
  173. keyword: '\\' + csname
  174. },
  175. relevance: 0,
  176. contains: [ SPACE_GOBBLER ],
  177. starts: starts_mode
  178. };
  179. };
  180. const BEGIN_ENV = function(envname, starts_mode) {
  181. return hljs.inherit(
  182. {
  183. begin: '\\\\begin(?=[ \t]*(\\r?\\n[ \t]*)?\\{' + envname + '\\})',
  184. keywords: {
  185. $pattern: /\\[a-zA-Z]+/,
  186. keyword: '\\begin'
  187. },
  188. relevance: 0,
  189. },
  190. ARGUMENT_AND_THEN(ARGUMENT_M, starts_mode)
  191. );
  192. };
  193. const VERBATIM_DELIMITED_EQUAL = (innerName = "string") => {
  194. return hljs.END_SAME_AS_BEGIN({
  195. className: innerName,
  196. begin: /(.|\r?\n)/,
  197. end: /(.|\r?\n)/,
  198. excludeBegin: true,
  199. excludeEnd: true,
  200. endsParent: true
  201. });
  202. };
  203. const VERBATIM_DELIMITED_ENV = function(envname) {
  204. return {
  205. className: 'string',
  206. end: '(?=\\\\end\\{' + envname + '\\})'
  207. };
  208. };
  209. const VERBATIM_DELIMITED_BRACES = (innerName = "string") => {
  210. return {
  211. relevance: 0,
  212. begin: /\{/,
  213. starts: {
  214. endsParent: true,
  215. contains: [
  216. {
  217. className: innerName,
  218. end: /(?=\})/,
  219. endsParent: true,
  220. contains: [
  221. {
  222. begin: /\{/,
  223. end: /\}/,
  224. relevance: 0,
  225. contains: [ "self" ]
  226. }
  227. ],
  228. }
  229. ]
  230. }
  231. };
  232. };
  233. const VERBATIM = [
  234. ...[
  235. 'verb',
  236. 'lstinline'
  237. ].map(csname => CSNAME(csname, { contains: [ VERBATIM_DELIMITED_EQUAL() ] })),
  238. CSNAME('mint', ARGUMENT_AND_THEN(ARGUMENT_M, { contains: [ VERBATIM_DELIMITED_EQUAL() ] })),
  239. CSNAME('mintinline', ARGUMENT_AND_THEN(ARGUMENT_M, { contains: [
  240. VERBATIM_DELIMITED_BRACES(),
  241. VERBATIM_DELIMITED_EQUAL()
  242. ] })),
  243. CSNAME('url', { contains: [
  244. VERBATIM_DELIMITED_BRACES("link"),
  245. VERBATIM_DELIMITED_BRACES("link")
  246. ] }),
  247. CSNAME('hyperref', { contains: [ VERBATIM_DELIMITED_BRACES("link") ] }),
  248. CSNAME('href', ARGUMENT_AND_THEN(ARGUMENT_O, { contains: [ VERBATIM_DELIMITED_BRACES("link") ] })),
  249. ...[].concat(...[
  250. '',
  251. '\\*'
  252. ].map(suffix => [
  253. BEGIN_ENV('verbatim' + suffix, VERBATIM_DELIMITED_ENV('verbatim' + suffix)),
  254. BEGIN_ENV('filecontents' + suffix, ARGUMENT_AND_THEN(ARGUMENT_M, VERBATIM_DELIMITED_ENV('filecontents' + suffix))),
  255. ...[
  256. '',
  257. 'B',
  258. 'L'
  259. ].map(prefix =>
  260. BEGIN_ENV(prefix + 'Verbatim' + suffix, ARGUMENT_AND_THEN(ARGUMENT_O, VERBATIM_DELIMITED_ENV(prefix + 'Verbatim' + suffix)))
  261. )
  262. ])),
  263. BEGIN_ENV('minted', ARGUMENT_AND_THEN(ARGUMENT_O, ARGUMENT_AND_THEN(ARGUMENT_M, VERBATIM_DELIMITED_ENV('minted')))),
  264. ];
  265. return {
  266. name: 'LaTeX',
  267. aliases: [ 'tex' ],
  268. contains: [
  269. ...VERBATIM,
  270. ...EVERYTHING_BUT_VERBATIM
  271. ]
  272. };
  273. }
  274. export { latex as default };