TextMacrosMethods.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2020-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Method definitions for the textmacros package
  19. *
  20. * @author dpvc@mathjax.org (Davide P. Cervone)
  21. */
  22. import TexParser from '../TexParser.js';
  23. import {retryAfter} from '../../../util/Retries.js';
  24. import {TextParser} from './TextParser.js';
  25. import BaseMethods from '../base/BaseMethods.js';
  26. /**
  27. * The methods used to implement the text-mode macros
  28. */
  29. export const TextMacrosMethods = {
  30. /**
  31. * @param {TextParser} parser The text-mode parser
  32. * @param {string} c The character that called this function
  33. */
  34. Comment(parser: TextParser, _c: string) {
  35. while (parser.i < parser.string.length && parser.string.charAt(parser.i) !== '\n') {
  36. parser.i++;
  37. }
  38. parser.i++;
  39. },
  40. /**
  41. * @param {TextParser} parser The text-mode parser
  42. * @param {string} open The delimiter used to open math-mode in text-mode
  43. */
  44. Math(parser: TextParser, open: string) {
  45. parser.saveText();
  46. let i = parser.i;
  47. let j, c;
  48. let braces = 0;
  49. //
  50. // Loop through the string looking for the closing delimiter, while matching braces
  51. //
  52. while ((c = parser.GetNext())) {
  53. j = parser.i++;
  54. switch (c) {
  55. case '\\':
  56. const cs = parser.GetCS();
  57. if (cs === ')') c = '\\('; // \( is the opening delimiter for \)
  58. case '$':
  59. //
  60. // If there are no unbalanced braces and we have found the close delimiter,
  61. // process the contents of the delimiters in math mode (using the original TeX parser)
  62. //
  63. if (braces === 0 && open === c) {
  64. const config = parser.texParser.configuration;
  65. const mml = (new TexParser(parser.string.substr(i, j - i), parser.stack.env, config)).mml();
  66. parser.PushMath(mml);
  67. return;
  68. }
  69. break;
  70. case '{':
  71. braces++;
  72. break;
  73. case '}':
  74. if (braces === 0) {
  75. parser.Error('ExtraCloseMissingOpen', 'Extra close brace or missing open brace');
  76. }
  77. braces--;
  78. break;
  79. }
  80. }
  81. parser.Error('MathNotTerminated', 'Math-mode is not properly terminated');
  82. },
  83. /**
  84. * @param {TextParser} parser The text-mode parser
  85. * @param {string} c The character that called this function
  86. */
  87. MathModeOnly(parser: TextParser, c: string) {
  88. parser.Error('MathModeOnly', '\'%1\' allowed only in math mode', c);
  89. },
  90. /**
  91. * @param {TextParser} parser The text-mode parser
  92. * @param {string} c The character that called this function
  93. */
  94. Misplaced(parser: TextParser, c: string) {
  95. parser.Error('Misplaced', '\'%1\' can not be used here', c);
  96. },
  97. /**
  98. * @param {TextParser} parser The text-mode parser
  99. * @param {string} c The character that called this function
  100. */
  101. OpenBrace(parser: TextParser, _c: string) {
  102. //
  103. // Save the current stack environment and make a copy of it for
  104. // use within the braced group.
  105. //
  106. const env = parser.stack.env;
  107. parser.envStack.push(env);
  108. parser.stack.env = Object.assign({}, env);
  109. },
  110. /**
  111. * @param {TextParser} parser The text-mode parser
  112. * @param {string} c The character that called this function
  113. */
  114. CloseBrace(parser: TextParser, _c: string) {
  115. //
  116. // Restore the saved stack environment, if there was one
  117. //
  118. if (parser.envStack.length) {
  119. parser.saveText();
  120. parser.stack.env = parser.envStack.pop();
  121. } else {
  122. parser.Error('ExtraCloseMissingOpen', 'Extra close brace or missing open brace');
  123. }
  124. },
  125. /**
  126. * @param {TextParser} parser The text-mode parser
  127. * @param {string} c The character that called this function
  128. */
  129. OpenQuote(parser: TextParser, c: string) {
  130. //
  131. // Handle smart open quotes
  132. //
  133. if (parser.string.charAt(parser.i) === c) {
  134. parser.text += '\u201C';
  135. parser.i++;
  136. } else {
  137. parser.text += '\u2018';
  138. }
  139. },
  140. /**
  141. * @param {TextParser} parser The text-mode parser
  142. * @param {string} c The character that called this function
  143. */
  144. CloseQuote(parser: TextParser, c: string) {
  145. //
  146. // Handle smart close quotes
  147. //
  148. if (parser.string.charAt(parser.i) === c) {
  149. parser.text += '\u201D';
  150. parser.i++;
  151. } else {
  152. parser.text += '\u2019';
  153. }
  154. },
  155. /**
  156. * @param {TextParser} parser The text-mode parser
  157. * @param {string} c The character that called this function
  158. */
  159. Tilde(parser: TextParser, _c: string) {
  160. parser.text += '\u00A0'; // non-breaking space
  161. },
  162. /**
  163. * @param {TextParser} parser The text-mode parser
  164. * @param {string} c The character that called this function
  165. */
  166. Space(parser: TextParser, _c: string) {
  167. parser.text += ' '; // regular space, but skipping multiple spaces
  168. while (parser.GetNext().match(/\s/)) parser.i++;
  169. },
  170. /**
  171. * @param {TextParser} parser The text-mode parser
  172. * @param {string} name The control sequence that called this function
  173. */
  174. SelfQuote(parser: TextParser, name: string) {
  175. parser.text += name.substr(1); // add in the quoted character
  176. },
  177. /**
  178. * @param {TextParser} parser The text-mode parser
  179. * @param {string} name The control sequence that called this function
  180. * @param {string} c The character to insert into the string
  181. */
  182. Insert(parser: TextParser, _name: string, c: string) {
  183. parser.text += c;
  184. },
  185. /**
  186. * @param {TextParser} parser The text-mode parser
  187. * @param {string} name The control sequence that called this function
  188. * @param {string} c The character to insert into the string
  189. */
  190. Accent(parser: TextParser, name: string, c: string) {
  191. //
  192. // Create an accented character using mover
  193. //
  194. const base = parser.ParseArg(name);
  195. const accent = parser.create('token', 'mo', {}, c);
  196. parser.addAttributes(accent);
  197. parser.Push(parser.create('node', 'mover', [base, accent]));
  198. },
  199. /**
  200. * @param {TextParser} parser The text-mode parser
  201. * @param {string} name The control sequence that called this function
  202. */
  203. Emph(parser: TextParser, name: string) {
  204. //
  205. // Switch to/from italics
  206. //
  207. const variant = (parser.stack.env.mathvariant === '-tex-mathit' ? 'normal' : '-tex-mathit');
  208. parser.Push(parser.ParseTextArg(name, {mathvariant: variant}));
  209. },
  210. /**
  211. * @param {TextParser} parser The text-mode parser
  212. * @param {string} name The control sequence that called this function
  213. * @param {string} variant The font variant to use from now on
  214. */
  215. SetFont(parser: TextParser, _name: string, variant: string) {
  216. parser.saveText();
  217. parser.stack.env.mathvariant = variant;
  218. },
  219. /**
  220. * @param {TextParser} parser The text-mode parser
  221. * @param {string} name The control sequence that called this function
  222. * @param {number} size The font size to use from now on
  223. */
  224. SetSize(parser: TextParser, _name: string, size: number) {
  225. parser.saveText();
  226. parser.stack.env.mathsize = size;
  227. },
  228. /**
  229. * @param {TextParser} parser The text-mode parser
  230. * @param {string} name The control sequence that called this function
  231. */
  232. CheckAutoload(parser: TextParser, name: string) {
  233. const autoload = parser.configuration.packageData.get('autoload');
  234. const texParser = parser.texParser;
  235. name = name.slice(1);
  236. //
  237. // Check if a macro is undefined, or currently set to autoload an extension.
  238. // If so, process the macro in the original TexParser:
  239. // This will handle the undefined macro using the TeX parser's configuration, then return,
  240. // or will cause the autoloaded extension to load or be processed and restart the expression.
  241. // Otherwise, process the macro in text mode.
  242. //
  243. const macro = texParser.lookup('macro', name);
  244. if (!macro || (autoload && macro._func === autoload.Autoload)) {
  245. texParser.parse('macro', [texParser, name]);
  246. if (!macro) return;
  247. retryAfter(Promise.resolve());
  248. }
  249. texParser.parse('macro', [parser, name]);
  250. },
  251. //
  252. // Copy some methods from the base package
  253. //
  254. Macro: BaseMethods.Macro,
  255. Spacer: BaseMethods.Spacer,
  256. Hskip: BaseMethods.Hskip,
  257. rule: BaseMethods.rule,
  258. Rule: BaseMethods.Rule,
  259. HandleRef: BaseMethods.HandleRef
  260. };