securemark 0.294.0 → 0.294.1
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 +4 -0
- package/dist/index.js +164 -119
- package/package.json +2 -2
- package/src/combinator/control/constraint/contract.ts +2 -2
- package/src/combinator/control/constraint/line.ts +2 -2
- package/src/combinator/control/manipulation/indent.ts +3 -7
- package/src/combinator/control/manipulation/scope.ts +4 -6
- package/src/combinator/control/manipulation/surround.ts +5 -8
- package/src/combinator/control/monad/bind.ts +2 -3
- package/src/combinator/data/data.ts +2 -2
- package/src/combinator/data/parser/context.test.ts +4 -4
- package/src/combinator/data/parser/inits.ts +4 -6
- package/src/combinator/data/parser/sequence.ts +4 -6
- package/src/combinator/data/parser/some.ts +5 -7
- package/src/combinator/data/parser.ts +3 -21
- package/src/debug.test.ts +2 -2
- package/src/parser/api/bind.ts +5 -5
- package/src/parser/api/header.ts +2 -2
- package/src/parser/api/normalize.ts +2 -2
- package/src/parser/api/parse.ts +2 -2
- package/src/parser/block/codeblock.ts +2 -2
- package/src/parser/block/extension/example.ts +2 -2
- package/src/parser/block/extension/figure.ts +1 -1
- package/src/parser/block/extension/message.ts +2 -2
- package/src/parser/block/extension/table.ts +2 -2
- package/src/parser/block.ts +4 -0
- package/src/parser/inline/emstrong.ts +5 -5
- package/src/parser/inline/reference.ts +2 -2
- package/src/parser/inline/ruby.ts +4 -4
- package/src/parser/processor/note.ts +2 -2
- package/src/parser/segment.ts +4 -4
- package/src/parser/source/line.ts +5 -0
- package/src/parser/source/str.ts +1 -1
- package/src/parser/util.ts +5 -5
- package/src/parser/visibility.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securemark",
|
|
3
|
-
"version": "0.294.
|
|
3
|
+
"version": "0.294.1",
|
|
4
4
|
"description": "Secure markdown renderer working on browsers for user input data.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/falsandtru/securemark",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"LICENSE"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"spica": "0.0.
|
|
31
|
+
"spica": "0.0.809"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/dompurify": "3.0.5",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, Input, List, Data, Ctx, Node, Context,
|
|
1
|
+
import { Parser, Input, List, Data, Ctx, Node, Context, failsafe } from '../../data/parser';
|
|
2
2
|
import { matcher } from '../../../combinator';
|
|
3
3
|
|
|
4
4
|
//export function contract<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], parser: P, cond: (nodes: readonly Data<P>[], rest: string) => boolean): P;
|
|
@@ -37,7 +37,7 @@ export function verify<N>(parser: Parser<N>, cond: (nodes: List<Data<N>>, contex
|
|
|
37
37
|
if (position === source.length) return;
|
|
38
38
|
const result = parser(input);
|
|
39
39
|
assert(context.position > position || !result);
|
|
40
|
-
if (result && !cond(
|
|
40
|
+
if (result && !cond(result, context)) return;
|
|
41
41
|
return result;
|
|
42
42
|
});
|
|
43
43
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, input,
|
|
1
|
+
import { Parser, input, failsafe } from '../../data/parser';
|
|
2
2
|
|
|
3
3
|
export function line<P extends Parser<unknown>>(parser: P): P;
|
|
4
4
|
export function line<N>(parser: Parser<N>): Parser<N> {
|
|
@@ -18,7 +18,7 @@ export function line<N>(parser: Parser<N>): Parser<N> {
|
|
|
18
18
|
if (result === undefined) return;
|
|
19
19
|
if (!isBlank(source.slice(context.position, position + line.length))) return;
|
|
20
20
|
context.position = position + line.length;
|
|
21
|
-
return
|
|
21
|
+
return result;
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, List, Data, subinput,
|
|
1
|
+
import { Parser, List, Data, subinput, failsafe } from '../../data/parser';
|
|
2
2
|
import { some } from '../../data/parser/some';
|
|
3
3
|
import { block } from '../constraint/block';
|
|
4
4
|
import { line } from '../constraint/line';
|
|
@@ -26,14 +26,10 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
|
|
|
26
26
|
context.position = source.length;
|
|
27
27
|
return new List([new Data(source.slice(position))]);
|
|
28
28
|
}))),
|
|
29
|
-
([indent]) => indent.length * 2 + +(indent[0] === ' '),
|
|
29
|
+
([indent]) => indent.length <= 16 ? indent.length * 2 + +(indent[0] === ' ') : -1, [])), separation),
|
|
30
30
|
(lines, context) => {
|
|
31
31
|
assert(parser = parser as Parser<N>);
|
|
32
|
-
|
|
33
|
-
assert(result);
|
|
34
|
-
return result
|
|
35
|
-
? eval(result)
|
|
36
|
-
: undefined;
|
|
32
|
+
return parser(subinput(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context));
|
|
37
33
|
}));
|
|
38
34
|
}
|
|
39
35
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, Context, input,
|
|
1
|
+
import { Parser, Context, input, failsafe } from '../../data/parser';
|
|
2
2
|
import { matcher } from '../../../combinator';
|
|
3
3
|
|
|
4
4
|
export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P): P;
|
|
@@ -8,7 +8,7 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>): Parser<N> {
|
|
|
8
8
|
return failsafe(({ context }) => {
|
|
9
9
|
const { source, position } = context;
|
|
10
10
|
if (position === source.length) return;
|
|
11
|
-
const src =
|
|
11
|
+
const src = match({ context })?.head?.value ?? '';
|
|
12
12
|
assert(source.startsWith(src, position));
|
|
13
13
|
if (src === '') return;
|
|
14
14
|
context.range = src.length;
|
|
@@ -20,8 +20,7 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>): Parser<N> {
|
|
|
20
20
|
assert(context.position > position || !result);
|
|
21
21
|
context.source = source;
|
|
22
22
|
context.offset -= position;
|
|
23
|
-
|
|
24
|
-
return eval(result);
|
|
23
|
+
return result;
|
|
25
24
|
});
|
|
26
25
|
}
|
|
27
26
|
|
|
@@ -47,7 +46,6 @@ export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>): Parser<N>
|
|
|
47
46
|
assert(context.position > position || !res2);
|
|
48
47
|
context.source = source;
|
|
49
48
|
context.offset -= position;
|
|
50
|
-
|
|
51
|
-
return eval(res2);
|
|
49
|
+
return res2;
|
|
52
50
|
});
|
|
53
51
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser,
|
|
1
|
+
import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, failsafe } from '../../data/parser';
|
|
2
2
|
import { matcher, clear } from '../../../combinator';
|
|
3
3
|
|
|
4
4
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
@@ -52,29 +52,26 @@ export function surround<N>(
|
|
|
52
52
|
if (position === source.length) return;
|
|
53
53
|
const { linebreak } = context;
|
|
54
54
|
context.linebreak = 0;
|
|
55
|
-
const
|
|
55
|
+
const nodesO = opener(input);
|
|
56
56
|
assert(context.position >= position);
|
|
57
|
-
const nodesO = eval(resultO);
|
|
58
57
|
if (!nodesO) {
|
|
59
58
|
return void revert(context, linebreak);
|
|
60
59
|
}
|
|
61
60
|
if (isBacktrack(context, backtracks, position, context.position - position || 1)) {
|
|
62
61
|
return void revert(context, linebreak);
|
|
63
62
|
}
|
|
64
|
-
const
|
|
63
|
+
const nodesM = context.position < source.length ? parser(input) : undefined;
|
|
65
64
|
assert(context.position >= position);
|
|
66
65
|
context.range = context.position - position;
|
|
67
|
-
|
|
68
|
-
if (!resultM && !optional) {
|
|
66
|
+
if (!nodesM && !optional) {
|
|
69
67
|
setBacktrack(context, backtracks, position);
|
|
70
68
|
const result = g?.([nodesO, nodesM], context);
|
|
71
69
|
revert(context, linebreak);
|
|
72
70
|
return result;
|
|
73
71
|
}
|
|
74
|
-
const
|
|
72
|
+
const nodesC = nodesM || optional ? closer(input) : undefined;
|
|
75
73
|
assert(context.position >= position);
|
|
76
74
|
context.range = context.position - position;
|
|
77
|
-
const nodesC = eval(resultC);
|
|
78
75
|
if (!nodesC) {
|
|
79
76
|
setBacktrack(context, backtracks, position);
|
|
80
77
|
const result = g?.([nodesO, nodesM], context);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser,
|
|
1
|
+
import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, failsafe } from '../../data/parser';
|
|
2
2
|
|
|
3
3
|
export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: List<Data<SubNode<P>>>, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
|
|
4
4
|
export function bind<P extends Parser<unknown>>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
|
|
@@ -14,9 +14,8 @@ export function bind<N, U>(parser: Parser<N>, f: (nodes: List<Data<N>>, context:
|
|
|
14
14
|
assert(context.position > position || !res1);
|
|
15
15
|
if (res1 === undefined) return;
|
|
16
16
|
context.range = context.position - position;
|
|
17
|
-
const res2 = f(
|
|
17
|
+
const res2 = f(res1, context);
|
|
18
18
|
assert(context.position > position || !res2);
|
|
19
|
-
if (res2 === undefined) return;
|
|
20
19
|
return context.position > position
|
|
21
20
|
? res2
|
|
22
21
|
: undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { CtxOptions, Parser } from './parser';
|
|
2
2
|
|
|
3
|
-
export class List<N extends List.Node = List.Node> {
|
|
3
|
+
export class List<N extends List.Node = List.Node, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any> {
|
|
4
4
|
constructor(nodes?: ArrayLike<N>) {
|
|
5
5
|
if (nodes === undefined) return;
|
|
6
6
|
for (let i = 0; i < nodes.length; ++i) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, List, Data, Ctx, CtxOptions, input
|
|
1
|
+
import { Parser, List, Data, Ctx, CtxOptions, input } from '../parser';
|
|
2
2
|
import { some } from './some';
|
|
3
3
|
import { reset, context, creation } from './context';
|
|
4
4
|
import { unwrap } from '../../../parser/util';
|
|
@@ -18,7 +18,7 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
18
18
|
it('root', () => {
|
|
19
19
|
const base: Context = { resources: { clock: 3, recursions: [1] } };
|
|
20
20
|
const ctx: Context = {};
|
|
21
|
-
assert.deepStrictEqual([...unwrap(
|
|
21
|
+
assert.deepStrictEqual([...unwrap(reset(base, parser)(input('123', ctx))!)], [3, 2, 1]);
|
|
22
22
|
assert(base.resources?.clock === 3);
|
|
23
23
|
assert(ctx.resources?.clock === undefined);
|
|
24
24
|
assert.throws(() => reset(base, parser)(input('1234', ctx)));
|
|
@@ -28,7 +28,7 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
28
28
|
it('node', () => {
|
|
29
29
|
const base: Context = { resources: { clock: 3, recursions: [1] } };
|
|
30
30
|
const ctx: Context = { resources: { clock: 2, recursions: [1] } };
|
|
31
|
-
assert.deepStrictEqual([...unwrap(
|
|
31
|
+
assert.deepStrictEqual([...unwrap(reset(base, parser)(input('1', ctx))!)], [2]);
|
|
32
32
|
assert(base.resources?.clock === 3);
|
|
33
33
|
assert(ctx.resources?.clock === 1);
|
|
34
34
|
assert.throws(() => reset(base, parser)(input('12', ctx)));
|
|
@@ -47,7 +47,7 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
47
47
|
it('', () => {
|
|
48
48
|
const base: Context = { status: true };
|
|
49
49
|
const ctx: Context = { resources: { clock: 2, recursions: [1] } };
|
|
50
|
-
assert.deepStrictEqual([...unwrap(
|
|
50
|
+
assert.deepStrictEqual([...unwrap(context(base, parser)(input('1', ctx))!)], [true]);
|
|
51
51
|
assert(base.resources?.clock === undefined);
|
|
52
52
|
assert(ctx.resources?.clock === 1);
|
|
53
53
|
assert(ctx.status === undefined);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode,
|
|
1
|
+
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode, Ctx } from '../parser';
|
|
2
2
|
|
|
3
3
|
export function inits<P extends Parser<unknown, Ctx>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
4
4
|
export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
|
|
@@ -13,13 +13,11 @@ export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: Lis
|
|
|
13
13
|
if (context.delimiters?.match(input)) break;
|
|
14
14
|
const result = parsers[i](input);
|
|
15
15
|
if (result === undefined) break;
|
|
16
|
-
nodes = nodes
|
|
17
|
-
|
|
18
|
-
: eval(result);
|
|
19
|
-
if (resume?.(eval(result)) === false) break;
|
|
16
|
+
nodes = nodes?.import(result) ?? result;
|
|
17
|
+
if (resume?.(result) === false) break;
|
|
20
18
|
}
|
|
21
19
|
assert(context.position >= position);
|
|
22
|
-
return
|
|
20
|
+
return context.position > position
|
|
23
21
|
? nodes
|
|
24
22
|
: undefined;
|
|
25
23
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode
|
|
1
|
+
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
|
|
3
3
|
export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
4
4
|
export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
|
|
@@ -13,13 +13,11 @@ export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes:
|
|
|
13
13
|
if (context.delimiters?.match(input)) return;
|
|
14
14
|
const result = parsers[i](input);
|
|
15
15
|
if (result === undefined) return;
|
|
16
|
-
nodes = nodes
|
|
17
|
-
|
|
18
|
-
: eval(result);
|
|
19
|
-
if (resume?.(eval(result)) === false) return;
|
|
16
|
+
nodes = nodes?.import(result) ?? result;
|
|
17
|
+
if (resume?.(result) === false) return;
|
|
20
18
|
}
|
|
21
19
|
assert(context.position >= position);
|
|
22
|
-
return
|
|
20
|
+
return context.position > position
|
|
23
21
|
? nodes
|
|
24
22
|
: undefined;
|
|
25
23
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, List, Data
|
|
1
|
+
import { Parser, List, Data } from '../parser';
|
|
2
2
|
import { Delimiters } from './context/delimiter';
|
|
3
3
|
|
|
4
4
|
type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
|
|
@@ -23,22 +23,20 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
23
23
|
context.delimiters ??= new Delimiters();
|
|
24
24
|
context.delimiters.push(delims);
|
|
25
25
|
}
|
|
26
|
-
while
|
|
27
|
-
|
|
26
|
+
// whileは数倍遅い
|
|
27
|
+
for (; context.position < source.length;) {
|
|
28
28
|
if (match(input)) break;
|
|
29
29
|
if (context.delimiters?.match(input)) break;
|
|
30
30
|
const result = parser(input);
|
|
31
31
|
if (result === undefined) break;
|
|
32
|
-
nodes = nodes
|
|
33
|
-
? nodes.import(eval(result))
|
|
34
|
-
: eval(result);
|
|
32
|
+
nodes = nodes?.import(result) ?? result;
|
|
35
33
|
if (limit >= 0 && context.position - position > limit) break;
|
|
36
34
|
}
|
|
37
35
|
if (delims.length > 0) {
|
|
38
36
|
context.delimiters!.pop(delims.length);
|
|
39
37
|
}
|
|
40
38
|
assert(context.position >= position);
|
|
41
|
-
return
|
|
39
|
+
return context.position > position
|
|
42
40
|
? nodes
|
|
43
41
|
: undefined;
|
|
44
42
|
};
|
|
@@ -7,8 +7,7 @@ export interface Input<C extends CtxOptions = CtxOptions> {
|
|
|
7
7
|
readonly context: C & Ctx;
|
|
8
8
|
}
|
|
9
9
|
export type Result<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
|
|
10
|
-
=
|
|
11
|
-
| List<Data<N>>
|
|
10
|
+
= List<Data<N>, C, D>
|
|
12
11
|
| undefined;
|
|
13
12
|
export { List };
|
|
14
13
|
export class Data<N> implements List.Node {
|
|
@@ -78,28 +77,11 @@ export function clean<C extends Ctx>(context: C): C {
|
|
|
78
77
|
return context;
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
export { eval_ as eval };
|
|
82
|
-
function eval_<N>(result: NonNullable<Result<N>>, default_?: List<Data<N>>): List<Data<N>>;
|
|
83
|
-
function eval_<N>(result: Result<N>, default_: List<Data<N>>): List<Data<N>>;
|
|
84
|
-
function eval_<N>(result: Result<N>, default_?: undefined): List<Data<N>> | undefined;
|
|
85
|
-
function eval_<N>(result: Result<N>, default_?: List<Data<N>>): List<Data<N>> | undefined {
|
|
86
|
-
assert(!Array.isArray(result));
|
|
87
|
-
return result
|
|
88
|
-
? result as List<Data<N>>
|
|
89
|
-
: default_;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
80
|
export function failsafe<P extends Parser<unknown>>(parser: P): P;
|
|
93
81
|
export function failsafe<N>(parser: Parser<N>): Parser<N> {
|
|
94
82
|
assert(parser);
|
|
95
83
|
return input => {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
const result = parser(input);
|
|
99
|
-
if (result === undefined) {
|
|
100
|
-
context.source = source;
|
|
101
|
-
context.position = position;
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
84
|
+
const position = input.context.position;
|
|
85
|
+
return parser(input) ?? (input.context.position = position, undefined);
|
|
104
86
|
};
|
|
105
87
|
}
|
package/src/debug.test.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Result, Ctx
|
|
1
|
+
import { Result, Ctx } from './combinator/data/parser';
|
|
2
2
|
import { html, define } from 'typed-dom/dom';
|
|
3
3
|
import { querySelectorWith, querySelectorAllWith } from 'typed-dom/query';
|
|
4
4
|
|
|
5
5
|
export function inspect(result: Result<DocumentFragment | HTMLElement | string>, ctx: Ctx, until: number | string = Infinity): [string[], string] | undefined {
|
|
6
6
|
return result && [
|
|
7
|
-
|
|
7
|
+
result.foldl<string[]>((acc, { value: node }) => {
|
|
8
8
|
assert(node);
|
|
9
9
|
if (typeof node === 'string') return acc.push(node), acc;
|
|
10
10
|
if (node instanceof DocumentFragment) return acc.push(html('div', [node]).innerHTML), acc;
|
package/src/parser/api/bind.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParserSettings, Progress } from '../../..';
|
|
2
2
|
import { MarkdownParser } from '../../../markdown';
|
|
3
|
-
import { input
|
|
3
|
+
import { input } from '../../combinator/data/parser';
|
|
4
4
|
import { segment } from '../segment';
|
|
5
5
|
import { header } from '../header';
|
|
6
6
|
import { block } from '../block';
|
|
@@ -75,7 +75,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
75
75
|
for (; index < sourceSegments.length - last; ++index) {
|
|
76
76
|
assert(rev === revision);
|
|
77
77
|
const seg = sourceSegments[index];
|
|
78
|
-
const es =
|
|
78
|
+
const es = (header(input(seg, { header: index === 0 } as MarkdownParser.Options)) || block(input(seg, context)))
|
|
79
79
|
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? [];
|
|
80
80
|
blocks.splice(index, 0, [seg, es, url]);
|
|
81
81
|
if (es.length === 0) continue;
|
|
@@ -83,7 +83,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
83
83
|
// Therefore any `base` node will never be unavailable by deletions until all the dependent `el` nodes are added.
|
|
84
84
|
push(adds, es.map(el => [el, base] as const));
|
|
85
85
|
adds.reverse();
|
|
86
|
-
|
|
86
|
+
for (; adds.length > 0;) {
|
|
87
87
|
assert(rev === revision);
|
|
88
88
|
const [el, base] = adds.pop()!;
|
|
89
89
|
target.insertBefore(el, base);
|
|
@@ -100,7 +100,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
100
100
|
}
|
|
101
101
|
assert(blocks.length === sourceSegments.length);
|
|
102
102
|
adds.reverse();
|
|
103
|
-
|
|
103
|
+
for (; adds.length > 0;) {
|
|
104
104
|
assert(rev === revision);
|
|
105
105
|
const [el, base] = adds.pop()!;
|
|
106
106
|
target.insertBefore(el, base);
|
|
@@ -109,7 +109,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
109
109
|
if (rev !== revision) return yield { type: 'cancel' };
|
|
110
110
|
}
|
|
111
111
|
dels.reverse();
|
|
112
|
-
|
|
112
|
+
for (; dels.length > 0;) {
|
|
113
113
|
assert(rev === revision);
|
|
114
114
|
const [el] = dels.pop()!;
|
|
115
115
|
el.parentNode?.removeChild(el);
|
package/src/parser/api/header.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { input
|
|
1
|
+
import { input } from '../../combinator/data/parser';
|
|
2
2
|
import { header as h } from '../header';
|
|
3
3
|
|
|
4
4
|
export function header(source: string): string {
|
|
@@ -14,7 +14,7 @@ export function headers(source: string): string[] {
|
|
|
14
14
|
function parse(source: string): [HTMLElement, number] | [] {
|
|
15
15
|
const i = input(source, {});
|
|
16
16
|
const result = h(i);
|
|
17
|
-
const el =
|
|
17
|
+
const el = result?.head?.value;
|
|
18
18
|
return el?.tagName === 'ASIDE'
|
|
19
19
|
? [el, i.context.position]
|
|
20
20
|
: [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { input
|
|
1
|
+
import { input } from '../../combinator/data/parser';
|
|
2
2
|
import { unsafehtmlentity } from '../inline/htmlentity';
|
|
3
3
|
|
|
4
4
|
const UNICODE_REPLACEMENT_CHARACTER = '\uFFFD';
|
|
@@ -60,7 +60,7 @@ export const invisibleHTMLEntityNames = [
|
|
|
60
60
|
] as const;
|
|
61
61
|
const unreadableHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames.slice(2);
|
|
62
62
|
const unreadableEscapableCharacters = unreadableHTMLEntityNames
|
|
63
|
-
.map(name =>
|
|
63
|
+
.map(name => unsafehtmlentity(input(`&${name};`, {}))!.head!.value);
|
|
64
64
|
assert(unreadableEscapableCharacters.length === unreadableHTMLEntityNames.length);
|
|
65
65
|
assert(unreadableEscapableCharacters.every(c => c.length === 1));
|
|
66
66
|
const unreadableEscapableCharacter = new RegExp(`[${unreadableEscapableCharacters.join('')}]`, 'g');
|
package/src/parser/api/parse.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParserOptions } from '../../..';
|
|
2
2
|
import { MarkdownParser } from '../../../markdown';
|
|
3
|
-
import { input
|
|
3
|
+
import { input } from '../../combinator/data/parser';
|
|
4
4
|
import { segment } from '../segment';
|
|
5
5
|
import { header } from '../header';
|
|
6
6
|
import { block } from '../block';
|
|
@@ -35,7 +35,7 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
|
|
|
35
35
|
let index = 0;
|
|
36
36
|
for (const seg of segment(source)) {
|
|
37
37
|
node.append(
|
|
38
|
-
...
|
|
38
|
+
...(header(input(seg, { header: index++ === 0 } as MarkdownParser.Context)) || block(input(seg, context)))
|
|
39
39
|
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? []);
|
|
40
40
|
}
|
|
41
41
|
assert(opts.id !== '' || !node.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CodeBlockParser } from '../block';
|
|
2
|
-
import { List, Data, subinput
|
|
2
|
+
import { List, Data, subinput } from '../../combinator/data/parser';
|
|
3
3
|
import { block, fence, clear, fmap } from '../../combinator';
|
|
4
4
|
import { autolink } from '../autolink';
|
|
5
5
|
import { unwrap, invalid } from '../util';
|
|
@@ -73,6 +73,6 @@ export const codeblock: CodeBlockParser = block(fmap(
|
|
|
73
73
|
params.lang
|
|
74
74
|
? context.caches?.code?.get(`${params.lang ?? ''}\n${body.slice(0, -1)}`)?.cloneNode(true).childNodes ||
|
|
75
75
|
body.slice(0, -1) || undefined
|
|
76
|
-
: defrag(unwrap(
|
|
76
|
+
: defrag(unwrap(autolink(subinput(body.slice(0, -1), context)))));
|
|
77
77
|
return new List([new Data(el)]);
|
|
78
78
|
}));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
2
|
import { Recursion } from '../../context';
|
|
3
|
-
import { List, Data, subinput
|
|
3
|
+
import { List, Data, subinput } from '../../../combinator/data/parser';
|
|
4
4
|
import { recursion, block, fence, fmap } from '../../../combinator';
|
|
5
5
|
import { mathblock } from '../mathblock';
|
|
6
6
|
import { unwrap, invalid } from '../../util';
|
|
@@ -49,7 +49,7 @@ export const example: ExtensionParser.ExampleParser = recursion(Recursion.block,
|
|
|
49
49
|
new Data(html('aside', { class: 'example', 'data-type': 'math' }, [
|
|
50
50
|
html('pre', { translate: 'no' }, body.slice(0, -1)),
|
|
51
51
|
html('hr'),
|
|
52
|
-
|
|
52
|
+
mathblock(subinput(`$$\n${body}$$`, context))!.head!.value,
|
|
53
53
|
])),
|
|
54
54
|
]);
|
|
55
55
|
default:
|
|
@@ -44,7 +44,7 @@ export const segment: FigureParser.SegmentParser = block(match(
|
|
|
44
44
|
]),
|
|
45
45
|
]),
|
|
46
46
|
closer),
|
|
47
|
-
([, fence]) => fence.length,
|
|
47
|
+
([, fence]) => fence.length <= 16 ? fence.length : -1, [])));
|
|
48
48
|
|
|
49
49
|
export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
50
50
|
convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
|
-
import { List, Data, subinput
|
|
2
|
+
import { List, Data, subinput } from '../../../combinator/data/parser';
|
|
3
3
|
import { union, block, fence, fmap } from '../../../combinator';
|
|
4
4
|
import { segment } from '../../segment';
|
|
5
5
|
import { emptyline } from '../../source';
|
|
@@ -58,7 +58,7 @@ export const message: MessageParser = block(fmap(
|
|
|
58
58
|
},
|
|
59
59
|
[...segment(body)].reduce(
|
|
60
60
|
(acc, seg) =>
|
|
61
|
-
push(acc, unwrap(
|
|
61
|
+
push(acc, unwrap(content(subinput(seg, context)))),
|
|
62
62
|
[html('h1', title(type))])))
|
|
63
63
|
]);
|
|
64
64
|
}));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { max, min } from 'spica/alias';
|
|
2
2
|
import { ExtensionParser } from '../../block';
|
|
3
|
-
import { List, Data, subinput
|
|
3
|
+
import { List, Data, subinput } from '../../../combinator/data/parser';
|
|
4
4
|
import { union, subsequence, inits, some, block, line, validate, fence, rewrite, clear, surround, open, convert, dup, lazy, fmap } from '../../../combinator';
|
|
5
5
|
import { inline, medialink, media, lineshortmedia } from '../../inline';
|
|
6
6
|
import { str, anyline, emptyline, contentline } from '../../source';
|
|
@@ -42,7 +42,7 @@ export const table: TableParser = block(fmap(
|
|
|
42
42
|
switch (type) {
|
|
43
43
|
case 'grid':
|
|
44
44
|
case undefined:
|
|
45
|
-
return (
|
|
45
|
+
return (parser(subinput(body, context)) ?? new List([new Data(html('table'))]))
|
|
46
46
|
.foldl(
|
|
47
47
|
(acc, { value }) => acc.push(new Data(define(value, { 'data-type': type }))) && acc,
|
|
48
48
|
new List());
|
package/src/parser/block.ts
CHANGED
|
@@ -61,6 +61,10 @@ export const block: BlockParser = reset(
|
|
|
61
61
|
if (position === source.length) return;
|
|
62
62
|
const fst = source[position];
|
|
63
63
|
switch (fst) {
|
|
64
|
+
case '\n':
|
|
65
|
+
assert(source.trim() === '');
|
|
66
|
+
input.context.position = source.length;
|
|
67
|
+
return new List();
|
|
64
68
|
case '=':
|
|
65
69
|
if (source.startsWith('===', position)) return pagebreak(input);
|
|
66
70
|
break;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EmStrongParser, EmphasisParser, StrongParser } from '../inline';
|
|
2
2
|
import { Recursion, Command } from '../context';
|
|
3
|
-
import { Result, List, Data, Node, Context, IntermediateParser
|
|
3
|
+
import { Result, List, Data, Node, Context, IntermediateParser } from '../../combinator/data/parser';
|
|
4
4
|
import { union, some, recursion, precedence, validate, surround, open, lazy, bind } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { strong } from './strong';
|
|
@@ -105,7 +105,7 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
|
|
|
105
105
|
case 0:
|
|
106
106
|
break;
|
|
107
107
|
case 1:
|
|
108
|
-
nodes =
|
|
108
|
+
nodes = bind<EmphasisParser>(
|
|
109
109
|
subemphasis,
|
|
110
110
|
ds => {
|
|
111
111
|
const { source } = context;
|
|
@@ -117,11 +117,11 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
|
|
|
117
117
|
return prepend('*', nodes.import(ds));
|
|
118
118
|
}
|
|
119
119
|
})
|
|
120
|
-
({ context })
|
|
120
|
+
({ context }) ?? prepend('*', nodes);
|
|
121
121
|
prefix -= 1;
|
|
122
122
|
break;
|
|
123
123
|
case 2:
|
|
124
|
-
nodes =
|
|
124
|
+
nodes = bind<StrongParser>(
|
|
125
125
|
substrong,
|
|
126
126
|
ds => {
|
|
127
127
|
const { source } = context;
|
|
@@ -133,7 +133,7 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
|
|
|
133
133
|
return prepend('**', nodes.import(ds));
|
|
134
134
|
}
|
|
135
135
|
})
|
|
136
|
-
({ context })
|
|
136
|
+
({ context }) ?? prepend('**', nodes);
|
|
137
137
|
prefix -= 2;
|
|
138
138
|
break;
|
|
139
139
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReferenceParser } from '../inline';
|
|
2
2
|
import { State, Backtrack, Command } from '../context';
|
|
3
|
-
import { List, Data
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
4
4
|
import { union, subsequence, some, precedence, state, constraint, surround, isBacktrack, setBacktrack, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { textlink } from './link';
|
|
@@ -78,7 +78,7 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
78
78
|
})
|
|
79
79
|
({ context });
|
|
80
80
|
if (state & State.annotation && next) {
|
|
81
|
-
return (as as List<Data<string | HTMLElement>>).import(bs).import(
|
|
81
|
+
return (as as List<Data<string | HTMLElement>>).import(bs).import(result).import(next);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
context.position = position;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RubyParser } from '../inline';
|
|
2
2
|
import { Backtrack } from '../context';
|
|
3
|
-
import { List, Data
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
4
4
|
import { inits, surround, setBacktrack, dup, lazy, bind } from '../../combinator';
|
|
5
5
|
import { unsafehtmlentity } from './htmlentity';
|
|
6
6
|
import { txt } from '../source';
|
|
@@ -76,7 +76,7 @@ const text: RubyParser.TextParser = input => {
|
|
|
76
76
|
case '&': {
|
|
77
77
|
const result = unsafehtmlentity(input) ?? txt(input)!;
|
|
78
78
|
assert(result);
|
|
79
|
-
acc.last!.value +=
|
|
79
|
+
acc.last!.value += result.head!.value;
|
|
80
80
|
continue;
|
|
81
81
|
}
|
|
82
82
|
default: {
|
|
@@ -88,7 +88,7 @@ const text: RubyParser.TextParser = input => {
|
|
|
88
88
|
}
|
|
89
89
|
const result = txt(input)!;
|
|
90
90
|
assert(result);
|
|
91
|
-
acc.last!.value +=
|
|
91
|
+
acc.last!.value += result.head?.value ?? '';
|
|
92
92
|
continue;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -103,7 +103,7 @@ const text: RubyParser.TextParser = input => {
|
|
|
103
103
|
function* zip<N extends List.Node>(a: List<N>, b: List<N>): Iterable<[N | undefined, N | undefined]> {
|
|
104
104
|
const ia = a[Symbol.iterator]();
|
|
105
105
|
const ib = b[Symbol.iterator]();
|
|
106
|
-
|
|
106
|
+
for (; ;) {
|
|
107
107
|
const ra = ia.next();
|
|
108
108
|
const rb = ib.next();
|
|
109
109
|
if (ra.done) break;
|
|
@@ -191,7 +191,7 @@ function* proc(defs: Map<string, HTMLLIElement>, note: HTMLOListElement): Genera
|
|
|
191
191
|
for (const [key, def] of defs) {
|
|
192
192
|
defs.delete(key);
|
|
193
193
|
++count;
|
|
194
|
-
|
|
194
|
+
for (; length > size;) {
|
|
195
195
|
const node = children[count - 1] as HTMLLIElement;
|
|
196
196
|
if (equal(node, def)) continue I;
|
|
197
197
|
yield note.removeChild(node);
|
|
@@ -207,7 +207,7 @@ function* proc(defs: Map<string, HTMLLIElement>, note: HTMLOListElement): Genera
|
|
|
207
207
|
++length;
|
|
208
208
|
assert(children.length === length);
|
|
209
209
|
}
|
|
210
|
-
|
|
210
|
+
for (; length > size;) {
|
|
211
211
|
yield note.removeChild(children[size] as HTMLLIElement);
|
|
212
212
|
--length;
|
|
213
213
|
assert(children.length === length);
|