1
0

add initial marp implementation with sample content and build configuration

This commit is contained in:
2025-09-13 18:13:22 +02:00
parent dcacc9b409
commit e5f219507f
10319 changed files with 1402023 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
/*************************************************************
*
* Copyright (c) 2020-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Configuration file for the textmacros package
*
* @author dpvc@mathjax.org (Davide P. Cervone)
*/
import {TeX} from '../../tex.js';
import TexParser from '../TexParser.js';
import {Configuration, ParserConfiguration} from '../Configuration.js';
import ParseOptions from '../ParseOptions.js';
import {TagsFactory} from '../Tags.js';
import {StartItem, StopItem, MmlItem, StyleItem} from '../base/BaseItems.js';
import {TextParser} from './TextParser.js';
import {TextMacrosMethods} from './TextMacrosMethods.js';
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
import './TextMacrosMappings.js';
/**
* The base text macro configuration (used in the TextParser)
*/
export const TextBaseConfiguration = Configuration.create('text-base', {
parser: 'text',
handler: {
character: ['command', 'text-special'],
macro: ['text-macros']
},
fallback: {
//
// Unknown characters are added to the text verbatim
//
character: (parser: TextParser, c: string) => {
parser.text += c;
},
//
// For unknown macros, if they are defined in the main TeX parser
// and not string-replacement macros, give an error, otherwise
// run the macro (this either does the string replacement or
// produces the error as configured in the main TeX parser, so
// this will respect the noundefined package, if loaded).
//
macro: (parser: TextParser, name: string) => {
const texParser = parser.texParser;
const macro = texParser.lookup('macro', name);
if (macro && macro._func !== TextMacrosMethods.Macro) {
parser.Error('MathMacro', '%1 is only supported in math mode', '\\' + name);
}
texParser.parse('macro', [parser, name]);
}
},
items: {
[StartItem.prototype.kind]: StartItem,
[StopItem.prototype.kind]: StopItem,
[MmlItem.prototype.kind]: MmlItem,
[StyleItem.prototype.kind]: StyleItem // needed for \color
}
});
/**
* Replacement for ParseUtil.internalMath that handles text-mode macros.
*
* @param {TexParser} parser The TexParser calling this function
* @param {string} text The text-mode string to be processed
* @param {number|string} level The scriptlevel of the text
* @param {string} mathvariant The mathvariant for the text
* @return {MmlNode[]} The final MmlNode generated for the text
*/
function internalMath(parser: TexParser, text: string, level?: number | string, mathvariant?: string): MmlNode[] {
const config = parser.configuration.packageData.get('textmacros');
if (!(parser instanceof TextParser)) {
config.texParser = parser;
}
return [(new TextParser(text, mathvariant ? {mathvariant} : {}, config.parseOptions, level)).mml()];
}
//
// The textmacros package configuration
//
export const TextMacrosConfiguration = Configuration.create('textmacros', {
/**
* @param {ParserConfiguration} config The configuration object we are being configured within
* @param {TeX<any,any,any>} jax The TeX input jax in which we are running
*/
config(_config: ParserConfiguration, jax: TeX<any, any, any>) {
//
// Create the configuration and parseOptions objects for the
// internal TextParser and add the textBase configuration.
//
const textConf = new ParserConfiguration(jax.parseOptions.options.textmacros.packages, ['tex', 'text']);
textConf.init();
const parseOptions = new ParseOptions(textConf, []);
parseOptions.options = jax.parseOptions.options; // share the TeX options
textConf.config(jax);
TagsFactory.addTags(textConf.tags);
parseOptions.tags = TagsFactory.getDefault();
parseOptions.tags.configuration = parseOptions;
//
// Share the TeX input jax's parseOptions packageData object
// so that require and other packages will work in both parsers,
// set the textmacros data (texParser will be filled in later),
// and replace the internalMath function with our own.
//
parseOptions.packageData = jax.parseOptions.packageData;
parseOptions.packageData.set('textmacros', {parseOptions, jax, texParser: null});
parseOptions.options.internalMath = internalMath;
},
preprocessors: [(data: {data: ParseOptions}) => {
//
// Set the MmlFactory for the nodeFactory, since it was not available
// durring configuration above.
//
const config = data.data.packageData.get('textmacros');
config.parseOptions.nodeFactory.setMmlFactory(config.jax.mmlFactory);
}],
options: {
textmacros: {
packages: ['text-base'] // textmacro packages to load
}
}
});

View File

@@ -0,0 +1,151 @@
/*************************************************************
*
* Copyright (c) 2020-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Character and Macro mappings for the textmacros package
*
* @author dpvc@mathjax.org (Davide P. Cervone)
*/
import {MacroMap, CommandMap} from '../SymbolMap.js';
import {TexConstant} from '../TexConstants.js';
import {TextMacrosMethods} from './TextMacrosMethods.js';
import {MATHSPACE} from '../../../util/lengths.js';
//
// The special characters in text-mode
//
new MacroMap('text-special', {
'$': 'Math',
'%': 'Comment',
'^': 'MathModeOnly',
'_': 'MathModeOnly',
'&': 'Misplaced',
'#': 'Misplaced',
'~': 'Tilde',
' ': 'Space',
'\t': 'Space',
'\r': 'Space',
'\n': 'Space',
'\u00A0': 'Tilde',
'{': 'OpenBrace',
'}': 'CloseBrace',
'`': 'OpenQuote',
'\'': 'CloseQuote'
}, TextMacrosMethods);
//
// The text-mode macro mappings
//
new CommandMap('text-macros', {
'(': 'Math',
'$': 'SelfQuote',
'_': 'SelfQuote',
'%': 'SelfQuote',
'{': 'SelfQuote',
'}': 'SelfQuote',
' ': 'SelfQuote',
'&': 'SelfQuote',
'#': 'SelfQuote',
'\\': 'SelfQuote',
'\'': ['Accent', '\u00B4'],
'\u2019': ['Accent', '\u00B4'],
'`': ['Accent', '\u0060'],
'\u2018': ['Accent', '\u0060'],
'^': ['Accent', '^'],
'\"': ['Accent', '\u00A8'],
'~': ['Accent', '~'],
'=': ['Accent', '\u00AF'],
'.': ['Accent', '\u02D9'],
'u': ['Accent', '\u02D8'],
'v': ['Accent', '\u02C7'],
emph: 'Emph',
rm: ['SetFont', TexConstant.Variant.NORMAL],
mit: ['SetFont', TexConstant.Variant.ITALIC],
oldstyle: ['SetFont', TexConstant.Variant.OLDSTYLE],
cal: ['SetFont', TexConstant.Variant.CALLIGRAPHIC],
it: ['SetFont', '-tex-mathit'], // needs special handling
bf: ['SetFont', TexConstant.Variant.BOLD],
bbFont: ['SetFont', TexConstant.Variant.DOUBLESTRUCK],
scr: ['SetFont', TexConstant.Variant.SCRIPT],
frak: ['SetFont', TexConstant.Variant.FRAKTUR],
sf: ['SetFont', TexConstant.Variant.SANSSERIF],
tt: ['SetFont', TexConstant.Variant.MONOSPACE],
tiny: ['SetSize', 0.5],
Tiny: ['SetSize', 0.6], // non-standard
scriptsize: ['SetSize', 0.7],
small: ['SetSize', 0.85],
normalsize: ['SetSize', 1.0],
large: ['SetSize', 1.2],
Large: ['SetSize', 1.44],
LARGE: ['SetSize', 1.73],
huge: ['SetSize', 2.07],
Huge: ['SetSize', 2.49],
Bbb: ['Macro', '{\\bbFont #1}', 1],
textnormal: ['Macro', '{\\rm #1}', 1],
textup: ['Macro', '{\\rm #1}', 1],
textrm: ['Macro', '{\\rm #1}', 1],
textit: ['Macro', '{\\it #1}', 1],
textbf: ['Macro', '{\\bf #1}', 1],
textsf: ['Macro', '{\\sf #1}', 1],
texttt: ['Macro', '{\\tt #1}', 1],
dagger: ['Insert', '\u2020'],
ddagger: ['Insert', '\u2021'],
S: ['Insert', '\u00A7'],
',': ['Spacer', MATHSPACE.thinmathspace],
':': ['Spacer', MATHSPACE.mediummathspace],
'>': ['Spacer', MATHSPACE.mediummathspace],
';': ['Spacer', MATHSPACE.thickmathspace],
'!': ['Spacer', MATHSPACE.negativethinmathspace],
enspace: ['Spacer', .5],
quad: ['Spacer', 1],
qquad: ['Spacer', 2],
thinspace: ['Spacer', MATHSPACE.thinmathspace],
negthinspace: ['Spacer', MATHSPACE.negativethinmathspace],
hskip: 'Hskip',
hspace: 'Hskip',
kern: 'Hskip',
mskip: 'Hskip',
mspace: 'Hskip',
mkern: 'Hskip',
rule: 'rule',
Rule: ['Rule'],
Space: ['Rule', 'blank'],
color: 'CheckAutoload',
textcolor: 'CheckAutoload',
colorbox: 'CheckAutoload',
fcolorbox: 'CheckAutoload',
href: 'CheckAutoload',
style: 'CheckAutoload',
class: 'CheckAutoload',
cssId: 'CheckAutoload',
unicode: 'CheckAutoload',
ref: ['HandleRef', false],
eqref: ['HandleRef', true],
}, TextMacrosMethods);

View File

@@ -0,0 +1,287 @@
/*************************************************************
*
* Copyright (c) 2020-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Method definitions for the textmacros package
*
* @author dpvc@mathjax.org (Davide P. Cervone)
*/
import TexParser from '../TexParser.js';
import {retryAfter} from '../../../util/Retries.js';
import {TextParser} from './TextParser.js';
import BaseMethods from '../base/BaseMethods.js';
/**
* The methods used to implement the text-mode macros
*/
export const TextMacrosMethods = {
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
Comment(parser: TextParser, _c: string) {
while (parser.i < parser.string.length && parser.string.charAt(parser.i) !== '\n') {
parser.i++;
}
parser.i++;
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} open The delimiter used to open math-mode in text-mode
*/
Math(parser: TextParser, open: string) {
parser.saveText();
let i = parser.i;
let j, c;
let braces = 0;
//
// Loop through the string looking for the closing delimiter, while matching braces
//
while ((c = parser.GetNext())) {
j = parser.i++;
switch (c) {
case '\\':
const cs = parser.GetCS();
if (cs === ')') c = '\\('; // \( is the opening delimiter for \)
case '$':
//
// If there are no unbalanced braces and we have found the close delimiter,
// process the contents of the delimiters in math mode (using the original TeX parser)
//
if (braces === 0 && open === c) {
const config = parser.texParser.configuration;
const mml = (new TexParser(parser.string.substr(i, j - i), parser.stack.env, config)).mml();
parser.PushMath(mml);
return;
}
break;
case '{':
braces++;
break;
case '}':
if (braces === 0) {
parser.Error('ExtraCloseMissingOpen', 'Extra close brace or missing open brace');
}
braces--;
break;
}
}
parser.Error('MathNotTerminated', 'Math-mode is not properly terminated');
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
MathModeOnly(parser: TextParser, c: string) {
parser.Error('MathModeOnly', '\'%1\' allowed only in math mode', c);
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
Misplaced(parser: TextParser, c: string) {
parser.Error('Misplaced', '\'%1\' can not be used here', c);
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
OpenBrace(parser: TextParser, _c: string) {
//
// Save the current stack environment and make a copy of it for
// use within the braced group.
//
const env = parser.stack.env;
parser.envStack.push(env);
parser.stack.env = Object.assign({}, env);
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
CloseBrace(parser: TextParser, _c: string) {
//
// Restore the saved stack environment, if there was one
//
if (parser.envStack.length) {
parser.saveText();
parser.stack.env = parser.envStack.pop();
} else {
parser.Error('ExtraCloseMissingOpen', 'Extra close brace or missing open brace');
}
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
OpenQuote(parser: TextParser, c: string) {
//
// Handle smart open quotes
//
if (parser.string.charAt(parser.i) === c) {
parser.text += '\u201C';
parser.i++;
} else {
parser.text += '\u2018';
}
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
CloseQuote(parser: TextParser, c: string) {
//
// Handle smart close quotes
//
if (parser.string.charAt(parser.i) === c) {
parser.text += '\u201D';
parser.i++;
} else {
parser.text += '\u2019';
}
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
Tilde(parser: TextParser, _c: string) {
parser.text += '\u00A0'; // non-breaking space
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} c The character that called this function
*/
Space(parser: TextParser, _c: string) {
parser.text += ' '; // regular space, but skipping multiple spaces
while (parser.GetNext().match(/\s/)) parser.i++;
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} name The control sequence that called this function
*/
SelfQuote(parser: TextParser, name: string) {
parser.text += name.substr(1); // add in the quoted character
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} name The control sequence that called this function
* @param {string} c The character to insert into the string
*/
Insert(parser: TextParser, _name: string, c: string) {
parser.text += c;
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} name The control sequence that called this function
* @param {string} c The character to insert into the string
*/
Accent(parser: TextParser, name: string, c: string) {
//
// Create an accented character using mover
//
const base = parser.ParseArg(name);
const accent = parser.create('token', 'mo', {}, c);
parser.addAttributes(accent);
parser.Push(parser.create('node', 'mover', [base, accent]));
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} name The control sequence that called this function
*/
Emph(parser: TextParser, name: string) {
//
// Switch to/from italics
//
const variant = (parser.stack.env.mathvariant === '-tex-mathit' ? 'normal' : '-tex-mathit');
parser.Push(parser.ParseTextArg(name, {mathvariant: variant}));
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} name The control sequence that called this function
* @param {string} variant The font variant to use from now on
*/
SetFont(parser: TextParser, _name: string, variant: string) {
parser.saveText();
parser.stack.env.mathvariant = variant;
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} name The control sequence that called this function
* @param {number} size The font size to use from now on
*/
SetSize(parser: TextParser, _name: string, size: number) {
parser.saveText();
parser.stack.env.mathsize = size;
},
/**
* @param {TextParser} parser The text-mode parser
* @param {string} name The control sequence that called this function
*/
CheckAutoload(parser: TextParser, name: string) {
const autoload = parser.configuration.packageData.get('autoload');
const texParser = parser.texParser;
name = name.slice(1);
//
// Check if a macro is undefined, or currently set to autoload an extension.
// If so, process the macro in the original TexParser:
// This will handle the undefined macro using the TeX parser's configuration, then return,
// or will cause the autoloaded extension to load or be processed and restart the expression.
// Otherwise, process the macro in text mode.
//
const macro = texParser.lookup('macro', name);
if (!macro || (autoload && macro._func === autoload.Autoload)) {
texParser.parse('macro', [texParser, name]);
if (!macro) return;
retryAfter(Promise.resolve());
}
texParser.parse('macro', [parser, name]);
},
//
// Copy some methods from the base package
//
Macro: BaseMethods.Macro,
Spacer: BaseMethods.Spacer,
Hskip: BaseMethods.Hskip,
rule: BaseMethods.rule,
Rule: BaseMethods.Rule,
HandleRef: BaseMethods.HandleRef
};

View File

@@ -0,0 +1,209 @@
/*************************************************************
*
* Copyright (c) 2020-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview The TextParser class for the textmacros package
*
* @author dpvc@mathjax.org (Davide P. Cervone)
*/
import TexParser from '../TexParser.js';
import TexError from '../TexError.js';
import ParseOptions from '../ParseOptions.js';
import ParseUtil from '../ParseUtil.js';
import {StackItem} from '../StackItem.js';
import {MmlNode, AbstractMmlNode} from '../../../core/MmlTree/MmlNode.js';
import {EnvList} from '../StackItem.js';
import NodeUtil from '../NodeUtil.js';
import {StopItem, StyleItem} from '../base/BaseItems.js';
/**
* Subclass of the TexParser but for handling text-mode material
*/
export class TextParser extends TexParser {
/**
* The accumulated text material to go into an mtext element
*/
public text: string;
/**
* Saved stack environments for processing braces
*/
public envStack: EnvList[];
/**
* The scriptlevel of this text-mode material
*/
public level: number | string | undefined;
/**
* The accumulated MmlNodes generated by parsing the text-mode material
*/
protected nodes: MmlNode[];
/**
* Short-hand for obtaining the saved TexParser
*/
public get texParser() {
return this.configuration.packageData.get('textmacros').texParser;
}
/**
* @override
*/
public get tags() {
return this.texParser.tags;
}
/**
* @override
* @constructor
*/
constructor(text: string, env: EnvList, configuration: ParseOptions, level?: number | string) {
super(text, env, configuration);
this.level = level;
}
/**
* Make sure we only return one element (wrap multiple ones in an mrow or mstyle, as needed).
*
* @override
*/
public mml() {
return (this.level != null ?
this.create('node', 'mstyle', this.nodes, {displaystyle: false, scriptlevel: this.level}) :
this.nodes.length === 1 ? this.nodes[0] : this.create('node', 'mrow', this.nodes));
}
/**
* @override
*/
public Parse() {
this.text = '';
this.nodes = [];
this.envStack = [];
super.Parse();
}
/**
* Creates an mtext element for the saved text and pushes that onto the node list
*/
public saveText() {
if (this.text) {
const mathvariant = this.stack.env.mathvariant;
const text = ParseUtil.internalText(this, this.text, mathvariant ? {mathvariant} : {});
this.text = '';
this.Push(text);
}
}
/**
* @override
*/
public Push(mml: MmlNode | StackItem) {
if (this.text) {
this.saveText();
}
if (mml instanceof StopItem) {
return super.Push(mml);
}
if (mml instanceof StyleItem) {
this.stack.env.mathcolor = this.stack.env.color;
return;
}
if (mml instanceof AbstractMmlNode) {
this.addAttributes(mml);
this.nodes.push(mml);
}
}
/**
* Push some math into the node list, adding mathsize and mathcolor
* if specified in the environment.
*
* @param {MmlNode} mml The math nodes to push
*/
public PushMath(mml: MmlNode) {
const env = this.stack.env;
if (!mml.isKind('TeXAtom')) {
mml = this.create('node', 'TeXAtom', [mml]); // make sure the math is an ORD
}
for (const name of ['mathsize', 'mathcolor']) {
if (env[name] && !mml.attributes.getExplicit(name)) {
if (!mml.isToken && !mml.isKind('mstyle')) {
mml = this.create('node', 'mstyle', [mml]);
}
NodeUtil.setAttribute(mml, name, env[name]);
}
}
if (mml.isInferred) {
mml = this.create('node', 'mrow', mml.childNodes);
}
this.nodes.push(mml);
}
/**
* Add mathsize, mathcolor, and mathvariant to token nodes,
* if they are specified in the environment
*
* @param {MmlNode} mml The node to adjust
*/
public addAttributes(mml: MmlNode) {
const env = this.stack.env;
if (!mml.isToken) return;
for (const name of ['mathsize', 'mathcolor', 'mathvariant']) {
if (env[name] && !mml.attributes.getExplicit(name)) {
NodeUtil.setAttribute(mml, name, env[name]);
}
}
}
/**
* Process the argument as text with the given environment settings
*
* @param {string} name The macro that is calling for a parameter
* @param {EnvList} env The environment to use
*/
public ParseTextArg(name: string, env: EnvList) {
const text = this.GetArgument(name);
env = Object.assign(Object.assign({}, this.stack.env), env);
return (new TextParser(text, env, this.configuration)).mml();
}
/**
* Process an argument as text rather than math
*
* @override
*/
public ParseArg(name: string) {
return (new TextParser(this.GetArgument(name), this.stack.env, this.configuration)).mml();
}
/**
* Throw an error
*
* @param {string} id The id for the message string
* @param {string} message The English version of the message
* @param {string[]} args Any substitution args for the message
*/
public Error(id: string, message: string, ...args: string[]) {
throw new TexError(id, message, ...args);
}
}