Skip to content

Commit

Permalink
feat: implemented MathJax multi-line specification, pre/post-conditio…
Browse files Browse the repository at this point in the history
…ns for start and end symbols, and escape
  • Loading branch information
akabekobeko committed Apr 23, 2021
1 parent bcffbda commit 2611e92
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 15 deletions.
22 changes: 19 additions & 3 deletions docs/vfm.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,28 @@ It is disabled by default. It is activated by satisfying one of the following.
- CLI options: `--math`
- Frontmatter: `math: true` (Priority over others)

**VFM**

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

It also supports multiple lines, such as `$x = y\n1 + 1 = 2$` and `$$\nx = y\n$$`. However, if there is a blank line `\n\n` such as `$x = y\n\n1 + 1 = 2$ `, the paragraphs will be separated and it will not be a mathematical syntax.

OK:

- `$...$`, `$$...$$` ...Range specification matches
- `$...\n...$`, `$\n...\n$` ...Within the same paragraph
- `$...\$...$`, `$$...\$...$$` ...Escape `$` by `\`

NG:

- `$...$$`, `$$...$` ...Range specification does not match
- `$...\n\n...$`, `$$...\n\n...$$` ...Split paragraph
- `$ ...$` ...Space ` ` immediately after `$` at start of inline
- `$... $` ...Space ` ` immediately before `$` at end of inline
- `$...$5` ...Digit `0...N` immediately after `$` at end of inline

**VFM**

```markdown
inline: $x = y$
inline:$x = y$

display: $$1 + 1 = 2$$
```
Expand Down
16 changes: 12 additions & 4 deletions src/plugins/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { Node } from 'unist';
import u from 'unist-builder';
import visit from 'unist-util-visit';

/** Inline math format, e.g. `$...$`. */
const regexpInline = /\$([^$].*?[^$])\$(?!\$)/g;
/**
* Inline math format, e.g. `$...$`.
* - OK: `$x = y$`, `$x = \$y$`
* - NG: `$$x = y$`, `$x = y$$`, `$ x = y$`, `$x = y $`, `$x = y$7`
*/
const regexpInline = /\$([^($| )].*?[^(\\|$| )])\$(?!(\$|\d))/gs;

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

/** Type of inline math in Markdown AST. */
const typeInline = 'inlineMath';
Expand All @@ -28,7 +32,11 @@ const mathUrl =
*/
const createTokenizers = () => {
const tokenizerInlineMath: Tokenizer = function (eat, value, silent) {
if (!value.startsWith('$') || value.startsWith('$$')) {
if (
!value.startsWith('$') ||
value.startsWith('$ ') ||
value.startsWith('$$')
) {
return;
}

Expand Down
95 changes: 87 additions & 8 deletions tests/math.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,80 @@ it('inline', () => {
expect(received).toBe(expected);
});

it('inline: multiline', () => {
const md = `$x=y
1 + 1 = 2$`;
const received = stringify(md, options);
const expected = `<p><span class="math inline">\\(x=y
1 + 1 = 2\\)</span></p>`;
expect(received).toBe(expected);
});

it('inline: ignore "$ ...$"', () => {
const md = 'text $ text$x = y$';
const received = stringify(md, options);
const expected =
'<p>text $ text<span class="math inline">\\(x = y\\)</span></p>';
expect(received).toBe(expected);
});

it('inline: ignore "$... $"', () => {
const md = 'text $x = $y$ text';
const received = stringify(md, options);
const expected =
'<p>text <span class="math inline">\\(x = $y\\)</span> text</p>';
expect(received).toBe(expected);
});

it('inline: ignore "$" with number', () => {
const md = 'There are $3 and $4 bread.';
const received = stringify(md, options);
const expected = '<p>There are $3 and $4 bread.</p>';
expect(received).toBe(expected);
});

it('inline: ignore "$.\\$.$"', () => {
const md = 'text $x = 5\\$ + 4$ text';
const received = stringify(md, options);
const expected =
'<p>text <span class="math inline">\\(x = 5\\$ + 4\\)</span> text</p>';
expect(received).toBe(expected);
});

it('inline: exclusive other markdown syntax', () => {
const received = stringify('text$**bold**$text', options);
const expected =
'<p>text<span class="math inline">\\(**bold**\\)</span>text</p>';
expect(received).toBe(expected);
});

it('display', () => {
const received = stringify('text$$1 + 1 = 2$$text', options);
const expected =
'<p>text<span class="math display">$$1 + 1 = 2$$</span>text</p>';
expect(received).toBe(expected);
});

it('display: multiline', () => {
const md = `$$
x=y
1 + 1 = 2
$$`;
const received = stringify(md, options);
const expected = `<p><span class="math display">$$
x=y
1 + 1 = 2
$$</span></p>`;
expect(received).toBe(expected);
});

it('display: exclusive other markdown syntax', () => {
const received = stringify('text$$**bold**$$text', options);
const expected =
'<p>text<span class="math display">$$**bold**$$</span>text</p>';
expect(received).toBe(expected);
});

it('inline and display', () => {
const received = stringify(
'inline: $x = y$\n\ndisplay: $$1 + 1 = 2$$',
Expand All @@ -29,23 +96,35 @@ it('inline and display', () => {
expect(received).toBe(expected);
});

it('un-match', () => {
it('un-match: $$$...', () => {
const received = stringify('text$$$unmatch$$$text', options);
const expected = '<p>text$$$unmatch$$$text</p>';
expect(received).toBe(expected);
});

it('inline: exclusive other markdown syntax', () => {
const received = stringify('text$**bold**$text', options);
it('un-match inline', () => {
const received = stringify('$x = y$ $ x = y$ $x = y $ $x = y$7', options);
const expected =
'<p>text<span class="math inline">\\(**bold**\\)</span>text</p>';
'<p><span class="math inline">\\(x = y\\)</span> $ x = y$ $x = y $ $x = y$7</p>';
expect(received).toBe(expected);
});

it('display: exclusive other markdown syntax', () => {
const received = stringify('text$$**bold**$$text', options);
const expected =
'<p>text<span class="math display">$$**bold**$$</span>text</p>';
it('un-match: divided paragraph', () => {
const md = `$x = y
1 + 1 = 2$
$$
x = y
1 + 1 = 2
$$`;
const received = stringify(md, options);
const expected = `<p>$x = y</p>
<p>1 + 1 = 2$
$$
x = y</p>
<p>1 + 1 = 2
$$</p>`;
expect(received).toBe(expected);
});

Expand Down

0 comments on commit 2611e92

Please sign in to comment.