securemark 0.243.2 → 0.244.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/design.md +1 -0
- package/dist/securemark.js +105 -68
- package/package-lock.json +147 -141
- package/package.json +6 -6
- package/src/combinator/control/manipulation/fence.ts +25 -15
- package/src/combinator/data/parser.ts +2 -0
- package/src/debug.test.ts +6 -4
- package/src/parser/block/codeblock.test.ts +1 -1
- package/src/parser/block/codeblock.ts +9 -6
- package/src/parser/block/extension/aside.ts +8 -5
- package/src/parser/block/extension/example.ts +9 -5
- package/src/parser/block/extension/fig.test.ts +2 -0
- package/src/parser/block/extension/figure.test.ts +3 -1
- package/src/parser/block/extension/figure.ts +6 -2
- package/src/parser/block/extension/message.ts +9 -5
- package/src/parser/block/extension/placeholder.ts +6 -5
- package/src/parser/block/extension/table.ts +8 -5
- package/src/parser/block/mathblock.test.ts +4 -4
- package/src/parser/block/mathblock.ts +11 -8
- package/src/parser/header.test.ts +1 -0
- package/src/parser/inline/bracket.test.ts +21 -2
- package/src/parser/inline/bracket.ts +2 -2
- package/src/parser/inline/link.ts +1 -1
- package/src/renderer/render/media/image.ts +2 -2
- package/src/renderer/render/media/video.ts +1 -1
- package/src/renderer/render/media/youtube.ts +1 -0
|
@@ -2,36 +2,46 @@ import { Parser, Ctx } from '../../data/parser';
|
|
|
2
2
|
import { firstline, isEmpty } from '../constraint/line';
|
|
3
3
|
import { unshift } from 'spica/array';
|
|
4
4
|
|
|
5
|
-
export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: RegExp, limit: number, separation
|
|
5
|
+
export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: RegExp, limit: number, separation = true): Parser<string, C, D> {
|
|
6
6
|
return source => {
|
|
7
7
|
if (source === '') return;
|
|
8
8
|
const matches = source.match(opener);
|
|
9
9
|
if (!matches) return;
|
|
10
10
|
assert(matches[0] === firstline(source));
|
|
11
11
|
const delim = matches[1];
|
|
12
|
+
assert(delim && delim === delim.trim());
|
|
12
13
|
if (matches[0].indexOf(delim, delim.length) !== -1) return;
|
|
13
14
|
let rest = source.slice(matches[0].length);
|
|
14
15
|
// Prevent annoying parsing in editing.
|
|
15
16
|
if (isEmpty(firstline(rest)) && firstline(rest.slice(firstline(rest).length)).trimEnd() !== delim) return;
|
|
16
17
|
let block = '';
|
|
17
18
|
let closer = '';
|
|
18
|
-
|
|
19
|
+
let overflow = '';
|
|
20
|
+
for (let count = 1; ; ++count) {
|
|
19
21
|
if (rest === '') break;
|
|
20
|
-
const line =
|
|
21
|
-
|
|
22
|
-
if
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
const line = firstline(rest);
|
|
23
|
+
if ((closer || count > limit + 1) && isEmpty(line)) break;
|
|
24
|
+
if(closer) {
|
|
25
|
+
overflow += line;
|
|
26
|
+
}
|
|
27
|
+
if (!closer && count <= limit + 1 && line.slice(0, delim.length) === delim && line.trimEnd() === delim) {
|
|
28
|
+
closer = line;
|
|
29
|
+
if (isEmpty(firstline(rest.slice(line.length)))) {
|
|
30
|
+
rest = rest.slice(line.length);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
if (!separation) {
|
|
34
|
+
rest = rest.slice(line.length);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
assert(!overflow);
|
|
38
|
+
overflow = line;
|
|
39
|
+
}
|
|
40
|
+
if (!overflow) {
|
|
41
|
+
block += line;
|
|
31
42
|
}
|
|
32
|
-
block += line;
|
|
33
43
|
rest = rest.slice(line.length);
|
|
34
44
|
}
|
|
35
|
-
return [unshift([block, closer], matches), rest];
|
|
45
|
+
return [unshift([block, overflow, closer], matches), rest];
|
|
36
46
|
};
|
|
37
47
|
}
|
package/src/debug.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Result, eval, exec } from './combinator/data/parser';
|
|
2
2
|
import { html, define } from 'typed-dom/dom';
|
|
3
|
+
import { querySelector, querySelectorAll } from 'typed-dom/query';
|
|
3
4
|
|
|
4
5
|
export function inspect(result: Result<HTMLElement | string>, until: number | string = Infinity): Result<string> {
|
|
5
6
|
return result && [
|
|
@@ -7,14 +8,15 @@ export function inspect(result: Result<HTMLElement | string>, until: number | st
|
|
|
7
8
|
assert(node);
|
|
8
9
|
if (typeof node === 'string') return node;
|
|
9
10
|
node = node.cloneNode(true);
|
|
10
|
-
assert(!node
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
assert(!querySelector(node, '.invalid[data-invalid-message$="."]'));
|
|
12
|
+
querySelectorAll(node, '.invalid').forEach(el => {
|
|
13
|
+
assert(el.matches('[data-invalid-syntax][data-invalid-type][data-invalid-message]'));
|
|
13
14
|
define(el, {
|
|
14
15
|
'data-invalid-syntax': null,
|
|
15
16
|
'data-invalid-type': null,
|
|
16
17
|
'data-invalid-message': null,
|
|
17
|
-
})
|
|
18
|
+
});
|
|
19
|
+
});
|
|
18
20
|
until = typeof until === 'number'
|
|
19
21
|
? until
|
|
20
22
|
: ~(~node.outerHTML.indexOf(until) || -Infinity) + until.length;
|
|
@@ -17,6 +17,7 @@ describe('Unit: parser/block/codeblock', () => {
|
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('```\na\n```\nb')), [['<pre class="invalid" translate="no">```\na\n```\nb</pre>'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('```\n\n\n```')), undefined);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('```a ```\n```')), undefined);
|
|
20
|
+
assert.deepStrictEqual(inspect(parser('```\n```\n```')), [['<pre class="invalid" translate="no">```\n```\n```</pre>'], '']);
|
|
20
21
|
assert.deepStrictEqual(inspect(parser('```\n````')), [['<pre class="invalid" translate="no">```\n````</pre>'], '']);
|
|
21
22
|
assert.deepStrictEqual(inspect(parser('````\n```')), [['<pre class="invalid" translate="no">````\n```</pre>'], '']);
|
|
22
23
|
assert.deepStrictEqual(inspect(parser(' ```\n```')), undefined);
|
|
@@ -30,7 +31,6 @@ describe('Unit: parser/block/codeblock', () => {
|
|
|
30
31
|
assert.deepStrictEqual(inspect(parser('```\na\nb\n```')), [['<pre class="text">a<br>b</pre>'], '']);
|
|
31
32
|
assert.deepStrictEqual(inspect(parser('```\n\\\n```')), [['<pre class="text">\\</pre>'], '']);
|
|
32
33
|
assert.deepStrictEqual(inspect(parser('```\n`\n```')), [['<pre class="text">`</pre>'], '']);
|
|
33
|
-
assert.deepStrictEqual(inspect(parser('```\n```\n```')), [['<pre class="text">```</pre>'], '']);
|
|
34
34
|
assert.deepStrictEqual(inspect(parser('```\n```\n\n```')), [['<pre class="text"></pre>'], '\n```']);
|
|
35
35
|
assert.deepStrictEqual(inspect(parser('```\n````\n```')), [['<pre class="text">````</pre>'], '']);
|
|
36
36
|
assert.deepStrictEqual(inspect(parser('```\n````\n\n```')), [['<pre class="text">````<br></pre>'], '']);
|
|
@@ -17,7 +17,7 @@ export const segment_: CodeBlockParser.SegmentParser = block(validate('```',
|
|
|
17
17
|
export const codeblock: CodeBlockParser = block(validate('```', fmap(
|
|
18
18
|
fence(opener, 300),
|
|
19
19
|
// Bug: Type mismatch between outer and inner.
|
|
20
|
-
([body, closer, opener, delim, param]: string[], _, context) => {
|
|
20
|
+
([body, overflow, closer, opener, delim, param]: string[], _, context) => {
|
|
21
21
|
const params = param.match(/(?:\\.?|\S)+/g)?.reduce<{
|
|
22
22
|
lang?: string;
|
|
23
23
|
path?: string;
|
|
@@ -45,17 +45,20 @@ export const codeblock: CodeBlockParser = block(validate('```', fmap(
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
name in params
|
|
48
|
-
? params.invalid = `Duplicate ${name}
|
|
48
|
+
? params.invalid = `Duplicate ${name} attribute`
|
|
49
49
|
: params[name] = value;
|
|
50
50
|
return params;
|
|
51
51
|
}, {}) ?? {};
|
|
52
|
-
if (!closer || params.invalid) return [html('pre', {
|
|
52
|
+
if (!closer || overflow || params.invalid) return [html('pre', {
|
|
53
53
|
class: 'invalid',
|
|
54
54
|
translate: 'no',
|
|
55
55
|
'data-invalid-syntax': 'codeblock',
|
|
56
|
-
'data-invalid-type': !closer ? 'fence' : 'argument',
|
|
57
|
-
'data-invalid-message':
|
|
58
|
-
|
|
56
|
+
'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
|
|
57
|
+
'data-invalid-message':
|
|
58
|
+
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
59
|
+
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
60
|
+
params.invalid,
|
|
61
|
+
}, `${opener}${body}${overflow || closer}`)];
|
|
59
62
|
const el = html('pre',
|
|
60
63
|
{
|
|
61
64
|
class: params.lang ? `code language-${params.lang}` : 'text',
|
|
@@ -7,14 +7,17 @@ import { html } from 'typed-dom/dom';
|
|
|
7
7
|
export const aside: ExtensionParser.AsideParser = creator(100, block(validate('~~~', fmap(
|
|
8
8
|
fence(/^(~{3,})aside(?!\S)([^\n]*)(?:$|\n)/, 300),
|
|
9
9
|
// Bug: Type mismatch between outer and inner.
|
|
10
|
-
([body, closer, opener, delim, param]: string[], _, context) => {
|
|
11
|
-
if (!closer || param.trimStart()) return [html('pre', {
|
|
10
|
+
([body, overflow, closer, opener, delim, param]: string[], _, context) => {
|
|
11
|
+
if (!closer || overflow || param.trimStart()) return [html('pre', {
|
|
12
12
|
class: 'invalid',
|
|
13
13
|
translate: 'no',
|
|
14
14
|
'data-invalid-syntax': 'aside',
|
|
15
|
-
'data-invalid-type': !closer ? 'fence' : 'argument',
|
|
16
|
-
'data-invalid-message':
|
|
17
|
-
|
|
15
|
+
'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
|
|
16
|
+
'data-invalid-message':
|
|
17
|
+
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
18
|
+
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
19
|
+
'Invalid argument',
|
|
20
|
+
}, `${opener}${body}${overflow || closer}`)];
|
|
18
21
|
const annotations = html('ol', { class: 'annotations' });
|
|
19
22
|
const references = html('ol', { class: 'references' });
|
|
20
23
|
const document = parse(body.slice(0, -1), {
|
|
@@ -10,14 +10,17 @@ const opener = /^(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/;
|
|
|
10
10
|
export const example: ExtensionParser.ExampleParser = creator(100, block(validate('~~~', fmap(
|
|
11
11
|
fence(opener, 300),
|
|
12
12
|
// Bug: Type mismatch between outer and inner.
|
|
13
|
-
([body, closer, opener, delim, type = 'markdown', param]: string[], _, context) => {
|
|
14
|
-
if (!closer || param.trimStart()) return [html('pre', {
|
|
13
|
+
([body, overflow, closer, opener, delim, type = 'markdown', param]: string[], _, context) => {
|
|
14
|
+
if (!closer || overflow || param.trimStart()) return [html('pre', {
|
|
15
15
|
class: 'invalid',
|
|
16
16
|
translate: 'no',
|
|
17
17
|
'data-invalid-syntax': 'example',
|
|
18
|
-
'data-invalid-type': !closer ? 'fence' : 'argument',
|
|
19
|
-
'data-invalid-message':
|
|
20
|
-
|
|
18
|
+
'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
|
|
19
|
+
'data-invalid-message':
|
|
20
|
+
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
21
|
+
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
22
|
+
'Invalid argument',
|
|
23
|
+
}, `${opener}${body}${overflow || closer}`)];
|
|
21
24
|
switch (type) {
|
|
22
25
|
case 'markdown': {
|
|
23
26
|
const annotations = html('ol', { class: 'annotations' });
|
|
@@ -52,6 +55,7 @@ export const example: ExtensionParser.ExampleParser = creator(100, block(validat
|
|
|
52
55
|
class: 'invalid',
|
|
53
56
|
translate: 'no',
|
|
54
57
|
'data-invalid-syntax': 'example',
|
|
58
|
+
'data-invalid-type': 'type',
|
|
55
59
|
'data-invalid-message': 'Invalid example type',
|
|
56
60
|
}, `${opener}${body}${closer}`),
|
|
57
61
|
];
|
|
@@ -31,6 +31,8 @@ describe('Unit: parser/block/extension/fig', () => {
|
|
|
31
31
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n```\n~~~\n```')), [['<figure data-type="text" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><pre class="text">~~~</pre></div></figure>'], '']);
|
|
32
32
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n$$\n\n$$')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
|
|
33
33
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n$$\n\n$$\n')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~\n~~~\n')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
|
|
34
36
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~example/markdown\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
|
|
35
37
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~example/markdown\n~~~\n')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
|
|
36
38
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~table\n~~~')), [['<figure data-type="table" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><table></table></div></figure>'], '']);
|
|
@@ -17,6 +17,7 @@ describe('Unit: parser/block/extension/figure', () => {
|
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n\n\n\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n\n\n\n~~~</pre>'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n !https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n !https://host\n~~~</pre>'], '']);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n~~~\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n~~~\n~~~</pre>'], '']);
|
|
20
|
+
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n\ncaption\n~~~\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n\ncaption\n~~~\n~~~</pre>'], '']);
|
|
20
21
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n~~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n~~~~</pre>'], '']);
|
|
21
22
|
assert.deepStrictEqual(inspect(parser('~~~~figure [$group-name]\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~~figure [$group-name]\n!https://host\n~~~</pre>'], '']);
|
|
22
23
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]a\nhttps://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]a\nhttps://host\n~~~</pre>'], '']);
|
|
@@ -33,7 +34,7 @@ describe('Unit: parser/block/extension/figure', () => {
|
|
|
33
34
|
assert.deepStrictEqual(inspect(parser('~~~figure [$figure-name]\n> \n\n~~~')), [['<figure data-type="quote" data-label="figure-name" data-group="figure" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
|
|
34
35
|
assert.deepStrictEqual(inspect(parser('~~~figure [$table-name]\n> \n\n~~~')), [['<figure data-type="quote" data-label="table-name" data-group="table" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
|
|
35
36
|
assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n0${'\n'.repeat(301)}~~~`), '>'), [['<pre class="invalid" translate="no">'], '']);
|
|
36
|
-
assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n~~~\n0${'\n'.repeat(301)}~~~\n~~~`), '>'), [['<pre class="invalid" translate="no">'], '\n~~~\n
|
|
37
|
+
assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n~~~\n0${'\n'.repeat(301)}~~~\n~~~`), '>'), [['<pre class="invalid" translate="no">'], `${'\n'.repeat(300)}~~~\n~~~`]);
|
|
37
38
|
});
|
|
38
39
|
|
|
39
40
|
it('valid', () => {
|
|
@@ -52,6 +53,7 @@ describe('Unit: parser/block/extension/figure', () => {
|
|
|
52
53
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n$$\n\n$$\n~~~')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
|
|
53
54
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n$$\n~~~\n$$\n~~~')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n~~~\n$$</div></div></figure>'], '']);
|
|
54
55
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n$$\n\n$$\n\ncaption\n~~~')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
|
|
56
|
+
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n~~~\n~~~\n\ncaption\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
|
|
55
57
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n~~~example/markdown\n~~~\n\ncaption\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
|
|
56
58
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n~~~table\n~~~\n\ncaption\n~~~')), [['<figure data-type="table" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><table></table></div></figure>'], '']);
|
|
57
59
|
assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n> \n~~~\n\n~~~')), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><blockquote><pre><br>~~~</pre></blockquote></div></figure>'], '']);
|
|
@@ -85,7 +85,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
|
85
85
|
])),
|
|
86
86
|
fmap(
|
|
87
87
|
fence(/^(~{3,})(?:figure|\[?\$\S*)(?!\S)[^\n]*(?:$|\n)/, 300),
|
|
88
|
-
([body, closer, opener, delim]: string[], _, context) => [
|
|
88
|
+
([body, overflow, closer, opener, delim]: string[], _, context) => [
|
|
89
89
|
html('pre', {
|
|
90
90
|
class: 'invalid',
|
|
91
91
|
translate: 'no',
|
|
@@ -95,6 +95,10 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
|
95
95
|
'data-invalid-type': 'fence',
|
|
96
96
|
'data-invalid-message': `Missing the closing delimiter "${delim}"`,
|
|
97
97
|
} ||
|
|
98
|
+
overflow && {
|
|
99
|
+
'data-invalid-type': 'fence',
|
|
100
|
+
'data-invalid-message': `Invalid trailing line after the closing delimiter "${delim}"`,
|
|
101
|
+
} ||
|
|
98
102
|
!seg_label(opener.match(/^~+(?:figure[^\S\n]+)?(\[?\$\S+)/)?.[1] ?? '', context) && {
|
|
99
103
|
'data-invalid-type': 'label',
|
|
100
104
|
'data-invalid-message': 'Invalid label',
|
|
@@ -107,7 +111,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
|
107
111
|
'data-invalid-type': 'content',
|
|
108
112
|
'data-invalid-message': 'Invalid content',
|
|
109
113
|
},
|
|
110
|
-
}, `${opener}${body}${closer}`),
|
|
114
|
+
}, `${opener}${body}${overflow || closer}`),
|
|
111
115
|
])));
|
|
112
116
|
|
|
113
117
|
function attributes(label: string, param: string, content: HTMLElement, caption: readonly HTMLElement[]): Record<string, string | undefined> {
|
|
@@ -21,14 +21,17 @@ import MessageParser = ExtensionParser.MessageParser;
|
|
|
21
21
|
export const message: MessageParser = block(validate('~~~', fmap(
|
|
22
22
|
fence(/^(~{3,})message\/(\S+)([^\n]*)(?:$|\n)/, 300),
|
|
23
23
|
// Bug: Type mismatch between outer and inner.
|
|
24
|
-
([body, closer, opener, delim, type, param]: string[], _, context) => {
|
|
25
|
-
if (!closer || param.trimStart()) return [html('pre', {
|
|
24
|
+
([body, overflow, closer, opener, delim, type, param]: string[], _, context) => {
|
|
25
|
+
if (!closer || overflow || param.trimStart()) return [html('pre', {
|
|
26
26
|
class: 'invalid',
|
|
27
27
|
translate: 'no',
|
|
28
28
|
'data-invalid-syntax': 'message',
|
|
29
|
-
'data-invalid-type': !closer ? 'fence' : 'argument',
|
|
30
|
-
'data-invalid-message':
|
|
31
|
-
|
|
29
|
+
'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
|
|
30
|
+
'data-invalid-message':
|
|
31
|
+
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
32
|
+
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
33
|
+
'Invalid argument',
|
|
34
|
+
}, `${opener}${body}${overflow || closer}`)];
|
|
32
35
|
switch (type) {
|
|
33
36
|
case 'note':
|
|
34
37
|
case 'caution':
|
|
@@ -39,6 +42,7 @@ export const message: MessageParser = block(validate('~~~', fmap(
|
|
|
39
42
|
class: 'invalid',
|
|
40
43
|
translate: 'no',
|
|
41
44
|
'data-invalid-syntax': 'message',
|
|
45
|
+
'data-invalid-type': 'type',
|
|
42
46
|
'data-invalid-message': 'Invalid message type',
|
|
43
47
|
}, `${opener}${body}${closer}`)];
|
|
44
48
|
}
|
|
@@ -12,12 +12,13 @@ export const segment_: ExtensionParser.PlaceholderParser.SegmentParser = block(v
|
|
|
12
12
|
|
|
13
13
|
export const placeholder: ExtensionParser.PlaceholderParser = block(validate('~~~', fmap(
|
|
14
14
|
fence(opener, Infinity),
|
|
15
|
-
([body, closer, opener, delim]) => [
|
|
15
|
+
([body, overflow, closer, opener, delim]) => [
|
|
16
16
|
html('pre', {
|
|
17
17
|
class: 'invalid',
|
|
18
18
|
translate: 'no',
|
|
19
|
-
'data-invalid-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
'data-invalid-message':
|
|
20
|
+
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
21
|
+
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
22
|
+
'Invalid argument',
|
|
23
|
+
}, `${opener}${body}${overflow || closer}`),
|
|
23
24
|
])));
|
|
@@ -26,14 +26,17 @@ export const segment_: TableParser.SegmentParser = block(validate('~~~',
|
|
|
26
26
|
export const table: TableParser = block(validate('~~~', recover(fmap(
|
|
27
27
|
fence(opener, 10000),
|
|
28
28
|
// Bug: Type mismatch between outer and inner.
|
|
29
|
-
([body, closer, opener, delim, param]: string[], _, context) => {
|
|
30
|
-
if (!closer || param.trimStart()) return [html('pre', {
|
|
29
|
+
([body, overflow, closer, opener, delim, param]: string[], _, context) => {
|
|
30
|
+
if (!closer || overflow || param.trimStart()) return [html('pre', {
|
|
31
31
|
class: 'invalid',
|
|
32
32
|
translate: 'no',
|
|
33
33
|
'data-invalid-syntax': 'table',
|
|
34
|
-
'data-invalid-type': !closer ? 'fence' : 'argument',
|
|
35
|
-
'data-invalid-message':
|
|
36
|
-
|
|
34
|
+
'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
|
|
35
|
+
'data-invalid-message':
|
|
36
|
+
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
37
|
+
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
38
|
+
'Invalid argument',
|
|
39
|
+
}, `${opener}${body}${overflow || closer}`)];
|
|
37
40
|
return eval(parser(body, context)) ?? [html('table')];
|
|
38
41
|
}),
|
|
39
42
|
(source, _, reason) =>
|
|
@@ -19,10 +19,12 @@ describe('Unit: parser/block/mathblock', () => {
|
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('$$ $$\n$$')), undefined);
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('$$lang\n$$')), [['<pre class="invalid" translate="no">$$lang\n$$</pre>'], '']);
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('$$ param\n$$')), [['<pre class="invalid" translate="no">$$ param\n$$</pre>'], '']);
|
|
22
|
+
assert.deepStrictEqual(inspect(parser('$$\n$$\n$$')), [['<pre class="invalid" translate="no">$$\n$$\n$$</pre>'], '']);
|
|
23
|
+
assert.deepStrictEqual(inspect(parser('$$\n$$$')), [['<pre class="invalid" translate="no">$$\n$$$</pre>'], '']);
|
|
22
24
|
assert.deepStrictEqual(inspect(parser('$$$\n$$')), [['<pre class="invalid" translate="no">$$$\n$$</pre>'], '']);
|
|
23
25
|
assert.deepStrictEqual(inspect(parser('$$$\n$$$')), [['<pre class="invalid" translate="no">$$$\n$$$</pre>'], '']);
|
|
24
26
|
assert.deepStrictEqual(inspect(parser(' $$\n$$')), undefined);
|
|
25
|
-
assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(
|
|
27
|
+
assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(301)}$$`), '>'), [['<pre class="invalid" translate="no">'], '']);
|
|
26
28
|
});
|
|
27
29
|
|
|
28
30
|
it('basic', () => {
|
|
@@ -34,12 +36,10 @@ describe('Unit: parser/block/mathblock', () => {
|
|
|
34
36
|
assert.deepStrictEqual(inspect(parser('$$\n\\\n$$')), [['<div class="math" translate="no">$$\n\\\n$$</div>'], '']);
|
|
35
37
|
assert.deepStrictEqual(inspect(parser('$$\n$\n$$')), [['<div class="math" translate="no">$$\n$\n$$</div>'], '']);
|
|
36
38
|
assert.deepStrictEqual(inspect(parser('$$\n$\n\n$$')), [['<div class="math" translate="no">$$\n$\n\n$$</div>'], '']);
|
|
37
|
-
assert.deepStrictEqual(inspect(parser('$$\n$$\n$$')), [['<div class="math" translate="no">$$\n$$\n$$</div>'], '']);
|
|
38
39
|
assert.deepStrictEqual(inspect(parser('$$\n$$\n\n$$')), [['<div class="math" translate="no">$$\n$$</div>'], '\n$$']);
|
|
39
40
|
assert.deepStrictEqual(inspect(parser('$$\n$$$\n$$')), [['<div class="math" translate="no">$$\n$$$\n$$</div>'], '']);
|
|
40
41
|
assert.deepStrictEqual(inspect(parser('$$\n$$$\n\n$$')), [['<div class="math" translate="no">$$\n$$$\n\n$$</div>'], '']);
|
|
41
|
-
assert.deepStrictEqual(inspect(parser('
|
|
42
|
-
assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(100)}$$`), '>'), [['<div class="math" translate="no">'], '']);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(300)}$$`), '>'), [['<div class="math" translate="no">'], '']);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
});
|
|
@@ -6,23 +6,26 @@ import { html } from 'typed-dom/dom';
|
|
|
6
6
|
const opener = /^(\${2,})(?!\$)([^\n]*)(?:$|\n)/;
|
|
7
7
|
|
|
8
8
|
export const segment: MathBlockParser.SegmentParser = block(validate('$$',
|
|
9
|
-
clear(fence(opener,
|
|
9
|
+
clear(fence(opener, 300))));
|
|
10
10
|
|
|
11
11
|
export const segment_: MathBlockParser.SegmentParser = block(validate('$$',
|
|
12
|
-
clear(fence(opener,
|
|
12
|
+
clear(fence(opener, 300, false))), false);
|
|
13
13
|
|
|
14
14
|
export const mathblock: MathBlockParser = block(validate('$$', fmap(
|
|
15
|
-
fence(opener,
|
|
15
|
+
fence(opener, 300),
|
|
16
16
|
// Bug: Type mismatch between outer and inner.
|
|
17
|
-
([body, closer, opener, delim, param]: string[], _, { caches: { math: cache = undefined } = {} }) => [
|
|
18
|
-
delim.length === 2 && closer && param.trimStart() === ''
|
|
17
|
+
([body, overflow, closer, opener, delim, param]: string[], _, { caches: { math: cache = undefined } = {} }) => [
|
|
18
|
+
delim.length === 2 && closer && !overflow && param.trimStart() === ''
|
|
19
19
|
? cache?.get(`${delim}\n${body}${delim}`)?.cloneNode(true) as HTMLDivElement ||
|
|
20
20
|
html('div', { class: 'math', translate: 'no' }, `${delim}\n${body}${delim}`)
|
|
21
21
|
: html('pre', {
|
|
22
22
|
class: 'invalid',
|
|
23
23
|
translate: 'no',
|
|
24
24
|
'data-invalid-syntax': 'mathblock',
|
|
25
|
-
'data-invalid-type': delim.length > 2 ? 'syntax' : !closer ? 'fence' : 'argument',
|
|
26
|
-
'data-invalid-message': delim.length > 2 ? 'Invalid syntax' :
|
|
27
|
-
|
|
25
|
+
'data-invalid-type': delim.length > 2 ? 'syntax' : !closer || overflow ? 'fence' : 'argument',
|
|
26
|
+
'data-invalid-message': delim.length > 2 ? 'Invalid syntax' :
|
|
27
|
+
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
28
|
+
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
29
|
+
'Invalid argument',
|
|
30
|
+
}, `${opener}${body}${overflow || closer}`),
|
|
28
31
|
])));
|
|
@@ -21,6 +21,7 @@ describe('Unit: parser/header', () => {
|
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('---\n \n---')), undefined);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('---\n-\n---')), [['<pre class="invalid" translate="no">---\n-\n---</pre>'], '']);
|
|
23
23
|
assert.deepStrictEqual(inspect(parser('----\na: b\n----')), [['<pre class="invalid" translate="no">----\na: b\n----</pre>'], '']);
|
|
24
|
+
assert.deepStrictEqual(inspect(parser(`---\n${'a: b\n'.repeat(101)}---`)), [[`<pre class="invalid" translate="no">---\n${'a: b\n'.repeat(101)}---</pre>`], '']);
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
it('basic', () => {
|
|
@@ -16,17 +16,36 @@ describe('Unit: parser/inline/bracket', () => {
|
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('(1)')), [['(', '1', ')'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('(10)')), [['(', '10', ')'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('(2000)')), [['(', '2000', ')'], '']);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser('(1, 2)')), [['(', '1, 2', ')'], '']);
|
|
20
19
|
assert.deepStrictEqual(inspect(parser('(0-1)')), [['(', '0-1', ')'], '']);
|
|
21
20
|
assert.deepStrictEqual(inspect(parser('(0)-1')), [['(', '0', ')'], '-1']);
|
|
22
21
|
assert.deepStrictEqual(inspect(parser('(0.1)')), [['(', '0.1', ')'], '']);
|
|
23
22
|
assert.deepStrictEqual(inspect(parser('(0.1.2)')), [['(', '0.1.2', ')'], '']);
|
|
24
23
|
assert.deepStrictEqual(inspect(parser('(1.1, 1.2-1.3, 1.4)')), [['(', '1.1, 1.2-1.3, 1.4', ')'], '']);
|
|
24
|
+
assert.deepStrictEqual(inspect(parser('(1 2)')), [['<span class="paren">(1 2)</span>'], '']);
|
|
25
|
+
assert.deepStrictEqual(inspect(parser('(1, 2)')), [['(', '1, 2', ')'], '']);
|
|
26
|
+
assert.deepStrictEqual(inspect(parser('(1a)')), [['(', '1a', ')'], '']);
|
|
25
27
|
assert.deepStrictEqual(inspect(parser('(a)')), [['(', 'a', ')'], '']);
|
|
28
|
+
assert.deepStrictEqual(inspect(parser('(a1)')), [['(', 'a1', ')'], '']);
|
|
29
|
+
assert.deepStrictEqual(inspect(parser('(a-1)')), [['(', 'a-1', ')'], '']);
|
|
30
|
+
assert.deepStrictEqual(inspect(parser('(a.1)')), [['(', 'a.1', ')'], '']);
|
|
31
|
+
assert.deepStrictEqual(inspect(parser('(a b)')), [['<span class="paren">(a b)</span>'], '']);
|
|
32
|
+
assert.deepStrictEqual(inspect(parser('(word)')), [['(', 'word', ')'], '']);
|
|
33
|
+
assert.deepStrictEqual(inspect(parser('(word word)')), [['<span class="paren">(word word)</span>'], '']);
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('(word, word)')), [['(', 'word, word', ')'], '']);
|
|
26
35
|
assert.deepStrictEqual(inspect(parser('(A)')), [['(', 'A', ')'], '']);
|
|
36
|
+
assert.deepStrictEqual(inspect(parser('(Name)')), [['(', 'Name', ')'], '']);
|
|
37
|
+
assert.deepStrictEqual(inspect(parser('(Word word)')), [['<span class="paren">(Word word)</span>'], '']);
|
|
38
|
+
assert.deepStrictEqual(inspect(parser('(Word Word)')), [['<span class="paren">(Word Word)</span>'], '']);
|
|
39
|
+
assert.deepStrictEqual(inspect(parser('(Name, Name)')), [['(', 'Name, Name', ')'], '']);
|
|
40
|
+
assert.deepStrictEqual(inspect(parser('(ABBR)')), [['(', 'ABBR', ')'], '']);
|
|
41
|
+
assert.deepStrictEqual(inspect(parser('(ABBR, ABBR)')), [['(', 'ABBR, ABBR', ')'], '']);
|
|
27
42
|
assert.deepStrictEqual(inspect(parser('(1,2)')), [['(', '1,2', ')'], '']);
|
|
28
43
|
assert.deepStrictEqual(inspect(parser('(0-1)')), [['(', '0-1', ')'], '']);
|
|
29
|
-
assert.deepStrictEqual(inspect(parser('
|
|
44
|
+
assert.deepStrictEqual(inspect(parser('(0.1)')), [['(', '0.1', ')'], '']);
|
|
45
|
+
assert.deepStrictEqual(inspect(parser('(a)')), [['(', 'a', ')'], '']);
|
|
46
|
+
assert.deepStrictEqual(inspect(parser('(A)')), [['(', 'A', ')'], '']);
|
|
47
|
+
assert.deepStrictEqual(inspect(parser('(A,B)')), [['(', 'A,B', ')'], '']);
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('(A、B)')), [['(', 'A、B', ')'], '']);
|
|
30
49
|
});
|
|
31
50
|
|
|
32
51
|
it('[', () => {
|
|
@@ -6,14 +6,14 @@ import { str } from '../source';
|
|
|
6
6
|
import { html, defrag } from 'typed-dom/dom';
|
|
7
7
|
import { unshift, push } from 'spica/array';
|
|
8
8
|
|
|
9
|
-
const index = /^
|
|
9
|
+
const index = /^[0-9A-Za-z]+(?:(?:[.-]|, )[0-9A-Za-z]+)*/;
|
|
10
10
|
|
|
11
11
|
export const bracket: BracketParser = lazy(() => creator(union([
|
|
12
12
|
surround(str('('), str(index), str(')')),
|
|
13
13
|
surround(str('('), some(inline, ')'), str(')'), true,
|
|
14
14
|
([as, bs = [], cs], rest) => [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))], rest],
|
|
15
15
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
16
|
-
surround(str('('), str(new RegExp(index.source.replace(/[09AZaz
|
|
16
|
+
surround(str('('), str(new RegExp(index.source.replace(', ', '[,、]').replace(/[09AZaz.]|\-(?!\w)/g, c => c.trimStart() && String.fromCharCode(c.charCodeAt(0) + 0xFEE0)))), str(')')),
|
|
17
17
|
surround(str('('), some(inline, ')'), str(')'), true,
|
|
18
18
|
([as, bs = [], cs], rest) => [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))], rest],
|
|
19
19
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { location, encodeURI, decodeURI, Location } from 'spica/global';
|
|
1
|
+
import { undefined, location, encodeURI, decodeURI, Location } from 'spica/global';
|
|
2
2
|
import { LinkParser } from '../inline';
|
|
3
3
|
import { eval } from '../../combinator/data/parser';
|
|
4
4
|
import { union, inits, tails, some, validate, guard, context, creator, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { define } from 'typed-dom/dom';
|
|
2
|
-
import { Collection } from 'spica/collection';
|
|
3
1
|
import { ObjectFromEntries } from 'spica/alias';
|
|
2
|
+
import { Collection } from 'spica/collection';
|
|
3
|
+
import { define } from 'typed-dom/dom';
|
|
4
4
|
|
|
5
5
|
export function image(source: HTMLImageElement, url: URL, cache?: Collection<string, HTMLElement>): HTMLImageElement {
|
|
6
6
|
if (cache?.has(url.href)) return define(
|