Skip to content

Commit

Permalink
feat: Math syntax enabled default
Browse files Browse the repository at this point in the history
  • Loading branch information
akabekobeko committed May 1, 2021
1 parent 904f3f2 commit 408a623
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 36 deletions.
17 changes: 10 additions & 7 deletions docs/vfm.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,11 +332,13 @@ section.author {

Outputs HTML processed by [MathJax](https://www.mathjax.org/).

It is disabled by default. It is activated by satisfying one of the following.
It is Enabled by default. To disable it, specify the following.

- VFM options: `math: true`
- CLI options: `--math`
- Frontmatter: `math: true` (Priority over others)
- `stringify` API options: `math: false`
- `VFM` API options: `math: false`
- CLI options: `--disable-math`
- Frontmatter: `math: false`
- It takes precedence over `stringify`, but` VFM` does not.

The VFM syntax for MathJax inline is `$...$` and the display is `$$...$$`.

Expand Down Expand Up @@ -366,7 +368,7 @@ display: $$1 + 1 = 2$$

**HTML**

It also outputs the `<script>` and `<body>` attributes for processing MathJax in Vivliostyle if `math` is enabled.
It also outputs the `<script>` and `<body>` attributes for processing MathJax in Vivliostyle if `math` is enabled. However, even if the math syntax is valid, it will not be output if it does not actually exist.

```html
<html>
Expand Down Expand Up @@ -424,8 +426,9 @@ The priority of `title` is as follows.

The priority of `math` is as follows.

1. `math` property of the frontmatter
2. `math` option of VFM
1. `math` option of `VFM` API
2. `math` property of the frontmatter
3. `math` option of `stringify` API

**class**

Expand Down
8 changes: 4 additions & 4 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const cli = meow(
--language Document language (ignored in partial mode)
--hard-line-breaks Add <br> at the position of hard line breaks, without needing spaces
--disable-format-html Disable automatic HTML format
--math Enable math syntax
--disable-math Disable math syntax
Examples
$ vfm input.md
Expand Down Expand Up @@ -46,7 +46,7 @@ const cli = meow(
disableFormatHtml: {
type: 'boolean',
},
math: {
disableMath: {
type: 'boolean',
},
},
Expand All @@ -63,7 +63,7 @@ function compile(input: string) {
language: cli.flags.language,
hardLineBreaks: cli.flags.hardLineBreaks,
disableFormatHtml: cli.flags.disableFormatHtml,
math: cli.flags.math,
math: cli.flags.disableMath === undefined ? true : !cli.flags.disableMath,
}),
);
}
Expand All @@ -76,7 +76,7 @@ function main(
language: { type: 'string' };
hardLineBreaks: { type: 'boolean' };
disableFormatHtml: { type: 'boolean' };
math: { type: 'boolean' };
disableMath: { type: 'boolean' };
}>,
) {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function VFM({
replace = undefined,
hardLineBreaks = false,
disableFormatHtml = false,
math = false,
math = true,
}: StringifyMarkdownOptions = {}): Processor {
const processor = unified()
.use(markdown(hardLineBreaks, math))
Expand Down
59 changes: 37 additions & 22 deletions src/plugins/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,24 @@ import visit from 'unist-util-visit';
* - OK: `$x = y$`, `$x = \$y$`
* - NG: `$$x = y$`, `$x = y$$`, `$ x = y$`, `$x = y $`, `$x = y$7`
*/
const regexpInline = /\$([^$\s].*?(?<=[^\\$\s]|[^\\](?:\\\\)+))\$(?!\$|\d)/gs;
const REGEXP_INLINE = /\$([^$\s].*?(?<=[^\\$\s]|[^\\](?:\\\\)+))\$(?!\$|\d)/gs;

/** Display math format, e.g. `$$...$$`. */
const regexpDisplay = /\$\$([^$].*?(?<=[^$]))\$\$(?!\$)/gs;
const REGEXP_DISPLAY = /\$\$([^$].*?(?<=[^$]))\$\$(?!\$)/gs;

/** Type of inline math in Markdown AST. */
const typeInline = 'inlineMath';
const TYPE_INLINE = 'inlineMath';

/** Type of display math in Markdown AST. */
const typeDisplay = 'displayMath';
const TYPE_DISPLAY = 'displayMath';

/** URL of MathJax v2 supported by Vivliostyle. */
const mathUrl =
const MATH_URL =
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-MML-AM_CHTML';

/** The flag indicates that math syntax was actually processed. */
let MATH_PROCESSED = false;

/**
* Create tokenizers for remark-parse.
* @returns Tokenizers.
Expand All @@ -40,7 +43,7 @@ const createTokenizers = () => {
return;
}

const match = new RegExp(regexpInline).exec(value);
const match = new RegExp(REGEXP_INLINE).exec(value);
if (!match) {
return;
}
Expand All @@ -55,9 +58,9 @@ const createTokenizers = () => {
now.offset += 1;

return eat(eaten)({
type: typeInline,
type: TYPE_INLINE,
children: [],
data: { hName: typeInline, value: valueText },
data: { hName: TYPE_INLINE, value: valueText },
});
};

Expand All @@ -71,7 +74,7 @@ const createTokenizers = () => {
return;
}

const match = new RegExp(regexpDisplay).exec(value);
const match = new RegExp(REGEXP_DISPLAY).exec(value);
if (!match) {
return;
}
Expand All @@ -86,9 +89,9 @@ const createTokenizers = () => {
now.offset += 1;

return eat(eaten)({
type: typeDisplay,
type: TYPE_DISPLAY,
children: [],
data: { hName: typeDisplay, value: valueText },
data: { hName: TYPE_DISPLAY, value: valueText },
});
};

Expand All @@ -105,6 +108,8 @@ const createTokenizers = () => {
* @returns Transformer or undefined (less than remark 13).
*/
export const mdast: Plugin = function (): Transformer | undefined {
MATH_PROCESSED = false;

// For less than remark 13 with exclusive other markdown syntax
if (
this.Parser &&
Expand All @@ -113,30 +118,30 @@ export const mdast: Plugin = function (): Transformer | undefined {
) {
const { inlineTokenizers, inlineMethods } = this.Parser.prototype;
const tokenizers = createTokenizers();
inlineTokenizers[typeInline] = tokenizers.tokenizerInlineMath;
inlineTokenizers[typeDisplay] = tokenizers.tokenizerDisplayMath;
inlineMethods.splice(inlineMethods.indexOf('text'), 0, typeInline);
inlineMethods.splice(inlineMethods.indexOf('text'), 0, typeDisplay);
inlineTokenizers[TYPE_INLINE] = tokenizers.tokenizerInlineMath;
inlineTokenizers[TYPE_DISPLAY] = tokenizers.tokenizerDisplayMath;
inlineMethods.splice(inlineMethods.indexOf('text'), 0, TYPE_INLINE);
inlineMethods.splice(inlineMethods.indexOf('text'), 0, TYPE_DISPLAY);
return;
}

return (tree: Node) => {
findReplace(tree, regexpInline, (_: string, valueText: string) => {
findReplace(tree, REGEXP_INLINE, (_: string, valueText: string) => {
return {
type: typeInline,
type: TYPE_INLINE,
data: {
hName: typeInline,
hName: TYPE_INLINE,
value: valueText,
},
children: [],
};
});

findReplace(tree, regexpDisplay, (_: string, valueText: string) => {
findReplace(tree, REGEXP_DISPLAY, (_: string, valueText: string) => {
return {
type: typeDisplay,
type: TYPE_DISPLAY,
data: {
hName: typeDisplay,
hName: TYPE_DISPLAY,
value: valueText,
},
children: [],
Expand All @@ -156,6 +161,8 @@ export const handlerInlineMath: Handler = (h, node: Node) => {
node.data = {};
}

MATH_PROCESSED = true;

return h(
{
type: 'element',
Expand All @@ -177,6 +184,8 @@ export const handlerInlineMath: Handler = (h, node: Node) => {
export const handlerDisplayMath: Handler = (h, node: Node) => {
if (!node.data) node.data = {};

MATH_PROCESSED = true;

return h(
{
type: 'element',
Expand All @@ -194,6 +203,12 @@ export const handlerDisplayMath: Handler = (h, node: Node) => {
* Set the `<script>` to load MathJax and `<body>` attribute that enable math typesetting.
*/
export const hast = () => (tree: Node) => {
if (!MATH_PROCESSED) {
return;
}

MATH_PROCESSED = false;

visit<Element>(tree, 'element', (node) => {
switch (node.tagName) {
case 'head':
Expand All @@ -202,7 +217,7 @@ export const hast = () => (tree: Node) => {
tagName: 'script',
properties: {
async: true,
src: mathUrl,
src: MATH_URL,
},
children: [],
});
Expand Down
34 changes: 32 additions & 2 deletions tests/math.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { stringify } from '../src/index';
const options = {
partial: true,
disableFormatHtml: true,
math: true,
};

it('inline', () => {
Expand Down Expand Up @@ -166,7 +165,38 @@ it('HTML head and body', () => {
expect(received).toBe(expected);
});

it('disable', () => {
it('math syntax does not exist, without <script> and <body> attribute', () => {
const md = 'Sample';
const received = stringify(md, {
math: false,
disableFormatHtml: true,
});
const expected = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>Sample</p>
</body>
</html>
`;
expect(received).toBe(expected);
});

it('disable with options', () => {
const md = '$x = y$';
const received = stringify(md, {
math: false,
partial: true,
disableFormatHtml: true,
});
const expected = '<p>$x = y$</p>';
expect(received).toBe(expected);
});

it('disable with frontmatter, override options', () => {
const markdown = `---
math: false
---
Expand Down

0 comments on commit 408a623

Please sign in to comment.