securemark 0.300.4 → 0.300.6
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 +8 -0
- package/dist/index.js +215 -151
- package/markdown.d.ts +36 -36
- package/package.json +1 -1
- package/src/api/bind.ts +5 -2
- package/src/api/header.ts +1 -1
- package/src/api/parse.test.ts +0 -27
- package/src/api/parse.ts +8 -2
- package/src/combinator/effect/backtrack.ts +19 -7
- package/src/combinator/parser/list.ts +13 -15
- package/src/combinator/parser.ts +3 -5
- package/src/debug.test.ts +3 -0
- package/src/parser/autolink.ts +2 -2
- package/src/parser/block/blockquote.test.ts +4 -4
- package/src/parser/block/blockquote.ts +2 -1
- package/src/parser/block/extension/aside.test.ts +3 -3
- package/src/parser/block/extension/aside.ts +2 -1
- package/src/parser/block/extension/example.test.ts +3 -3
- package/src/parser/block/extension/example.ts +3 -1
- package/src/parser/{header.test.ts → block/header.test.ts} +3 -3
- package/src/parser/{header.ts → block/header.ts} +7 -7
- package/src/parser/block/reply/cite.ts +2 -3
- package/src/parser/block/reply/quote.ts +1 -2
- package/src/parser/block/reply.ts +1 -2
- package/src/parser/block.ts +6 -5
- package/src/parser/context.ts +6 -4
- package/src/parser/document.ts +22 -11
- package/src/parser/inline/annotation.test.ts +24 -0
- package/src/parser/inline/annotation.ts +20 -7
- package/src/parser/inline/extension/index.ts +1 -2
- package/src/parser/inline/extension/indexee.ts +1 -2
- package/src/parser/inline/extension/label.ts +10 -6
- package/src/parser/inline/html.ts +1 -2
- package/src/parser/inline/htmlentity.ts +5 -4
- package/src/parser/inline/media.ts +2 -3
- package/src/parser/inline/reference.ts +3 -3
- package/src/parser/inline.ts +2 -2
- package/src/parser/node.ts +0 -5
- package/src/parser/segment.ts +2 -2
- package/src/parser/source/escapable.ts +5 -6
- package/src/parser/source/str.ts +3 -2
- package/src/parser/source/text.ts +3 -4
- package/src/parser/source/unescapable.ts +3 -4
- package/src/parser/source.ts +2 -2
- package/src/parser/util.ts +9 -0
- package/src/parser/visibility.ts +4 -5
- package/src/processor/figure.test.ts +35 -24
- package/src/processor/figure.ts +8 -23
- package/src/processor/note.test.ts +71 -25
- package/src/processor/note.ts +28 -24
- package/src/util/toc.ts +2 -3
package/src/parser/block.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DocumentParser } from '../../markdown';
|
|
2
2
|
import { Segment } from './context';
|
|
3
3
|
import { List, Node } from '../combinator/parser';
|
|
4
4
|
import { union, lazy, always } from '../combinator';
|
|
5
|
-
import { header } from './header';
|
|
5
|
+
import { header } from './block/header';
|
|
6
6
|
import { emptysegment } from './source';
|
|
7
|
-
import { pagebreak } from './block/pagebreak';
|
|
8
7
|
import { heading } from './block/heading';
|
|
9
8
|
import { ulist } from './block/ulist';
|
|
10
9
|
import { olist } from './block/olist';
|
|
11
10
|
import { ilist } from './block/ilist';
|
|
12
11
|
import { dlist } from './block/dlist';
|
|
13
12
|
import { table } from './block/table';
|
|
13
|
+
import { pagebreak } from './block/pagebreak';
|
|
14
14
|
import { codeblock } from './block/codeblock';
|
|
15
15
|
import { mathblock } from './block/mathblock';
|
|
16
16
|
import { extension } from './block/extension';
|
|
@@ -25,14 +25,15 @@ import { paragraph } from './block/paragraph';
|
|
|
25
25
|
import { rnd0Z } from 'spica/random';
|
|
26
26
|
import { html } from 'typed-dom/dom';
|
|
27
27
|
|
|
28
|
-
export import BlockParser =
|
|
29
|
-
export import
|
|
28
|
+
export import BlockParser = DocumentParser.BlockParser;
|
|
29
|
+
export import HeaderParser = BlockParser.HeaderParser;
|
|
30
30
|
export import HeadingParser = BlockParser.HeadingParser;
|
|
31
31
|
export import UListParser = BlockParser.UListParser;
|
|
32
32
|
export import OListParser = BlockParser.OListParser;
|
|
33
33
|
export import IListParser = BlockParser.IListParser;
|
|
34
34
|
export import DListParser = BlockParser.DListParser;
|
|
35
35
|
export import TableParser = BlockParser.TableParser;
|
|
36
|
+
export import PagebreakParser = BlockParser.PagebreakParser;
|
|
36
37
|
export import CodeBlockParser = BlockParser.CodeBlockParser;
|
|
37
38
|
export import MathBlockParser = BlockParser.MathBlockParser;
|
|
38
39
|
export import ExtensionParser = BlockParser.ExtensionParser;
|
package/src/parser/context.ts
CHANGED
|
@@ -28,7 +28,9 @@ export class Input<M extends object = object> extends Ipt<M> {
|
|
|
28
28
|
clock: -1,
|
|
29
29
|
interval: 200,
|
|
30
30
|
recursions: [
|
|
31
|
-
|
|
31
|
+
// DOMの垂直的追加の繰り返しが加速的に異常に重くなる。
|
|
32
|
+
// ブラウザの問題でありアルゴリズムの計算量は問題ない。
|
|
33
|
+
10 || Recursion.document,
|
|
32
34
|
100 || Recursion.block,
|
|
33
35
|
100 || Recursion.inline,
|
|
34
36
|
100 || Recursion.bracket,
|
|
@@ -77,13 +79,13 @@ class RecursionCounter {
|
|
|
77
79
|
}
|
|
78
80
|
private readonly stack: number[] = [];
|
|
79
81
|
private index = 0;
|
|
80
|
-
public add(depth: number):
|
|
82
|
+
public add(depth: number): boolean {
|
|
81
83
|
const { stack } = this
|
|
82
84
|
for (; this.index > 0 && stack[this.index - 1] >= depth; --this.index);
|
|
83
|
-
// 内側から数えるので無効化処理できずエラーを投げるしかない。
|
|
84
|
-
if (this.index === this.limit) return new Error(`Too much recursion`);
|
|
85
85
|
stack[this.index] = depth;
|
|
86
86
|
++this.index;
|
|
87
|
+
// 内側から数えるので無効化処理できない。
|
|
88
|
+
return this.index <= this.limit;
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
|
package/src/parser/document.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DocumentParser } from '../../markdown';
|
|
2
2
|
import { Input, Recursion } from './context';
|
|
3
|
-
import { Parser, Result, Node } from '../combinator/parser';
|
|
3
|
+
import { Parser, Result, List, Node } from '../combinator/parser';
|
|
4
4
|
import { always, force, recursion } from '../combinator';
|
|
5
5
|
import { build } from './parser';
|
|
6
6
|
import { parser as segment } from './segment';
|
|
@@ -10,14 +10,14 @@ import { figure } from '../processor/figure';
|
|
|
10
10
|
import { note } from '../processor/note';
|
|
11
11
|
import { frag, html } from 'typed-dom/dom';
|
|
12
12
|
|
|
13
|
-
export const document:
|
|
13
|
+
export const document: DocumentParser = (() => {
|
|
14
14
|
interface Memory {
|
|
15
15
|
readonly interpolation: boolean;
|
|
16
16
|
readonly references: HTMLOListElement;
|
|
17
|
-
doc?: DocumentFragment;
|
|
18
17
|
orphan?: boolean;
|
|
18
|
+
doc?: DocumentFragment;
|
|
19
19
|
}
|
|
20
|
-
const
|
|
20
|
+
const document = build(segment, block);
|
|
21
21
|
return always<Parser<DocumentFragment | HTMLElement, Input<Memory>>>([
|
|
22
22
|
(input, output) => {
|
|
23
23
|
input.id =
|
|
@@ -29,24 +29,35 @@ export const document: MarkdownParser = (() => {
|
|
|
29
29
|
references: input.notes?.references ?? html('ol', { class: 'references' }),
|
|
30
30
|
};
|
|
31
31
|
output.push();
|
|
32
|
+
output.labels.push(new List());
|
|
33
|
+
output.annotations.push(new List());
|
|
34
|
+
output.references.push(new List());
|
|
32
35
|
return output.context;
|
|
33
36
|
},
|
|
34
|
-
recursion(Recursion.document, force(() =>
|
|
37
|
+
recursion(Recursion.document, force(() => document)),
|
|
35
38
|
(input, output) => {
|
|
36
39
|
assert(input.position === input.source.length);
|
|
37
40
|
const { memory } = input;
|
|
38
41
|
const doc = memory.doc = frag(unwrap(output.pop()));
|
|
39
42
|
output.append(new Node(doc));
|
|
40
43
|
assert(input.id !== '' || !doc.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
|
|
41
|
-
if (input.test && !input.local)
|
|
44
|
+
if (input.test && !input.local) {
|
|
45
|
+
output.labels.at(-2)!.import(output.labels.pop()!);
|
|
46
|
+
output.annotations.at(-2)!.import(output.annotations.pop()!);
|
|
47
|
+
output.references.at(-2)!.import(output.references.pop()!);
|
|
48
|
+
return Result.skip;
|
|
49
|
+
}
|
|
42
50
|
memory.orphan = !memory.references.parentNode;
|
|
43
51
|
memory.orphan && doc.appendChild(memory.references);
|
|
44
52
|
return output.context;
|
|
45
53
|
},
|
|
46
|
-
input =>
|
|
47
|
-
conv(figure(input.memory.doc!, input.memory, input)),
|
|
48
|
-
input =>
|
|
49
|
-
conv(note(input.memory.doc!,
|
|
54
|
+
(input, output) =>
|
|
55
|
+
conv(figure(input.memory.doc!, output.labels.pop()!, input.memory, input)),
|
|
56
|
+
(input, output) =>
|
|
57
|
+
conv(note(input.memory.doc!, {
|
|
58
|
+
annotations: output.annotations.pop()!,
|
|
59
|
+
references: output.references.pop()!,
|
|
60
|
+
}, input.memory, input)),
|
|
50
61
|
(input, output) => {
|
|
51
62
|
const { memory } = input;
|
|
52
63
|
memory.orphan && !memory.interpolation && memory.references.remove();
|
|
@@ -62,6 +62,30 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
62
62
|
assert.deepStrictEqual(inspect(parser, input('(((a)))')), [['<sup class="annotation"><span><span class="paren">(a)</span></span></sup>'], '']);
|
|
63
63
|
assert.deepStrictEqual(inspect(parser, input('((((a))))')), [['<sup class="annotation"><span><sup class="annotation"><span>a</span></sup></span></sup>'], '']);
|
|
64
64
|
assert.deepStrictEqual(inspect(parser, input('(([[a]]))')), [['<sup class="annotation"><span><sup class="reference"><span>a</span></sup></span></sup>'], '']);
|
|
65
|
+
assert.deepStrictEqual(
|
|
66
|
+
inspect(parser, input(`${'(('.repeat(2)}0${'))'.repeat(2)}`)),
|
|
67
|
+
[['<sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup>'], '']);
|
|
68
|
+
assert.deepStrictEqual(
|
|
69
|
+
inspect(parser, input(`${'(('.repeat(3)}0${'))'.repeat(3)}`)),
|
|
70
|
+
[['<span class="invalid"><sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup></span>'], '']);
|
|
71
|
+
assert.deepStrictEqual(
|
|
72
|
+
inspect(parser, input(`(${'(('.repeat(2)}0${'))'.repeat(2)}`)),
|
|
73
|
+
[['<span class="paren">(<sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup></span>'], '']);
|
|
74
|
+
assert.deepStrictEqual(
|
|
75
|
+
inspect(parser, input(`(${'(('.repeat(3)}0${'))'.repeat(3)}`)),
|
|
76
|
+
[['<span class="paren">(<span class="invalid"><sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup></span></span>'], '']);
|
|
77
|
+
assert.deepStrictEqual(
|
|
78
|
+
inspect(parser, input(`${'(('.repeat(2)}0${'))'.repeat(2)}${'(('.repeat(2)}0${'))'.repeat(2)}`)),
|
|
79
|
+
[['<sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup>', '<sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup>'], '']);
|
|
80
|
+
assert.deepStrictEqual(
|
|
81
|
+
inspect(parser, input(`${'(('.repeat(2)}0${'))'.repeat(2)}${'(('.repeat(3)}0${'))'.repeat(2)}`)),
|
|
82
|
+
[['<sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup>', '<span class="paren">(<span class="paren">(<sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup></span></span>'], '']);
|
|
83
|
+
assert.deepStrictEqual(
|
|
84
|
+
inspect(parser, input(`${'(('.repeat(2)}0${'))'.repeat(2)}${'(('.repeat(3)}0${'))'.repeat(3)}`)),
|
|
85
|
+
[['<sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup>', '<span class="invalid"><sup class="annotation"><span><sup class="annotation"><span>0</span></sup></span></sup></span>'], '']);
|
|
86
|
+
assert.deepStrictEqual(
|
|
87
|
+
inspect(parser, input(`${'(('.repeat(3)}0))((1))))))`)),
|
|
88
|
+
[['<span class="invalid"><sup class="annotation"><span><sup class="annotation"><span>0</span></sup><sup class="annotation"><span>1</span></sup></span></sup></span>'], '']);
|
|
65
89
|
});
|
|
66
90
|
|
|
67
91
|
});
|
|
@@ -6,7 +6,7 @@ import { inline } from '../inline';
|
|
|
6
6
|
import { bracketname } from './bracket';
|
|
7
7
|
import { repeat } from '../repeat';
|
|
8
8
|
import { beforeNonblank, trimBlankNodeEnd } from '../visibility';
|
|
9
|
-
import { unwrap } from '../util';
|
|
9
|
+
import { unwrap, invalid } from '../util';
|
|
10
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
11
11
|
|
|
12
12
|
// シグネチャ等生成のために構文木のツリーウォークを再帰的に行い指数計算量にならないよう
|
|
@@ -32,7 +32,7 @@ export const annotation: AnnotationParser = lazy(() => constraint(State.annotati
|
|
|
32
32
|
([, bs], _, output) => output.import(bs),
|
|
33
33
|
([, bs], _, output) => bs && output.import(bs.push(new Node(Command.Cancel)))))),
|
|
34
34
|
(nodes, input, output, lead, follow) => {
|
|
35
|
-
const { linebreak, recursion, resources } = input;
|
|
35
|
+
const { position, linebreak, range, recursion, resources } = input;
|
|
36
36
|
if (linebreak !== 0 || nodes.length === 0 || lead === 0 || follow % 2 === 0) {
|
|
37
37
|
nodes.unshift(new Node('('));
|
|
38
38
|
nodes.push(new Node(')'));
|
|
@@ -40,13 +40,26 @@ export const annotation: AnnotationParser = lazy(() => constraint(State.annotati
|
|
|
40
40
|
new Node(html('span', { class: bracketname(input, 1, 1) }, defrag(unwrap(nodes))))
|
|
41
41
|
]);
|
|
42
42
|
}
|
|
43
|
-
output.error ??= recursion.add(resources?.recursions[Recursion.inline] ?? resources?.recursions.at(-1));
|
|
44
43
|
input.position += 1;
|
|
45
|
-
|
|
46
|
-
new Node(html('
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
if (!recursion.add(resources?.recursions[Recursion.inline] ?? resources?.recursions.at(-1))) {
|
|
45
|
+
return new List([new Node(html('span',
|
|
46
|
+
{
|
|
47
|
+
class: 'invalid',
|
|
48
|
+
...invalid('annotation', 'syntax', 'Recursions must be two or fewer')
|
|
49
|
+
},
|
|
50
|
+
defrag(unwrap(trimBlankNodeEnd(nodes)))))]);
|
|
51
|
+
}
|
|
52
|
+
const el = html('sup', { class: 'annotation' }, [
|
|
53
|
+
html('span', defrag(unwrap(trimBlankNodeEnd(nodes))))
|
|
49
54
|
]);
|
|
55
|
+
for (let list = output.annotations.at(-1)!, node = list.last, pos = position - range, i = 0; ; node = node!.prev, ++i) {
|
|
56
|
+
if (node && node.position > pos) continue;
|
|
57
|
+
i === list.length
|
|
58
|
+
? list.unshift(new Node(el, pos))
|
|
59
|
+
: list.insert(new Node(el, pos), node?.next);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return new List([new Node(el)]);
|
|
50
63
|
},
|
|
51
64
|
(nodes, input, output, prefix, postfix) => {
|
|
52
65
|
assert(postfix === 0);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../inline';
|
|
2
2
|
import { State, Backtrack } from '../../context';
|
|
3
|
-
import { Flag } from '../../node';
|
|
4
3
|
import { List, Node } from '../../../combinator/parser';
|
|
5
4
|
import { union, inits, some, precedence, state, constraint, backtrack, validate, surround, setBacktrack, lazy, fmap } from '../../../combinator';
|
|
6
5
|
import { inline } from '../../inline';
|
|
@@ -53,7 +52,7 @@ export const signature: IndexParser.SignatureParser = lazy(() => validate('|', b
|
|
|
53
52
|
const { position, range, linebreak } = input;
|
|
54
53
|
const head = position - range;
|
|
55
54
|
const index = identity('index', undefined, ns.foldl((acc, { value }) => acc + value, ''))?.slice(7);
|
|
56
|
-
if (linebreak !== 0 || ns.head!.flags & Flag.blank || !index) {
|
|
55
|
+
if (linebreak !== 0 || ns.head!.flags & Node.Flag.blank || !index) {
|
|
57
56
|
return void setBacktrack(input, 2 | Backtrack.escapable, head);
|
|
58
57
|
}
|
|
59
58
|
return output.append(new Node(html('span', { class: 'indexer', 'data-index': index })));
|
|
@@ -5,11 +5,10 @@ import { define } from 'typed-dom/dom';
|
|
|
5
5
|
|
|
6
6
|
export function indexee<P extends Parser<HTMLElement, Input>>(parser: P): P;
|
|
7
7
|
export function indexee(parser: Parser<HTMLElement, Input>): Parser<HTMLElement, Input> {
|
|
8
|
-
return fmap(parser, (ns, { id
|
|
8
|
+
return fmap(parser, (ns, { id }) =>
|
|
9
9
|
ns.length === 1
|
|
10
10
|
? new List([new Node(define(ns.head!.value, {
|
|
11
11
|
id: identity('index', id, ns.head!.value),
|
|
12
|
-
class: local ? `${ns.head!.value.className} local`.trimStart() : undefined,
|
|
13
12
|
'data-index': null
|
|
14
13
|
}))])
|
|
15
14
|
: ns);
|
|
@@ -26,12 +26,16 @@ export const label: ExtensionParser.LabelParser = constraint(State.label, fmap(
|
|
|
26
26
|
backtrack(surround('[', body, ']', false, [1 | Backtrack.common])),
|
|
27
27
|
body,
|
|
28
28
|
]),
|
|
29
|
-
([{ value }]) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
([{ value }], _, output) => {
|
|
30
|
+
const label = html('a',
|
|
31
|
+
{
|
|
32
|
+
class: 'label',
|
|
33
|
+
'data-label': value.slice(value[1] === '-' ? 0 : 1).toLowerCase(),
|
|
34
|
+
},
|
|
35
|
+
value);
|
|
36
|
+
output.labels.at(-1)!.push(new Node(label));
|
|
37
|
+
return new List([new Node(label)]);
|
|
38
|
+
}));
|
|
35
39
|
|
|
36
40
|
export function number(label: string, base: string): string {
|
|
37
41
|
return isFixed(label)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { HTMLParser } from '../inline';
|
|
2
2
|
import { Input, Recursion } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/parser';
|
|
4
|
-
import { Flag } from '../node';
|
|
5
4
|
import { union, some, recursion, precedence, surround, open, match, lazy } from '../../combinator';
|
|
6
5
|
import { inline } from '../inline';
|
|
7
6
|
import { str } from '../source';
|
|
@@ -28,7 +27,7 @@ export const html: HTMLParser = lazy(() => union([
|
|
|
28
27
|
open(str(/ ?/y), str('>'), true),
|
|
29
28
|
true, [],
|
|
30
29
|
([as, bs = new List(), cs], input, output) =>
|
|
31
|
-
output.append(new Node(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs).import(cs))], new List(), new List(), input), as.head!.value === '<wbr' ? Flag.blank : Flag.none)),
|
|
30
|
+
output.append(new Node(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs).import(cs))], new List(), new List(), input), as.head!.position, as.head!.value === '<wbr' ? Node.Flag.blank : Node.Flag.none)),
|
|
32
31
|
([as, bs = new List()], input, output) =>
|
|
33
32
|
output.append(new Node(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs))], new List(), new List(), input)))),
|
|
34
33
|
match(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HTMLEntityParser, UnsafeHTMLEntityParser } from '../inline';
|
|
2
2
|
import { Backtrack } from '../context';
|
|
3
|
-
import {
|
|
3
|
+
import { isBlankHTMLEntityName } from '../node';
|
|
4
4
|
import { List, Node } from '../../combinator/parser';
|
|
5
5
|
import { union, surround, fmap } from '../../combinator';
|
|
6
6
|
import { str } from '../source';
|
|
@@ -15,15 +15,16 @@ export const unsafehtmlentity: UnsafeHTMLEntityParser = surround(
|
|
|
15
15
|
output.append(
|
|
16
16
|
new Node(
|
|
17
17
|
parser(as.head!.value + bs.head!.value + cs.head!.value),
|
|
18
|
-
|
|
18
|
+
as.head!.position,
|
|
19
|
+
isBlankHTMLEntityName(bs.head!.value) ? Node.Flag.blank : Node.Flag.none)),
|
|
19
20
|
([as, bs], _, output) =>
|
|
20
21
|
output.append(new Node(as.head!.value + (bs?.head?.value ?? ''))));
|
|
21
22
|
|
|
22
23
|
export const htmlentity: HTMLEntityParser = fmap(
|
|
23
24
|
union([unsafehtmlentity]),
|
|
24
|
-
([{ value, flags }]) => new List([
|
|
25
|
+
([{ value, position, flags }]) => new List([
|
|
25
26
|
value.length === 1 || value.at(-1) !== ';'
|
|
26
|
-
? new Node(value, flags)
|
|
27
|
+
? new Node(value, position, flags)
|
|
27
28
|
: new Node(html('span', {
|
|
28
29
|
class: 'invalid',
|
|
29
30
|
...invalid('htmlentity', 'syntax', 'Invalid HTML entity'),
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { MediaParser } from '../inline';
|
|
2
2
|
import { State, Recursion, Backtrack, Command } from '../context';
|
|
3
3
|
import { Result, List, Node } from '../../combinator/parser';
|
|
4
|
-
import { Flag } from '../node';
|
|
5
4
|
import { union, inits, tails, some, recursion, precedence, constraint, backtrack, surround, open, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
|
|
6
5
|
import { uri, option as linkoption, resolve, decode, parse } from './link';
|
|
7
6
|
import { attributes } from './html';
|
|
@@ -33,7 +32,7 @@ export const media: MediaParser = lazy(() => constraint(State.media, backtrack(o
|
|
|
33
32
|
true,
|
|
34
33
|
[3 | Backtrack.escapable, 2 | Backtrack.ruby],
|
|
35
34
|
([, ns = new List()], input, output) => {
|
|
36
|
-
if (input.linebreak !== 0 || ns.head?.flags! & Flag.blank || ns.head?.value?.[0].trimStart() === '') {
|
|
35
|
+
if (input.linebreak !== 0 || ns.head?.flags! & Node.Flag.blank || ns.head?.value?.[0].trimStart() === '') {
|
|
37
36
|
const head = input.position - input.range;
|
|
38
37
|
return void setBacktrack(input, 2 | Backtrack.escapable | Backtrack.ruby, head);
|
|
39
38
|
}
|
|
@@ -51,7 +50,7 @@ export const media: MediaParser = lazy(() => constraint(State.media, backtrack(o
|
|
|
51
50
|
nodes =>
|
|
52
51
|
nodes.length === 1
|
|
53
52
|
? new List<Node<List<Node<string>>>>([new Node(new List([new Node('')])), nodes.delete(nodes.head!)])
|
|
54
|
-
: new List<Node<List<Node<string>>>>([new Node(new List([new Node(nodes.head!.value.foldl((acc, { value }) => acc + value, '').trimEnd(), nodes.head!.value.head?.flags)])), nodes.delete(nodes.last!)])),
|
|
53
|
+
: new List<Node<List<Node<string>>>>([new Node(new List([new Node(nodes.head!.value.foldl((acc, { value }) => acc + value, '').trimEnd(), nodes.head!.position, nodes.head!.value.head?.flags)])), nodes.delete(nodes.last!)])),
|
|
55
54
|
([{ value: [{ value: text }] }, { value: params }], input) => {
|
|
56
55
|
assert(text === text.trim());
|
|
57
56
|
if (params.last!.value === Command.Cancel) {
|
|
@@ -26,9 +26,9 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
26
26
|
setBacktrack(input, 2 | Backtrack.link, head, 2);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
]));
|
|
29
|
+
const el = html('sup', attributes(ns), [html('span', defrag(unwrap(trimBlankNodeEnd(ns))))]);
|
|
30
|
+
output.references.at(-1)!.push(new Node(el));
|
|
31
|
+
return output.import(new List([new Node(el)]));
|
|
32
32
|
},
|
|
33
33
|
(_, input) => {
|
|
34
34
|
const { source, position, range, linebreak } = input;
|
package/src/parser/inline.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DocumentParser } from '../../markdown';
|
|
2
2
|
import { union, lazy } from '../combinator';
|
|
3
3
|
import { annotation } from './inline/annotation';
|
|
4
4
|
import { reference } from './inline/reference';
|
|
@@ -23,7 +23,7 @@ import { bracket } from './inline/bracket';
|
|
|
23
23
|
import { autolink } from './inline/autolink';
|
|
24
24
|
import { text, strs } from './source';
|
|
25
25
|
|
|
26
|
-
export import InlineParser =
|
|
26
|
+
export import InlineParser = DocumentParser.InlineParser;
|
|
27
27
|
export import AnnotationParser = InlineParser.AnnotationParser;
|
|
28
28
|
export import ReferenceParser = InlineParser.ReferenceParser;
|
|
29
29
|
export import TemplateParser = InlineParser.TemplateParser;
|
package/src/parser/node.ts
CHANGED
package/src/parser/segment.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DocumentParser } from '../../markdown';
|
|
2
2
|
import { Input, Segment } from './context';
|
|
3
3
|
import { Output, List, Node, run } from '../combinator/parser';
|
|
4
4
|
import { union, some } from '../combinator';
|
|
@@ -8,7 +8,7 @@ import { segment as mathblock } from './block/mathblock';
|
|
|
8
8
|
import { segment as extension } from './block/extension';
|
|
9
9
|
import { contentline, emptysegment } from './source';
|
|
10
10
|
|
|
11
|
-
import SegmentParser =
|
|
11
|
+
import SegmentParser = DocumentParser.SegmentParser;
|
|
12
12
|
|
|
13
13
|
export const parser: SegmentParser = union([
|
|
14
14
|
some(emptysegment),
|
|
@@ -2,7 +2,6 @@ import { EscapableSourceParser } from '../source';
|
|
|
2
2
|
import { Result, Node } from '../../combinator/parser';
|
|
3
3
|
import { spend } from '../../combinator';
|
|
4
4
|
import { Command } from '../context';
|
|
5
|
-
import { Flag } from '../node';
|
|
6
5
|
import { html } from 'typed-dom/dom';
|
|
7
6
|
|
|
8
7
|
export const escsource: EscapableSourceParser = (input, output) => {
|
|
@@ -15,23 +14,23 @@ export const escsource: EscapableSourceParser = (input, output) => {
|
|
|
15
14
|
case Command.Escape:
|
|
16
15
|
spend(input, output, 1);
|
|
17
16
|
input.position += 1;
|
|
18
|
-
return output.append(new Node(source.slice(position + 1, position + 2)));
|
|
17
|
+
return output.append(new Node(source.slice(position + 1, position + 2), position));
|
|
19
18
|
case '\\':
|
|
20
19
|
switch (source[position + 1]) {
|
|
21
20
|
case undefined:
|
|
22
21
|
case '\r':
|
|
23
22
|
case '\n':
|
|
24
|
-
return output.append(new Node(char));
|
|
23
|
+
return output.append(new Node(char, position));
|
|
25
24
|
default:
|
|
26
25
|
spend(input, output, 1);
|
|
27
26
|
input.position += 1;
|
|
28
|
-
return output.append(new Node(source.slice(position, position + 2)));
|
|
27
|
+
return output.append(new Node(source.slice(position, position + 2), position));
|
|
29
28
|
}
|
|
30
29
|
case '\r':
|
|
31
30
|
return Result.succ;
|
|
32
31
|
case '\n':
|
|
33
32
|
input.linebreak ||= source.length - position;
|
|
34
|
-
return output.append(new Node(html('br'), Flag.blank));
|
|
33
|
+
return output.append(new Node(html('br'), position, Node.Flag.blank));
|
|
35
34
|
default:
|
|
36
35
|
assert(char !== '\n');
|
|
37
36
|
let i = seek(source, position);
|
|
@@ -39,7 +38,7 @@ export const escsource: EscapableSourceParser = (input, output) => {
|
|
|
39
38
|
i -= position;
|
|
40
39
|
spend(input, output, i - 1);
|
|
41
40
|
input.position += i - 1;
|
|
42
|
-
return output.append(new Node(source.slice(position, input.position)));
|
|
41
|
+
return output.append(new Node(source.slice(position, input.position), position));
|
|
43
42
|
}
|
|
44
43
|
};
|
|
45
44
|
|
package/src/parser/source/str.ts
CHANGED
|
@@ -6,9 +6,10 @@ export function str(pattern: string | RegExp, after?: string | RegExp): StrParse
|
|
|
6
6
|
export function str(pattern: string | RegExp, after?: string | RegExp): Parser<string> {
|
|
7
7
|
const match = matcher(pattern, true, after ? tester(after, false) : undefined);
|
|
8
8
|
return (input, output) => {
|
|
9
|
+
const { position } = input;
|
|
9
10
|
const src = match(input, output);
|
|
10
11
|
if (src === undefined) return;
|
|
11
|
-
return output.append(new Node(src));
|
|
12
|
+
return output.append(new Node(src, position));
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -25,6 +26,6 @@ export function strs(char: string, min: number = 1, max: number = -1): Parser<st
|
|
|
25
26
|
}
|
|
26
27
|
if (cnt < min) return;
|
|
27
28
|
input.position = pos;
|
|
28
|
-
return output.append(new Node(source.slice(position, input.position)));
|
|
29
|
+
return output.append(new Node(source.slice(position, input.position), position));
|
|
29
30
|
};
|
|
30
31
|
}
|
|
@@ -2,7 +2,6 @@ import { TextParser, TxtParser } from '../source';
|
|
|
2
2
|
import { Result, Node } from '../../combinator/parser';
|
|
3
3
|
import { union, spend } from '../../combinator';
|
|
4
4
|
import { State, Command } from '../context';
|
|
5
|
-
import { Flag } from '../node';
|
|
6
5
|
import { isWhitespace } from './whitespace';
|
|
7
6
|
import { html } from 'typed-dom/dom';
|
|
8
7
|
|
|
@@ -27,13 +26,13 @@ export const text: TextParser = (input, output) => {
|
|
|
27
26
|
default:
|
|
28
27
|
spend(input, output, 1);
|
|
29
28
|
input.position += 1;
|
|
30
|
-
return output.append(new Node(source.slice(position + 1, input.position)));
|
|
29
|
+
return output.append(new Node(source.slice(position + 1, input.position), position));
|
|
31
30
|
}
|
|
32
31
|
case '\r':
|
|
33
32
|
return Result.succ;
|
|
34
33
|
case '\n':
|
|
35
34
|
input.linebreak ||= source.length - position;
|
|
36
|
-
return output.append(new Node(html('br'), Flag.blank));
|
|
35
|
+
return output.append(new Node(html('br'), position, Node.Flag.blank));
|
|
37
36
|
default:
|
|
38
37
|
assert(char !== '\n');
|
|
39
38
|
nonWhitespace.lastIndex = position + 1;
|
|
@@ -54,7 +53,7 @@ export const text: TextParser = (input, output) => {
|
|
|
54
53
|
input.position += i - 1;
|
|
55
54
|
const linestart = position === 0 || source[position - 1] === '\n';
|
|
56
55
|
if (position === input.position || s && !linestart || lineend) return Result.succ;
|
|
57
|
-
return output.append(new Node(source.slice(position, input.position)));
|
|
56
|
+
return output.append(new Node(source.slice(position, input.position), position));
|
|
58
57
|
}
|
|
59
58
|
};
|
|
60
59
|
|
|
@@ -2,7 +2,6 @@ import { UnescapableSourceParser } from '../source';
|
|
|
2
2
|
import { Result, Node } from '../../combinator/parser';
|
|
3
3
|
import { spend } from '../../combinator';
|
|
4
4
|
import { State, Command } from '../context';
|
|
5
|
-
import { Flag } from '../node';
|
|
6
5
|
import { nonWhitespace, canSkip, backToUrlHead, backToEmailHead } from './text';
|
|
7
6
|
import { html } from 'typed-dom/dom';
|
|
8
7
|
|
|
@@ -16,12 +15,12 @@ export const unescsource: UnescapableSourceParser = (input, output) => {
|
|
|
16
15
|
case Command.Escape:
|
|
17
16
|
spend(input, output, 1);
|
|
18
17
|
input.position += 1;
|
|
19
|
-
return output.append(new Node(source.slice(position + 1, position + 2)));
|
|
18
|
+
return output.append(new Node(source.slice(position + 1, position + 2), position));
|
|
20
19
|
case '\r':
|
|
21
20
|
return Result.succ;
|
|
22
21
|
case '\n':
|
|
23
22
|
input.linebreak ||= source.length - position;
|
|
24
|
-
return output.append(new Node(html('br'), Flag.blank));
|
|
23
|
+
return output.append(new Node(html('br'), position, Node.Flag.blank));
|
|
25
24
|
default:
|
|
26
25
|
assert(char !== '\n');
|
|
27
26
|
nonWhitespace.lastIndex = position + 1;
|
|
@@ -34,7 +33,7 @@ export const unescsource: UnescapableSourceParser = (input, output) => {
|
|
|
34
33
|
i -= position;
|
|
35
34
|
spend(input, output, i - 1);
|
|
36
35
|
input.position += i - 1;
|
|
37
|
-
return output.append(new Node(source.slice(position, input.position)));
|
|
36
|
+
return output.append(new Node(source.slice(position, input.position), position));
|
|
38
37
|
}
|
|
39
38
|
};
|
|
40
39
|
|
package/src/parser/source.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DocumentParser } from '../../markdown';
|
|
2
2
|
|
|
3
|
-
import SourceParser =
|
|
3
|
+
import SourceParser = DocumentParser.SourceParser;
|
|
4
4
|
export import TextParser = SourceParser.TextParser;
|
|
5
5
|
export import TxtParser = SourceParser.TxtParser;
|
|
6
6
|
export import EscapableSourceParser = SourceParser.EscapableSourceParser;
|
package/src/parser/util.ts
CHANGED
|
@@ -64,3 +64,12 @@ export function stringify(nodes: Iterable<HTMLElement | string>): string {
|
|
|
64
64
|
export function randomID(): string {
|
|
65
65
|
return `random-${rnd0Z(6)}`;
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
export function collect(target: ParentNode, selector:string): HTMLElement[] {
|
|
69
|
+
const acc = [];
|
|
70
|
+
for (let el = target.firstElementChild; el; el = el?.nextElementSibling) {
|
|
71
|
+
if (!el.matches(selector)) continue;
|
|
72
|
+
acc.push(el as HTMLElement);
|
|
73
|
+
}
|
|
74
|
+
return acc;
|
|
75
|
+
}
|
package/src/parser/visibility.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Parser, List, Node } from '../combinator/parser';
|
|
2
2
|
import { Input, Command } from './context';
|
|
3
|
-
import { Flag } from './node';
|
|
4
3
|
import { always, fmap } from '../combinator';
|
|
5
4
|
import { invisibleBlankHTMLEntityNames } from '../api/normalize';
|
|
6
5
|
import { isWhitespace } from './source';
|
|
@@ -81,7 +80,7 @@ export function isNonblankFirstLine(nodes: List<Node<HTMLElement | string>>): bo
|
|
|
81
80
|
if (nodes.length === 0) return true;
|
|
82
81
|
for (const node of nodes) {
|
|
83
82
|
if (isNonblank(node)) return true;
|
|
84
|
-
if (node.flags & Flag.blank && typeof node.value === 'object' && node.value.tagName === 'BR') break;
|
|
83
|
+
if (node.flags & Node.Flag.blank && typeof node.value === 'object' && node.value.tagName === 'BR') break;
|
|
85
84
|
}
|
|
86
85
|
return false;
|
|
87
86
|
}
|
|
@@ -90,7 +89,7 @@ export function isNonblankNodeStart(nodes: List<Node<HTMLElement | string>>): bo
|
|
|
90
89
|
return isNonblank(nodes.head!, 0);
|
|
91
90
|
}
|
|
92
91
|
function isNonblank({ value: node, flags }: Node<HTMLElement | string>, strpos?: number): boolean {
|
|
93
|
-
if (flags & Flag.blank) return false;
|
|
92
|
+
if (flags & Node.Flag.blank) return false;
|
|
94
93
|
if (typeof node !== 'string') return true;
|
|
95
94
|
const str = node && strpos !== undefined
|
|
96
95
|
? node[strpos >= 0 ? strpos : node.length + strpos]
|
|
@@ -131,11 +130,11 @@ export function trimBlankEnd<N extends HTMLElement>(parser: Parser<N>): Parser<s
|
|
|
131
130
|
return fmap(parser, trimBlankNodeEnd);
|
|
132
131
|
}
|
|
133
132
|
export function trimBlankNodeEnd<N extends HTMLElement>(nodes: List<Node<string | N>>): List<Node<string | N>> {
|
|
134
|
-
const skip = nodes.last && ~nodes.last.flags & Flag.blank && typeof nodes.last.value === 'object'
|
|
133
|
+
const skip = nodes.last && ~nodes.last.flags & Node.Flag.blank && typeof nodes.last.value === 'object'
|
|
135
134
|
? nodes.last.value.className === 'indexer'
|
|
136
135
|
: false;
|
|
137
136
|
for (let node = skip ? nodes.last?.prev : nodes.last; node;) {
|
|
138
|
-
if (~node.flags & Flag.blank) {
|
|
137
|
+
if (~node.flags & Node.Flag.blank) {
|
|
139
138
|
if (typeof node.value === 'string') {
|
|
140
139
|
const str = node.value.trimEnd();
|
|
141
140
|
if (str.length > 0) {
|