securemark 0.296.3 → 0.296.5
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/design.md +3 -4
- package/dist/index.js +146 -82
- package/markdown.d.ts +8 -4
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +12 -6
- package/src/combinator/control/manipulation/recovery.ts +3 -3
- package/src/combinator/data/parser.ts +7 -0
- package/src/parser/api/bind.ts +10 -5
- package/src/parser/api/parse.ts +3 -2
- package/src/parser/block/extension/fig.ts +3 -1
- package/src/parser/block/extension/figure.ts +2 -1
- package/src/parser/block/extension/message.ts +1 -1
- package/src/parser/block/extension.test.ts +3 -3
- package/src/parser/block/extension.ts +3 -4
- package/src/parser/block/heading.ts +4 -4
- package/src/parser/block.ts +22 -12
- package/src/parser/context.ts +13 -0
- package/src/parser/inline/autolink/account.ts +1 -1
- package/src/parser/inline/autolink/anchor.ts +1 -1
- package/src/parser/inline/autolink/hashnum.ts +1 -1
- package/src/parser/inline/autolink/hashtag.ts +1 -1
- package/src/parser/inline/emstrong.ts +1 -1
- package/src/parser/inline/extension/indexee.ts +8 -8
- package/src/parser/inline/mark.ts +1 -1
- package/src/parser/inline.test.ts +0 -1
- package/src/parser/processor/note.test.ts +35 -29
- package/src/parser/processor/note.ts +32 -23
- package/src/parser/segment.test.ts +73 -73
- package/src/parser/segment.ts +9 -11
- package/src/parser/source/line.ts +25 -6
- package/src/parser/source/str.ts +9 -10
- package/src/parser/source.ts +2 -1
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import { Parser, failsafe } from '../../data/parser';
|
|
1
|
+
import { Parser, List, Segment, failsafe } from '../../data/parser';
|
|
2
2
|
import { isEmptyline } from './line';
|
|
3
3
|
|
|
4
|
-
export function block<P extends Parser>(parser: P, separation?: boolean): P;
|
|
5
|
-
export function block<N>(parser: Parser<N>, separation = true): Parser<N> {
|
|
4
|
+
export function block<P extends Parser>(parser: P, separation?: boolean, segment?: number): P;
|
|
5
|
+
export function block<N>(parser: Parser<N>, separation = true, segment = 0): Parser<N> {
|
|
6
6
|
assert(parser);
|
|
7
7
|
return failsafe(input => {
|
|
8
8
|
const { context } = input;
|
|
9
9
|
const { source, position } = context;
|
|
10
10
|
if (position === source.length) return;
|
|
11
|
+
if (segment !== 0 && context.segment & Segment.write) {
|
|
12
|
+
if (context.segment !== (segment | Segment.write)) return;
|
|
13
|
+
context.position = source.length;
|
|
14
|
+
return new List();
|
|
15
|
+
}
|
|
11
16
|
const result = parser(input);
|
|
12
17
|
if (result === undefined) return;
|
|
13
18
|
if (separation && !isEmptyline(source, context.position)) return;
|
|
14
19
|
assert(context.position === source.length || source[context.position - 1] === '\n');
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
if (segment !== 0 && context.segment & Segment.write ^ Segment.write) {
|
|
21
|
+
context.segment = segment;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
18
24
|
});
|
|
19
25
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Parser, Input, Result, Context } from '../../data/parser';
|
|
2
2
|
|
|
3
|
-
export function recover<P extends Parser>(parser: P,
|
|
4
|
-
export function recover<N>(parser: Parser<N>,
|
|
3
|
+
export function recover<P extends Parser>(parser: P, catcher: (input: Input<Parser.Context<P>>, reason: unknown) => Result<Parser.Node<P>>): P;
|
|
4
|
+
export function recover<N>(parser: Parser<N>, catcher: (input: Input<Context>, reason: unknown) => Result<N>): Parser<N> {
|
|
5
5
|
return input => {
|
|
6
6
|
const { context } = input;
|
|
7
7
|
const { source, position } = context;
|
|
@@ -12,7 +12,7 @@ export function recover<N>(parser: Parser<N>, fallback: (input: Input<Context>,
|
|
|
12
12
|
assert(reason instanceof Error && reason.name === 'AssertionError' && !+console.error(reason) || 1);
|
|
13
13
|
context.source = source;
|
|
14
14
|
context.position = position;
|
|
15
|
-
return
|
|
15
|
+
return catcher(input, reason);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
}
|
|
@@ -33,6 +33,7 @@ export class Context {
|
|
|
33
33
|
{
|
|
34
34
|
source,
|
|
35
35
|
position,
|
|
36
|
+
segment,
|
|
36
37
|
resources,
|
|
37
38
|
delimiters,
|
|
38
39
|
precedence,
|
|
@@ -45,6 +46,7 @@ export class Context {
|
|
|
45
46
|
) {
|
|
46
47
|
this.source = source ?? '';
|
|
47
48
|
this.position = position ?? 0;
|
|
49
|
+
this.segment = segment ?? 0;
|
|
48
50
|
this.resources = resources;
|
|
49
51
|
this.precedence = precedence ?? 0;
|
|
50
52
|
this.delimiters = delimiters ?? new Delimiters();
|
|
@@ -56,6 +58,7 @@ export class Context {
|
|
|
56
58
|
}
|
|
57
59
|
public source: string;
|
|
58
60
|
public position: number;
|
|
61
|
+
public segment: number;
|
|
59
62
|
public readonly resources?: {
|
|
60
63
|
clock: number;
|
|
61
64
|
recursions: number[];
|
|
@@ -103,6 +106,10 @@ export class Context {
|
|
|
103
106
|
public backtracks: Record<number, number>;
|
|
104
107
|
}
|
|
105
108
|
export type Options = Partial<Context>;
|
|
109
|
+
export const enum Segment {
|
|
110
|
+
unknown = 0,
|
|
111
|
+
write = 1,
|
|
112
|
+
}
|
|
106
113
|
|
|
107
114
|
export function input<C extends Context>(source: string, context: C): Input<C> {
|
|
108
115
|
context.source = source;
|
package/src/parser/api/bind.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ParserSettings, Progress } from '../../..';
|
|
2
|
-
import { Context } from '../context';
|
|
2
|
+
import { Context, Segment } from '../context';
|
|
3
3
|
import { input } from '../../combinator/data/parser';
|
|
4
4
|
import { segment } from '../segment';
|
|
5
5
|
import { block } from '../block';
|
|
@@ -47,8 +47,10 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
47
47
|
context.url = url ? new ReadonlyURL(url as ':') : undefined;
|
|
48
48
|
const rev = revision = Symbol();
|
|
49
49
|
const sourceSegments: string[] = [];
|
|
50
|
-
|
|
50
|
+
const sourceSegmentAttrs: Segment[] = [];
|
|
51
|
+
for (const [seg, attr] of segment(source)) {
|
|
51
52
|
sourceSegments.push(seg);
|
|
53
|
+
sourceSegmentAttrs.push(attr);
|
|
52
54
|
yield { type: 'segment', value: seg };
|
|
53
55
|
}
|
|
54
56
|
const targetSegments = blocks.map(([seg]) => seg);
|
|
@@ -74,12 +76,15 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
74
76
|
context.header = true;
|
|
75
77
|
for (; index < sourceSegments.length - last; ++index) {
|
|
76
78
|
assert(rev === revision);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
+
const src = sourceSegments[index];
|
|
80
|
+
context.segment = sourceSegmentAttrs[index] | Segment.write;
|
|
81
|
+
const es = block(input(src, new Context(context)))
|
|
79
82
|
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? [];
|
|
80
83
|
// @ts-expect-error
|
|
81
84
|
context.header = false;
|
|
82
|
-
blocks.
|
|
85
|
+
blocks.length === index
|
|
86
|
+
? blocks.push([src, es, url])
|
|
87
|
+
: blocks.splice(index, 0, [src, es, url]);
|
|
83
88
|
if (es.length === 0) continue;
|
|
84
89
|
// All deletion processes always run after all addition processes have done.
|
|
85
90
|
// Therefore any `base` node will never be unavailable by deletions until all the dependent `el` nodes are added.
|
package/src/parser/api/parse.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParserOptions } from '../../..';
|
|
2
2
|
import { input } from '../../combinator/data/parser';
|
|
3
|
-
import { Context } from '../context';
|
|
3
|
+
import { Context, Segment } from '../context';
|
|
4
4
|
import { segment } from '../segment';
|
|
5
5
|
import { block } from '../block';
|
|
6
6
|
import { normalize } from './normalize';
|
|
@@ -32,7 +32,8 @@ export function parse(source: string, options: Options = {}, context?: Context):
|
|
|
32
32
|
const node = frag();
|
|
33
33
|
// @ts-expect-error
|
|
34
34
|
context.header = true;
|
|
35
|
-
for (const seg of segment(source)) {
|
|
35
|
+
for (const [seg, attr] of segment(source)) {
|
|
36
|
+
context.segment = attr | Segment.write;
|
|
36
37
|
node.append(
|
|
37
38
|
...block(input(seg, new Context(context)))
|
|
38
39
|
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? []);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
|
+
import { Segment } from '../../context';
|
|
2
3
|
import { union, sequence, some, block, line, verify, rewrite, close, convert } from '../../../combinator';
|
|
3
4
|
import { contentline } from '../../source';
|
|
4
5
|
import { figure } from './figure';
|
|
@@ -23,7 +24,7 @@ export const segment: FigParser.SegmentParser = block(
|
|
|
23
24
|
seg_placeholder,
|
|
24
25
|
some(contentline),
|
|
25
26
|
]),
|
|
26
|
-
]));
|
|
27
|
+
]), true, Segment.fig);
|
|
27
28
|
|
|
28
29
|
export const fig: FigParser = block(rewrite(segment, verify(convert(
|
|
29
30
|
(source, context) => {
|
|
@@ -33,6 +34,7 @@ export const fig: FigParser = block(rewrite(segment, verify(convert(
|
|
|
33
34
|
const { position } = context;
|
|
34
35
|
const result = parser({ context });
|
|
35
36
|
context.position = position;
|
|
37
|
+
context.segment = Segment.figure | Segment.write;
|
|
36
38
|
return result
|
|
37
39
|
? `${fence}figure ${source.replace(/^(.+\n.+\n)([\S\s]+?)\n?$/, '$1\n$2')}\n${fence}`
|
|
38
40
|
: `${fence}figure ${source}\n\n${fence}`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
|
+
import { Segment } from '../../context';
|
|
2
3
|
import { List, Node, subinput } from '../../../combinator/data/parser';
|
|
3
4
|
import { union, inits, sequence, some, block, line, fence, rewrite, close, match, convert, fallback, fmap } from '../../../combinator';
|
|
4
5
|
import { str, contentline, emptyline } from '../../source';
|
|
@@ -44,7 +45,7 @@ export const segment: FigureParser.SegmentParser = block(match(
|
|
|
44
45
|
]),
|
|
45
46
|
]),
|
|
46
47
|
closer),
|
|
47
|
-
([, fence]) => fence.length - 1, [], 2 ** 4 - 1)));
|
|
48
|
+
([, fence]) => fence.length - 1, [], 2 ** 4 - 1)), true, Segment.figure);
|
|
48
49
|
|
|
49
50
|
export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
50
51
|
convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
|
|
@@ -30,9 +30,9 @@ describe('Unit: parser/block/extension', () => {
|
|
|
30
30
|
assert(parser(input('~~~\na\n~~~', new Context())));
|
|
31
31
|
assert(parser(input('~~~a\n~~~', new Context())));
|
|
32
32
|
assert(parser(input('~~~a\nb\n~~~', new Context())));
|
|
33
|
-
assert(parser(input('$-0', new Context())));
|
|
34
|
-
assert(parser(input('$-0\n', new Context())));
|
|
35
|
-
assert(parser(input('$-0\n\n', new Context())));
|
|
33
|
+
//assert(parser(input('$-0', new Context())));
|
|
34
|
+
//assert(parser(input('$-0\n', new Context())));
|
|
35
|
+
//assert(parser(input('$-0\n\n', new Context())));
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ExtensionParser } from '../block';
|
|
2
2
|
import { union } from '../../combinator';
|
|
3
|
-
import {
|
|
4
|
-
import { fig, segment as seg_fig } from './extension/fig';
|
|
3
|
+
import { segment as seg_fig } from './extension/fig';
|
|
5
4
|
import { figure, segment as seg_figure } from './extension/figure';
|
|
6
5
|
import { table, segment as seg_table } from './extension/table';
|
|
7
6
|
import { message } from './extension/message';
|
|
@@ -17,8 +16,8 @@ export const segment: ExtensionParser.SegmentParser = union([
|
|
|
17
16
|
]);
|
|
18
17
|
|
|
19
18
|
export const extension: ExtensionParser = union([
|
|
20
|
-
figbase,
|
|
21
|
-
fig,
|
|
19
|
+
//figbase,
|
|
20
|
+
//fig,
|
|
22
21
|
figure,
|
|
23
22
|
table,
|
|
24
23
|
message,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { HeadingParser } from '../block';
|
|
2
|
-
import { State } from '../context';
|
|
2
|
+
import { Segment, State } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
4
|
import { union, some, state, block, line, focus, rewrite, open, fmap, firstline } from '../../combinator';
|
|
5
5
|
import { inline, indexee, indexer, dataindex } from '../inline';
|
|
6
|
-
import { str } from '../source';
|
|
6
|
+
import { str, strs } from '../source';
|
|
7
7
|
import { visualize, trimBlank } from '../visibility';
|
|
8
8
|
import { unwrap, invalid } from '../util';
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
@@ -20,14 +20,14 @@ export const segment: HeadingParser.SegmentParser = block(focus(
|
|
|
20
20
|
context.position += line.length;
|
|
21
21
|
}
|
|
22
22
|
return acc;
|
|
23
|
-
}, false));
|
|
23
|
+
}, false), true, Segment.heading);
|
|
24
24
|
|
|
25
25
|
export const heading: HeadingParser = block(rewrite(segment,
|
|
26
26
|
// その他の表示制御は各所のCSSで行う。
|
|
27
27
|
state(State.annotation | State.reference | State.index | State.label | State.link,
|
|
28
28
|
line(indexee(fmap(union([
|
|
29
29
|
open(
|
|
30
|
-
|
|
30
|
+
strs('#', 2),
|
|
31
31
|
visualize(trimBlank(some(union([indexer, inline])))), true),
|
|
32
32
|
open(
|
|
33
33
|
str('#'),
|
package/src/parser/block.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { MarkdownParser } from '../../markdown';
|
|
2
|
-
import { Recursion, Command } from './context';
|
|
2
|
+
import { Segment, Recursion, Command } from './context';
|
|
3
3
|
import { List, Node } from '../combinator/data/parser';
|
|
4
|
-
import { union, reset,
|
|
4
|
+
import { union, reset, firstline, recover } from '../combinator';
|
|
5
5
|
import { MAX_SEGMENT_SIZE } from './segment';
|
|
6
6
|
import { header } from './header';
|
|
7
|
-
import {
|
|
7
|
+
import { emptysegment } from './source';
|
|
8
8
|
import { pagebreak } from './block/pagebreak';
|
|
9
9
|
import { heading } from './block/heading';
|
|
10
10
|
import { ulist } from './block/ulist';
|
|
@@ -15,6 +15,9 @@ import { table } from './block/table';
|
|
|
15
15
|
import { codeblock } from './block/codeblock';
|
|
16
16
|
import { mathblock } from './block/mathblock';
|
|
17
17
|
import { extension } from './block/extension';
|
|
18
|
+
import { figbase } from './block/extension/figbase';
|
|
19
|
+
import { fig } from './block/extension/fig';
|
|
20
|
+
import { figure } from './block/extension/figure';
|
|
18
21
|
import { sidefence } from './block/sidefence';
|
|
19
22
|
import { blockquote } from './block/blockquote';
|
|
20
23
|
import { mediablock } from './block/mediablock';
|
|
@@ -57,12 +60,22 @@ export const block: BlockParser = reset(
|
|
|
57
60
|
backtracks: {},
|
|
58
61
|
},
|
|
59
62
|
error(union([
|
|
60
|
-
|
|
63
|
+
emptysegment,
|
|
61
64
|
input => {
|
|
62
|
-
const { context: { source, position } } = input;
|
|
65
|
+
const { context: { source, position, segment } } = input;
|
|
63
66
|
if (position === source.length) return;
|
|
67
|
+
switch (segment ^ Segment.write) {
|
|
68
|
+
case Segment.heading:
|
|
69
|
+
return heading(input);
|
|
70
|
+
case Segment.fig:
|
|
71
|
+
return fig(input);
|
|
72
|
+
case Segment.figure:
|
|
73
|
+
return figure(input);
|
|
74
|
+
}
|
|
64
75
|
const fst = source[position];
|
|
65
76
|
switch (fst) {
|
|
77
|
+
case Command.Error:
|
|
78
|
+
throw new Error(firstline(source, position + 1).trimEnd());
|
|
66
79
|
case '=':
|
|
67
80
|
if (source.startsWith('===', position)) return pagebreak(input);
|
|
68
81
|
break;
|
|
@@ -84,7 +97,7 @@ export const block: BlockParser = reset(
|
|
|
84
97
|
case '[':
|
|
85
98
|
switch (source[position + 1]) {
|
|
86
99
|
case '$':
|
|
87
|
-
return
|
|
100
|
+
return figbase(input);
|
|
88
101
|
case '!':
|
|
89
102
|
return mediablock(input);
|
|
90
103
|
}
|
|
@@ -95,11 +108,9 @@ export const block: BlockParser = reset(
|
|
|
95
108
|
case '>':
|
|
96
109
|
if (source[position + 1] === '>') return blockquote(input) || reply(input);
|
|
97
110
|
return blockquote(input);
|
|
98
|
-
case '#':
|
|
99
|
-
return heading(input);
|
|
100
111
|
case '$':
|
|
101
112
|
if (source[position + 1] === '$') return mathblock(input);
|
|
102
|
-
return
|
|
113
|
+
return figbase(input);
|
|
103
114
|
case '|':
|
|
104
115
|
return table(input) || sidefence(input);
|
|
105
116
|
case '(':
|
|
@@ -113,9 +124,8 @@ export const block: BlockParser = reset(
|
|
|
113
124
|
|
|
114
125
|
function error(parser: BlockParser): BlockParser {
|
|
115
126
|
const reg = new RegExp(String.raw`^${Command.Error}[^\n]*\n`)
|
|
116
|
-
return recover<BlockParser>(
|
|
117
|
-
|
|
118
|
-
parser),
|
|
127
|
+
return recover<BlockParser>(
|
|
128
|
+
parser,
|
|
119
129
|
({ context: { source, position, id } }, reason) => new List([
|
|
120
130
|
new Node(html('h1',
|
|
121
131
|
{
|
package/src/parser/context.ts
CHANGED
|
@@ -7,6 +7,7 @@ export class Context extends Ctx {
|
|
|
7
7
|
) {
|
|
8
8
|
super(options);
|
|
9
9
|
const {
|
|
10
|
+
segment,
|
|
10
11
|
buffer,
|
|
11
12
|
sequential,
|
|
12
13
|
header,
|
|
@@ -15,6 +16,7 @@ export class Context extends Ctx {
|
|
|
15
16
|
id,
|
|
16
17
|
caches,
|
|
17
18
|
} = options;
|
|
19
|
+
this.segment = segment ?? Segment.unknown;
|
|
18
20
|
this.buffer = buffer ?? new List();
|
|
19
21
|
this.sequential = sequential ?? false;
|
|
20
22
|
this.header = header ?? true;
|
|
@@ -23,6 +25,7 @@ export class Context extends Ctx {
|
|
|
23
25
|
this.id = id;
|
|
24
26
|
this.caches = caches;
|
|
25
27
|
}
|
|
28
|
+
public override segment: Segment;
|
|
26
29
|
public buffer: List<Node<(string | HTMLElement)>>;
|
|
27
30
|
public sequential: boolean;
|
|
28
31
|
public readonly header: boolean;
|
|
@@ -37,6 +40,16 @@ export class Context extends Ctx {
|
|
|
37
40
|
}
|
|
38
41
|
export type Options = Partial<Context>;
|
|
39
42
|
|
|
43
|
+
export const enum Segment {
|
|
44
|
+
unknown = 0,
|
|
45
|
+
write = 1,
|
|
46
|
+
nonempty = 0,
|
|
47
|
+
empty = 1 << 1,
|
|
48
|
+
heading = 3 << 1,
|
|
49
|
+
fig = 4 << 1,
|
|
50
|
+
figure = 5 << 1,
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
export const enum State {
|
|
41
54
|
annotation = 1 << 7,
|
|
42
55
|
reference = 1 << 6,
|
|
@@ -13,7 +13,7 @@ import { define } from 'typed-dom/dom';
|
|
|
13
13
|
export const account: AutolinkParser.AccountParser = lazy(() => constraint(State.autolink, state(State.autolink,
|
|
14
14
|
surround(
|
|
15
15
|
surround(
|
|
16
|
-
/(?<![0-9a-z])@/yi,
|
|
16
|
+
/(?<![0-9a-z@#])@/yi,
|
|
17
17
|
str(/[0-9a-z](?:[.-](?=[0-9a-z])|[0-9a-z]){0,254}\/|/yi),
|
|
18
18
|
str(/[a-z][0-9a-z]*(?:[.-][0-9a-z]+)*(?![_.-]?[0-9a-z@]|>>|:\S)/yi),
|
|
19
19
|
false,
|
|
@@ -18,7 +18,7 @@ import { define } from 'typed-dom/dom';
|
|
|
18
18
|
|
|
19
19
|
export const anchor: AutolinkParser.AnchorParser = lazy(() => constraint(State.autolink, state(State.autolink,
|
|
20
20
|
surround(
|
|
21
|
-
/(?<![0-9a-z])>>/yi,
|
|
21
|
+
/(?<![0-9a-z@#])>>/yi,
|
|
22
22
|
str(/[0-9a-z]+(?:-[0-9a-z]+)*(?![_.-]?[0-9a-z@#]|>>|:\S)/yi),
|
|
23
23
|
'',
|
|
24
24
|
false,
|
|
@@ -10,7 +10,7 @@ import { define } from 'typed-dom/dom';
|
|
|
10
10
|
export const hashnum: AutolinkParser.HashnumParser = lazy(() => constraint(State.autolink, state(State.autolink,
|
|
11
11
|
surround(
|
|
12
12
|
new RegExp([
|
|
13
|
-
/(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yu.source,
|
|
13
|
+
/(?<![^\p{C}\p{S}\p{P}\s]|emoji|[@#])#/yu.source,
|
|
14
14
|
].join('|').replace(/emoji/g, emoji.source), 'yu'),
|
|
15
15
|
str(new RegExp([
|
|
16
16
|
/[0-9]{1,9}(?![_.-]?[0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
|
|
@@ -14,7 +14,7 @@ export const emoji = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presen
|
|
|
14
14
|
export const hashtag: AutolinkParser.HashtagParser = lazy(() => constraint(State.autolink, state(State.autolink,
|
|
15
15
|
surround(
|
|
16
16
|
new RegExp([
|
|
17
|
-
/(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yu.source,
|
|
17
|
+
/(?<![^\p{C}\p{S}\p{P}\s]|emoji|[@#])#/yu.source,
|
|
18
18
|
].join('|').replace(/emoji/g, emoji.source), 'yu'),
|
|
19
19
|
verify(
|
|
20
20
|
str(new RegExp([
|
|
@@ -26,7 +26,7 @@ export const emstrong: EmStrongParser = lazy(() =>
|
|
|
26
26
|
precedence(0, recursion(Recursion.inline, repeat('***', beforeNonblank, surround(
|
|
27
27
|
'',
|
|
28
28
|
some(union([some(inline, '*', afterNonblank)])),
|
|
29
|
-
strs('*', 3),
|
|
29
|
+
strs('*', 1, 3),
|
|
30
30
|
false, [],
|
|
31
31
|
([, bs, cs], context): Result<Parser.Node<EmStrongParser>, Parser.Context<EmStrongParser>> => {
|
|
32
32
|
assert(cs.length === 1);
|
|
@@ -33,7 +33,7 @@ export function identity(
|
|
|
33
33
|
if (index === '' && text.tagName === 'LI') return undefined;
|
|
34
34
|
return index
|
|
35
35
|
? `${type}:${id ?? ''}:${index}`
|
|
36
|
-
: identity(type, id, signature(text));
|
|
36
|
+
: identity(type, id, signature(text.cloneNode(true)));
|
|
37
37
|
}
|
|
38
38
|
text = text.trim();
|
|
39
39
|
if (text === '') return undefined;
|
|
@@ -106,10 +106,10 @@ assert(baseR(~0 >>> 0, 36) === (~0 >>> 0).toString(36));
|
|
|
106
106
|
assert(baseR(61, 62) === 'Z');
|
|
107
107
|
assert(baseR(62, 62) === '10');
|
|
108
108
|
|
|
109
|
-
export function signature(
|
|
110
|
-
assert(!
|
|
111
|
-
|
|
112
|
-
for (let es = target.querySelectorAll('code[data-src], .math[data-src], .
|
|
109
|
+
export function signature(target: Element | DocumentFragment): string {
|
|
110
|
+
assert(!target.parentNode);
|
|
111
|
+
assert(!target.querySelector('br:not(:has(+ :is(ul, ol)))') || target.nodeName === 'MARK');
|
|
112
|
+
for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, .checkbox, ul, ol, .label[data-label]'),
|
|
113
113
|
len = es.length, i = 0; i < len; ++i) {
|
|
114
114
|
const el = es[i];
|
|
115
115
|
switch (el.tagName) {
|
|
@@ -144,9 +144,9 @@ export function signature(source: Element | DocumentFragment): string {
|
|
|
144
144
|
return target.textContent!.trim();
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
export function text(
|
|
148
|
-
assert(!
|
|
149
|
-
|
|
147
|
+
export function text(target: Element | DocumentFragment): string {
|
|
148
|
+
assert(!target.parentNode);
|
|
149
|
+
assert(!target.querySelector('br:not(:has(+ :is(ul, ol)))'));
|
|
150
150
|
for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, .checkbox, ul, ol'),
|
|
151
151
|
len = es.length, i = 0; i < len; ++i) {
|
|
152
152
|
const el = es[i];
|
|
@@ -19,7 +19,7 @@ export const mark: MarkParser = lazy(() =>
|
|
|
19
19
|
(nodes, { id, state }, nest) => {
|
|
20
20
|
const el = html('mark', defrag(unwrap(nodes)));
|
|
21
21
|
if (state & State.linkers || nest) return new List([new Node(el)]);
|
|
22
|
-
define(el, { id: identity('mark', id, signature(el)) });
|
|
22
|
+
define(el, { id: identity('mark', id, signature(el.cloneNode(true))) });
|
|
23
23
|
return el.id
|
|
24
24
|
? new List([new Node(el), new Node(html('a', { href: `#${el.id}` }))])
|
|
25
25
|
: new List([new Node(el)]);
|
|
@@ -228,7 +228,6 @@ describe('Unit: parser/inline', () => {
|
|
|
228
228
|
assert.deepStrictEqual(inspect(parser, input('#a', new Context())), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
|
|
229
229
|
assert.deepStrictEqual(inspect(parser, input('#http://host', new Context())), [['#', 'http', '://host'], '']);
|
|
230
230
|
assert.deepStrictEqual(inspect(parser, input('#a\nb\n#c\n[#d]', new Context())), [['<a class="hashtag" href="/hashtags/a">#a</a>', '<br>', 'b', '<br>', '<a class="hashtag" href="/hashtags/c">#c</a>', '<br>', '<a class="index" href="#index::d">d</a>'], '']);
|
|
231
|
-
assert.deepStrictEqual(inspect(parser, input('##a', new Context())), [['#', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
|
|
232
231
|
assert.deepStrictEqual(inspect(parser, input('_#a', new Context())), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
|
|
233
232
|
assert.deepStrictEqual(inspect(parser, input('_#a_', new Context())), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>', '_'], '']);
|
|
234
233
|
assert.deepStrictEqual(inspect(parser, input('_#a_b', new Context())), [['_', '<a class="hashtag" href="/hashtags/a_b">#a_b</a>'], '']);
|
|
@@ -255,18 +255,20 @@ describe('Unit: parser/processor/note', () => {
|
|
|
255
255
|
]).outerHTML,
|
|
256
256
|
]);
|
|
257
257
|
assert.deepStrictEqual(
|
|
258
|
-
note.outerHTML,
|
|
259
|
-
|
|
260
|
-
html('
|
|
261
|
-
html('
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
258
|
+
[note.outerHTML],
|
|
259
|
+
[
|
|
260
|
+
html('ol', [
|
|
261
|
+
html('li', { id: 'reference::def:a_b' }, [
|
|
262
|
+
html('span', 'a b'),
|
|
263
|
+
html('sup', [html('a', { href: '#reference::ref:a_b:1' }, '^1')]),
|
|
264
|
+
]),
|
|
265
|
+
]).outerHTML,
|
|
266
|
+
]);
|
|
265
267
|
}
|
|
266
268
|
});
|
|
267
269
|
|
|
268
270
|
it('abbr', () => {
|
|
269
|
-
const target = parse('[[^A 1
|
|
271
|
+
const target = parse('[[^A 1]][[^A 1|b]][[^A 1]]');
|
|
270
272
|
const note = html('ol');
|
|
271
273
|
for (let i = 0; i < 3; ++i) {
|
|
272
274
|
[...reference(target, note)];
|
|
@@ -289,17 +291,19 @@ describe('Unit: parser/processor/note', () => {
|
|
|
289
291
|
]).outerHTML,
|
|
290
292
|
]);
|
|
291
293
|
assert.deepStrictEqual(
|
|
292
|
-
note.outerHTML,
|
|
293
|
-
|
|
294
|
-
html('
|
|
295
|
-
html('
|
|
296
|
-
|
|
297
|
-
html('
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
294
|
+
[note.outerHTML],
|
|
295
|
+
[
|
|
296
|
+
html('ol', [
|
|
297
|
+
html('li', { id: 'reference::def:A_1' }, [
|
|
298
|
+
html('span', 'b'),
|
|
299
|
+
html('sup', [
|
|
300
|
+
html('a', { href: '#reference::ref:A_1:1' }, '^1'),
|
|
301
|
+
html('a', { href: '#reference::ref:A_1:2', title: 'b' }, '^2'),
|
|
302
|
+
html('a', { href: '#reference::ref:A_1:3' }, '^3'),
|
|
303
|
+
])
|
|
304
|
+
]),
|
|
305
|
+
]).outerHTML,
|
|
306
|
+
]);
|
|
303
307
|
}
|
|
304
308
|
});
|
|
305
309
|
|
|
@@ -336,16 +340,18 @@ describe('Unit: parser/processor/note', () => {
|
|
|
336
340
|
]).outerHTML,
|
|
337
341
|
]);
|
|
338
342
|
assert.deepStrictEqual(
|
|
339
|
-
note.outerHTML,
|
|
340
|
-
|
|
341
|
-
html('
|
|
342
|
-
html('
|
|
343
|
-
|
|
344
|
-
html('
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
343
|
+
[note.outerHTML],
|
|
344
|
+
[
|
|
345
|
+
html('ol', [
|
|
346
|
+
html('li', { id: 'reference::def:B' }, [
|
|
347
|
+
html('span', 'c'),
|
|
348
|
+
html('sup', [
|
|
349
|
+
html('a', { href: '#reference::ref:B:1', title: 'c' }, '^1'),
|
|
350
|
+
html('a', { href: '#reference::ref:B:2' }, '^2'),
|
|
351
|
+
]),
|
|
352
|
+
]),
|
|
353
|
+
]).outerHTML,
|
|
354
|
+
]);
|
|
349
355
|
target.lastChild?.remove();
|
|
350
356
|
}
|
|
351
357
|
});
|