securemark 0.293.5 → 0.294.0
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 +845 -534
- package/markdown.d.ts +13 -13
- package/package.json +3 -3
- package/src/combinator/control/constraint/block.test.ts +6 -6
- package/src/combinator/control/constraint/contract.ts +3 -3
- package/src/combinator/control/constraint/line.test.ts +7 -7
- package/src/combinator/control/constraint/line.ts +1 -1
- package/src/combinator/control/manipulation/clear.ts +2 -3
- package/src/combinator/control/manipulation/convert.ts +2 -2
- package/src/combinator/control/manipulation/duplicate.ts +4 -5
- package/src/combinator/control/manipulation/fence.ts +2 -2
- package/src/combinator/control/manipulation/indent.test.ts +2 -2
- package/src/combinator/control/manipulation/indent.ts +4 -4
- package/src/combinator/control/manipulation/reverse.ts +2 -2
- package/src/combinator/control/manipulation/scope.ts +3 -4
- package/src/combinator/control/manipulation/surround.ts +12 -13
- package/src/combinator/control/monad/bind.ts +6 -6
- package/src/combinator/control/monad/fmap.ts +7 -7
- package/src/combinator/data/data.ts +135 -0
- package/src/combinator/data/parser/context.test.ts +8 -8
- package/src/combinator/data/parser/context.ts +3 -3
- package/src/combinator/data/parser/inits.ts +6 -7
- package/src/combinator/data/parser/sequence.test.ts +3 -3
- package/src/combinator/data/parser/sequence.ts +6 -7
- package/src/combinator/data/parser/some.test.ts +3 -3
- package/src/combinator/data/parser/some.ts +4 -4
- package/src/combinator/data/parser/subsequence.test.ts +4 -4
- package/src/combinator/data/parser/subsequence.ts +3 -3
- package/src/combinator/data/parser/tails.ts +3 -3
- package/src/combinator/data/parser/union.test.ts +3 -3
- package/src/combinator/data/parser.ts +16 -7
- package/src/debug.test.ts +6 -5
- package/src/parser/api/bind.ts +4 -6
- package/src/parser/api/header.ts +1 -1
- package/src/parser/api/normalize.ts +1 -1
- package/src/parser/api/parse.ts +3 -1
- package/src/parser/block/blockquote.ts +6 -4
- package/src/parser/block/codeblock.ts +8 -7
- package/src/parser/block/dlist.ts +9 -8
- package/src/parser/block/extension/aside.ts +27 -21
- package/src/parser/block/extension/example.ts +29 -26
- package/src/parser/block/extension/fig.ts +1 -1
- package/src/parser/block/extension/figbase.ts +6 -5
- package/src/parser/block/extension/figure.ts +23 -19
- package/src/parser/block/extension/message.ts +35 -24
- package/src/parser/block/extension/placeholder.ts +17 -13
- package/src/parser/block/extension/table.ts +47 -40
- package/src/parser/block/heading.test.ts +3 -12
- package/src/parser/block/heading.ts +12 -8
- package/src/parser/block/ilist.ts +13 -12
- package/src/parser/block/mathblock.ts +21 -17
- package/src/parser/block/mediablock.ts +7 -5
- package/src/parser/block/olist.ts +15 -5
- package/src/parser/block/pagebreak.ts +2 -1
- package/src/parser/block/paragraph.ts +3 -1
- package/src/parser/block/reply/cite.ts +20 -15
- package/src/parser/block/reply/quote.ts +6 -4
- package/src/parser/block/reply.ts +6 -3
- package/src/parser/block/sidefence.ts +8 -7
- package/src/parser/block/table.ts +23 -22
- package/src/parser/block/ulist.ts +16 -12
- package/src/parser/block.ts +7 -6
- package/src/parser/header.ts +18 -18
- package/src/parser/inline/annotation.ts +3 -1
- package/src/parser/inline/autolink/account.ts +3 -2
- package/src/parser/inline/autolink/anchor.ts +3 -2
- package/src/parser/inline/autolink/channel.ts +5 -4
- package/src/parser/inline/autolink/email.ts +4 -3
- package/src/parser/inline/autolink/hashnum.ts +3 -2
- package/src/parser/inline/autolink/hashtag.ts +4 -3
- package/src/parser/inline/autolink/url.ts +7 -6
- package/src/parser/inline/bracket.ts +16 -15
- package/src/parser/inline/code.ts +5 -4
- package/src/parser/inline/deletion.ts +5 -5
- package/src/parser/inline/emphasis.ts +4 -3
- package/src/parser/inline/emstrong.test.ts +18 -18
- package/src/parser/inline/emstrong.ts +39 -30
- package/src/parser/inline/extension/index.ts +22 -19
- package/src/parser/inline/extension/indexee.ts +2 -2
- package/src/parser/inline/extension/indexer.ts +2 -1
- package/src/parser/inline/extension/label.ts +7 -3
- package/src/parser/inline/extension/placeholder.ts +6 -6
- package/src/parser/inline/html.ts +27 -28
- package/src/parser/inline/htmlentity.ts +9 -8
- package/src/parser/inline/insertion.ts +5 -5
- package/src/parser/inline/italic.ts +5 -5
- package/src/parser/inline/link.ts +36 -38
- package/src/parser/inline/mark.ts +7 -7
- package/src/parser/inline/math.ts +5 -4
- package/src/parser/inline/media.ts +33 -32
- package/src/parser/inline/reference.ts +19 -20
- package/src/parser/inline/remark.ts +11 -11
- package/src/parser/inline/ruby.ts +50 -53
- package/src/parser/inline/strong.ts +4 -3
- package/src/parser/inline/template.ts +16 -15
- package/src/parser/inline.test.ts +3 -3
- package/src/parser/segment.ts +3 -1
- package/src/parser/source/escapable.ts +9 -8
- package/src/parser/source/line.ts +4 -3
- package/src/parser/source/str.ts +2 -2
- package/src/parser/source/text.ts +9 -8
- package/src/parser/source/unescapable.ts +6 -5
- package/src/parser/util.ts +18 -13
- package/src/parser/visibility.ts +19 -20
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { MediaParser } from '../inline';
|
|
2
2
|
import { State, Recursion, Backtrack, Command } from '../context';
|
|
3
|
-
import { subinput } from '../../combinator/data/parser';
|
|
4
|
-
import { union, inits, tails, some, creation, recursion, precedence, constraint,
|
|
3
|
+
import { List, Data, subinput } from '../../combinator/data/parser';
|
|
4
|
+
import { union, inits, tails, some, creation, recursion, precedence, constraint, surround, open, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
|
|
5
5
|
import { unsafelink, uri, option as linkoption, resolve, decode } from './link';
|
|
6
6
|
import { attributes } from './html';
|
|
7
7
|
import { unsafehtmlentity } from './htmlentity';
|
|
8
8
|
import { txt, str } from '../source';
|
|
9
|
-
import { invalid } from '../util';
|
|
9
|
+
import { unwrap, invalid } from '../util';
|
|
10
10
|
import { ReadonlyURL } from 'spica/url';
|
|
11
|
-
import { unshift, push } from 'spica/array';
|
|
12
11
|
import { html, define } from 'typed-dom/dom';
|
|
13
12
|
|
|
14
13
|
const optspec = {
|
|
@@ -21,7 +20,7 @@ Object.setPrototypeOf(optspec, null);
|
|
|
21
20
|
|
|
22
21
|
export const media: MediaParser = lazy(() => constraint(State.media, creation(10, open(
|
|
23
22
|
'!',
|
|
24
|
-
bind(
|
|
23
|
+
bind(fmap(tails([
|
|
25
24
|
dup(surround(
|
|
26
25
|
'[',
|
|
27
26
|
precedence(1, some(union([
|
|
@@ -31,9 +30,9 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
|
|
|
31
30
|
]), ']')),
|
|
32
31
|
']',
|
|
33
32
|
true,
|
|
34
|
-
([, ns =
|
|
33
|
+
([, ns = new List()], context) =>
|
|
35
34
|
context.linebreak === 0
|
|
36
|
-
?
|
|
35
|
+
? ns
|
|
37
36
|
: undefined,
|
|
38
37
|
undefined,
|
|
39
38
|
[3 | Backtrack.escbracket])),
|
|
@@ -47,26 +46,29 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
|
|
|
47
46
|
if (!bs) return;
|
|
48
47
|
const head = context.position - context.range!;
|
|
49
48
|
setBacktrack(context, [2 | Backtrack.link], head);
|
|
50
|
-
return
|
|
49
|
+
return as.import(bs).push(new Data(Command.Cancel)) && as;
|
|
51
50
|
},
|
|
52
51
|
[3 | Backtrack.link])),
|
|
53
52
|
]),
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
nodes =>
|
|
54
|
+
nodes.length === 1
|
|
55
|
+
? new List<Data<List<Data<string>>>>([new Data(new List([new Data('')])), nodes.delete(nodes.head!)])
|
|
56
|
+
: new List<Data<List<Data<string>>>>([new Data(new List([new Data(nodes.head!.value.foldl((acc, { value }) => acc + value, ''))])), nodes.delete(nodes.last!)])),
|
|
57
|
+
([{ value: [{ value: text }] }, { value: params }], context) => {
|
|
58
|
+
if (text && text.trimStart() === '') return;
|
|
59
|
+
text = text.trim();
|
|
60
|
+
if (params.last!.value === Command.Cancel) {
|
|
58
61
|
params.pop();
|
|
59
|
-
return [
|
|
60
|
-
html('span',
|
|
62
|
+
return new List([
|
|
63
|
+
new Data(html('span',
|
|
61
64
|
{
|
|
62
65
|
class: 'invalid',
|
|
63
66
|
...invalid('media', 'syntax', 'Missing the closing symbol "}"')
|
|
64
67
|
},
|
|
65
|
-
'!' + context.source.slice(context.position - context.range!, context.position))
|
|
66
|
-
]
|
|
68
|
+
'!' + context.source.slice(context.position - context.range!, context.position)))
|
|
69
|
+
]);
|
|
67
70
|
}
|
|
68
|
-
|
|
69
|
-
const INSECURE_URI = params.shift()!;
|
|
71
|
+
const INSECURE_URI = params.shift()!.value;
|
|
70
72
|
assert(INSECURE_URI === INSECURE_URI.trim());
|
|
71
73
|
assert(!INSECURE_URI.match(/\s/));
|
|
72
74
|
// altが空だとエラーが見えないため埋める。
|
|
@@ -85,37 +87,37 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
|
|
|
85
87
|
|| html('img', { class: 'media', 'data-src': uri?.source });
|
|
86
88
|
assert(!el.matches('.invalid'));
|
|
87
89
|
el.setAttribute('alt', text);
|
|
88
|
-
if (!sanitize(el, uri)) return [
|
|
90
|
+
if (!sanitize(el, uri)) return new List([new Data(el)]);
|
|
89
91
|
assert(!el.matches('.invalid'));
|
|
90
|
-
const [attrs, linkparams] = attributes('media', optspec, params);
|
|
92
|
+
const [attrs, linkparams] = attributes('media', optspec, unwrap(params));
|
|
91
93
|
define(el, attrs);
|
|
92
94
|
assert(el.matches('img') || !el.matches('.invalid'));
|
|
93
95
|
// Awaiting the generic support for attr().
|
|
94
96
|
if (el.hasAttribute('aspect-ratio')) {
|
|
95
97
|
el.style.aspectRatio = el.getAttribute('aspect-ratio')!;
|
|
96
98
|
}
|
|
97
|
-
if (context.state! & State.link) return [
|
|
98
|
-
if (cache && cache.tagName !== 'IMG') return [
|
|
99
|
+
if (context.state! & State.link) return new List([new Data(el)]);
|
|
100
|
+
if (cache && cache.tagName !== 'IMG') return new List([new Data(el)]);
|
|
99
101
|
const { source, position } = context;
|
|
100
102
|
return fmap(
|
|
101
103
|
unsafelink as MediaParser,
|
|
102
|
-
([
|
|
104
|
+
([{ value }]) => {
|
|
103
105
|
context.source = source;
|
|
104
106
|
context.position = position;
|
|
105
|
-
return [define(
|
|
107
|
+
return new List([new Data(define(value, { class: null, target: '_blank' }, [el]))]);
|
|
106
108
|
})
|
|
107
109
|
(subinput(`{ ${INSECURE_URI}${linkparams.join('')} }`, context));
|
|
108
110
|
})))));
|
|
109
111
|
|
|
110
112
|
const bracket: MediaParser.TextParser.BracketParser = lazy(() => recursion(Recursion.terminal, union([
|
|
111
113
|
surround(str('('), some(union([unsafehtmlentity, bracket, txt]), ')'), str(')'), true,
|
|
112
|
-
undefined, () =>
|
|
114
|
+
undefined, () => new List(), [3 | Backtrack.escbracket]),
|
|
113
115
|
surround(str('['), some(union([unsafehtmlentity, bracket, txt]), ']'), str(']'), true,
|
|
114
|
-
undefined, () =>
|
|
116
|
+
undefined, () => new List(), [3 | Backtrack.escbracket]),
|
|
115
117
|
surround(str('{'), some(union([unsafehtmlentity, bracket, txt]), '}'), str('}'), true,
|
|
116
|
-
undefined, () =>
|
|
118
|
+
undefined, () => new List(), [3 | Backtrack.escbracket]),
|
|
117
119
|
surround(str('"'), precedence(2, some(union([unsafehtmlentity, txt]), '"')), str('"'), true,
|
|
118
|
-
undefined, () =>
|
|
120
|
+
undefined, () => new List(), [3 | Backtrack.escbracket]),
|
|
119
121
|
])));
|
|
120
122
|
|
|
121
123
|
const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
|
|
@@ -124,11 +126,10 @@ const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
|
|
|
124
126
|
str(/[x:]/y),
|
|
125
127
|
str(/[1-9][0-9]*(?=[ }])/y),
|
|
126
128
|
false,
|
|
127
|
-
([[a], [b], [c]]) =>
|
|
129
|
+
([[{ value: a }], [{ value: b }], [{ value: c }]]) =>
|
|
128
130
|
b === 'x'
|
|
129
|
-
? [`width="${a}"
|
|
130
|
-
: [`aspect-ratio="${a}/${c}"`],
|
|
131
|
-
]),
|
|
131
|
+
? new List([new Data(`width="${a}"`), new Data(`height="${c}"`)])
|
|
132
|
+
: new List([new Data(`aspect-ratio="${a}/${c}"`)])),
|
|
132
133
|
linkoption,
|
|
133
134
|
]));
|
|
134
135
|
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { ReferenceParser } from '../inline';
|
|
2
2
|
import { State, Backtrack, Command } from '../context';
|
|
3
|
-
import { eval } from '../../combinator/data/parser';
|
|
3
|
+
import { List, Data, eval } 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';
|
|
7
7
|
import { str } from '../source';
|
|
8
8
|
import { trimBlankStart, trimBlankNodeEnd } from '../visibility';
|
|
9
|
+
import { unwrap, invalid } from '../util';
|
|
9
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
|
-
import { unshift, push } from 'spica/array';
|
|
11
|
-
import { invalid } from '../util';
|
|
12
11
|
|
|
13
12
|
export const reference: ReferenceParser = lazy(() => constraint(State.reference, surround(
|
|
14
13
|
str('[['),
|
|
@@ -22,7 +21,7 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
22
21
|
([, ns], context) => {
|
|
23
22
|
const { position, range = 0, linebreak = 0 } = context;
|
|
24
23
|
if (linebreak === 0) {
|
|
25
|
-
return [
|
|
24
|
+
return new List([new Data(html('sup', attributes(ns), [html('span', defrag(unwrap(trimBlankNodeEnd(ns))))]))]);
|
|
26
25
|
}
|
|
27
26
|
else {
|
|
28
27
|
const head = position - range;
|
|
@@ -42,13 +41,13 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
42
41
|
else {
|
|
43
42
|
assert(source[position] === ']');
|
|
44
43
|
if (state & State.annotation) {
|
|
45
|
-
push(
|
|
44
|
+
bs.push(new Data(source[position]));
|
|
46
45
|
}
|
|
47
46
|
context.position += 1;
|
|
48
47
|
let result: ReturnType<typeof textlink>;
|
|
49
48
|
if (source[context.position] !== '{') {
|
|
50
49
|
setBacktrack(context, [2 | Backtrack.link], head + 1);
|
|
51
|
-
result =
|
|
50
|
+
result = new List();
|
|
52
51
|
}
|
|
53
52
|
else {
|
|
54
53
|
result = !isBacktrack(context, [1 | Backtrack.link])
|
|
@@ -57,7 +56,7 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
57
56
|
context.range = range;
|
|
58
57
|
if (!result) {
|
|
59
58
|
setBacktrack(context, [2 | Backtrack.link], head + 1);
|
|
60
|
-
result =
|
|
59
|
+
result = new List();
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
62
|
assert(result);
|
|
@@ -71,21 +70,21 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
71
70
|
some(inline, ']', [[']', 1]]),
|
|
72
71
|
str(']'),
|
|
73
72
|
true,
|
|
74
|
-
([, cs =
|
|
75
|
-
|
|
76
|
-
([, cs =
|
|
73
|
+
([, cs = new List(), ds]) =>
|
|
74
|
+
cs.import(ds),
|
|
75
|
+
([, cs = new List()]) => {
|
|
77
76
|
setBacktrack(context, [2 | Backtrack.link], head);
|
|
78
|
-
return
|
|
77
|
+
return cs;
|
|
79
78
|
})
|
|
80
79
|
({ context });
|
|
81
80
|
if (state & State.annotation && next) {
|
|
82
|
-
return
|
|
81
|
+
return (as as List<Data<string | HTMLElement>>).import(bs).import(eval(result!)).import(eval(next));
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
context.position = position;
|
|
86
85
|
}
|
|
87
86
|
return state & State.annotation
|
|
88
|
-
?
|
|
87
|
+
? as.import(bs as List<Data<string>>)
|
|
89
88
|
: undefined;
|
|
90
89
|
},
|
|
91
90
|
[1 | Backtrack.bracket, 3 | Backtrack.doublebracket])));
|
|
@@ -98,22 +97,22 @@ const abbr: ReferenceParser.AbbrParser = surround(
|
|
|
98
97
|
true,
|
|
99
98
|
([, ns], context) => {
|
|
100
99
|
const { source, position, range = 0 } = context;
|
|
101
|
-
if (!ns) return [
|
|
100
|
+
if (!ns) return new List([new Data(''), new Data(source.slice(position - range, source[position - 1] === '|' ? position - 1 : position))]);
|
|
102
101
|
context.position += source[position] === ' ' ? 1 : 0;
|
|
103
|
-
return [
|
|
102
|
+
return new List([new Data(Command.Separator), new Data(ns.head!.value.trimEnd())]);
|
|
104
103
|
},
|
|
105
104
|
(_, context) => {
|
|
106
105
|
context.position -= context.range!;
|
|
107
|
-
return [
|
|
106
|
+
return new List([new Data('')]);
|
|
108
107
|
});
|
|
109
108
|
|
|
110
|
-
function attributes(ns:
|
|
111
|
-
switch (ns
|
|
109
|
+
function attributes(ns: List<Data<string | HTMLElement>>): Record<string, string | undefined> {
|
|
110
|
+
switch (ns.head!.value) {
|
|
112
111
|
case '':
|
|
113
112
|
return { class: 'invalid', ...invalid('reference', 'syntax', 'Invalid abbreviation') };
|
|
114
113
|
case Command.Separator:
|
|
115
|
-
const abbr = ns
|
|
116
|
-
ns
|
|
114
|
+
const abbr = ns.head!.next!.value as string;
|
|
115
|
+
ns.head!.value = ns.head!.next!.value = '';
|
|
117
116
|
return { class: 'reference', 'data-abbr': abbr };
|
|
118
117
|
default:
|
|
119
118
|
return { class: 'reference' };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { RemarkParser } from '../inline';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
3
4
|
import { union, some, recursion, precedence, focus, surround, close, fallback, lazy } from '../../combinator';
|
|
4
5
|
import { inline } from '../inline';
|
|
5
6
|
import { text, str } from '../source';
|
|
6
|
-
import { invalid } from '../util';
|
|
7
|
-
import { unshift, push } from 'spica/array';
|
|
7
|
+
import { unwrap, invalid } from '../util';
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
10
|
export const remark: RemarkParser = lazy(() => fallback(surround(
|
|
@@ -12,13 +12,13 @@ export const remark: RemarkParser = lazy(() => fallback(surround(
|
|
|
12
12
|
precedence(3, recursion(Recursion.inline,
|
|
13
13
|
some(union([inline]), /\s%\]/y, [[/\s%\]/y, 3]]))),
|
|
14
14
|
close(text, str(`%]`)), true,
|
|
15
|
-
([as, bs =
|
|
16
|
-
html('span', { class: 'remark' }, [
|
|
15
|
+
([as, bs = new List(), cs]) => new List([
|
|
16
|
+
new Data(html('span', { class: 'remark' }, [
|
|
17
17
|
html('input', { type: 'checkbox' }),
|
|
18
|
-
html('span', defrag(
|
|
19
|
-
]),
|
|
20
|
-
]
|
|
21
|
-
([as, bs]) => bs &&
|
|
22
|
-
focus(/\[%+(?=\s)/y, ({ context: { source } }) => [
|
|
23
|
-
html('span', { class: 'invalid', ...invalid('remark', 'syntax', 'Invalid start symbol') }, source)
|
|
24
|
-
]
|
|
18
|
+
html('span', defrag(unwrap(as.import(bs as List<Data<string>>).import(cs)))),
|
|
19
|
+
])),
|
|
20
|
+
]),
|
|
21
|
+
([as, bs]) => bs && as.import(bs as List<Data<string>>)),
|
|
22
|
+
focus(/\[%+(?=\s)/y, ({ context: { source } }) => new List([
|
|
23
|
+
new Data(html('span', { class: 'invalid', ...invalid('remark', 'syntax', 'Invalid start symbol') }, source))
|
|
24
|
+
]))));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { RubyParser } from '../inline';
|
|
2
2
|
import { Backtrack } from '../context';
|
|
3
|
-
import { eval } from '../../combinator/data/parser';
|
|
3
|
+
import { List, Data, eval } 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';
|
|
7
7
|
import { isTightNodeStart } from '../visibility';
|
|
8
|
-
import {
|
|
8
|
+
import { unwrap } from '../util';
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
11
|
export const ruby: RubyParser = lazy(() => bind(
|
|
@@ -14,8 +14,8 @@ export const ruby: RubyParser = lazy(() => bind(
|
|
|
14
14
|
'[', text, ']',
|
|
15
15
|
false,
|
|
16
16
|
([, ns]) => {
|
|
17
|
-
ns && ns.
|
|
18
|
-
return isTightNodeStart(ns) ?
|
|
17
|
+
ns && ns.last?.value === '' && ns.pop();
|
|
18
|
+
return isTightNodeStart(ns) ? ns : undefined;
|
|
19
19
|
},
|
|
20
20
|
undefined,
|
|
21
21
|
[1 | Backtrack.bracket, 3 | Backtrack.ruby])),
|
|
@@ -24,48 +24,49 @@ export const ruby: RubyParser = lazy(() => bind(
|
|
|
24
24
|
false, undefined, undefined,
|
|
25
25
|
[1 | Backtrack.bracket, 3 | Backtrack.ruby])),
|
|
26
26
|
]),
|
|
27
|
-
([texts, rubies], context) => {
|
|
27
|
+
([{ value: texts }, { value: rubies = undefined } = {}], context) => {
|
|
28
28
|
if (rubies === undefined) {
|
|
29
29
|
const head = context.position - context.range!;
|
|
30
30
|
return void setBacktrack(context, [2 | Backtrack.ruby], head);
|
|
31
31
|
}
|
|
32
32
|
switch (true) {
|
|
33
|
-
case
|
|
34
|
-
return [
|
|
35
|
-
html('ruby', defrag(texts
|
|
36
|
-
.reduce((acc,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
, []))),
|
|
54
|
-
]];
|
|
33
|
+
case texts.length >= rubies.length:
|
|
34
|
+
return new List([
|
|
35
|
+
new Data(html('ruby', defrag(unwrap([...zip(texts, rubies)]
|
|
36
|
+
.reduce((acc, [{ value: text = '' } = {}, { value: ruby = '' } = {}]) =>
|
|
37
|
+
acc.import(
|
|
38
|
+
ruby
|
|
39
|
+
? new List([new Data(text), new Data(html('rp', '(')), new Data(html('rt', ruby)), new Data(html('rp', ')'))])
|
|
40
|
+
: new List([new Data(text), new Data(html('rt'))]))
|
|
41
|
+
, new List<Data<string | HTMLElement>>()))))),
|
|
42
|
+
]);
|
|
43
|
+
case texts.length === 1 && [...texts.head!.value].length >= rubies.length:
|
|
44
|
+
return new List([
|
|
45
|
+
new Data(html('ruby', defrag(unwrap([...zip(new List([...texts.head!.value].map(char => new Data(char))), rubies)]
|
|
46
|
+
.reduce((acc, [{ value: text = '' } = {}, { value: ruby = '' } = {}]) =>
|
|
47
|
+
acc.import(
|
|
48
|
+
ruby
|
|
49
|
+
? new List([new Data(text), new Data(html('rp', '(')), new Data(html('rt', ruby)), new Data(html('rp', ')'))])
|
|
50
|
+
: new List([new Data(text), new Data(html('rt'))]))
|
|
51
|
+
, new List<Data<string | HTMLElement>>()))))),
|
|
52
|
+
]);
|
|
55
53
|
default:
|
|
56
54
|
assert(rubies.length > 0);
|
|
57
|
-
return [
|
|
58
|
-
html('ruby', defrag(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
return new List([
|
|
56
|
+
new Data(html('ruby', defrag(unwrap(new List<Data<string | HTMLElement>>([
|
|
57
|
+
new Data(texts.foldr(({ value }, acc) => value + ' ' + acc, '').slice(0, -1)),
|
|
58
|
+
new Data(html('rp', '(')),
|
|
59
|
+
new Data(html('rt', rubies.foldr(({ value }, acc) => value + ' ' + acc, '').trim())),
|
|
60
|
+
new Data(html('rp', ')')),
|
|
61
|
+
]))))),
|
|
62
|
+
]);
|
|
62
63
|
}
|
|
63
64
|
}));
|
|
64
65
|
|
|
65
66
|
const text: RubyParser.TextParser = input => {
|
|
66
67
|
const { context } = input;
|
|
67
68
|
const { source } = context;
|
|
68
|
-
const acc = [''];
|
|
69
|
+
const acc = new List([new Data('')]);
|
|
69
70
|
let state = false;
|
|
70
71
|
context.sequential = true;
|
|
71
72
|
for (let { position } = context; position < source.length; position = context.position) {
|
|
@@ -75,41 +76,37 @@ const text: RubyParser.TextParser = input => {
|
|
|
75
76
|
case '&': {
|
|
76
77
|
const result = unsafehtmlentity(input) ?? txt(input)!;
|
|
77
78
|
assert(result);
|
|
78
|
-
acc
|
|
79
|
+
acc.last!.value += eval(result).head!.value;
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
81
82
|
default: {
|
|
82
83
|
if (source[position].trimStart() === '') {
|
|
83
|
-
state ||= acc.
|
|
84
|
-
acc.push('');
|
|
84
|
+
state ||= acc.last!.value.trimStart() !== '';
|
|
85
|
+
acc.push(new Data(''));
|
|
85
86
|
context.position += 1;
|
|
86
87
|
continue;
|
|
87
88
|
}
|
|
88
89
|
const result = txt(input)!;
|
|
89
90
|
assert(result);
|
|
90
|
-
acc
|
|
91
|
+
acc.last!.value += eval(result).head?.value ?? '';
|
|
91
92
|
continue;
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
context.sequential = false;
|
|
96
|
-
state ||= acc.
|
|
97
|
+
state ||= acc.last!.value.trimStart() !== '';
|
|
97
98
|
return state
|
|
98
|
-
?
|
|
99
|
+
? acc
|
|
99
100
|
: undefined;
|
|
100
101
|
};
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// }
|
|
113
|
-
// }
|
|
114
|
-
// return attrs ?? {};
|
|
115
|
-
//}
|
|
103
|
+
function* zip<N extends List.Node>(a: List<N>, b: List<N>): Iterable<[N | undefined, N | undefined]> {
|
|
104
|
+
const ia = a[Symbol.iterator]();
|
|
105
|
+
const ib = b[Symbol.iterator]();
|
|
106
|
+
while (true) {
|
|
107
|
+
const ra = ia.next();
|
|
108
|
+
const rb = ib.next();
|
|
109
|
+
if (ra.done) break;
|
|
110
|
+
yield [ra.value, rb.value];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { StrongParser } from '../inline';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
3
4
|
import { union, some, recursion, precedence, surround, open, lazy } from '../../combinator';
|
|
4
5
|
import { inline } from '../inline';
|
|
5
6
|
import { emphasis } from './emphasis';
|
|
6
7
|
import { str } from '../source';
|
|
7
8
|
import { tightStart, blankWith } from '../visibility';
|
|
8
|
-
import {
|
|
9
|
+
import { unwrap } from '../util';
|
|
9
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
11
|
|
|
11
12
|
export const strong: StrongParser = lazy(() => surround(
|
|
@@ -17,5 +18,5 @@ export const strong: StrongParser = lazy(() => surround(
|
|
|
17
18
|
open(some(inline, '*'), inline),
|
|
18
19
|
]))))),
|
|
19
20
|
str('**'), false,
|
|
20
|
-
([, bs]) => [
|
|
21
|
-
([as, bs]) => bs &&
|
|
21
|
+
([, bs]) => new List([new Data(html('strong', defrag(unwrap(bs))))]),
|
|
22
|
+
([as, bs]) => bs && as.import(bs as List<Data<string>>)));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { TemplateParser } from '../inline';
|
|
2
2
|
import { Recursion, Backtrack } from '../context';
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
3
4
|
import { union, some, recursion, precedence, surround, lazy } from '../../combinator';
|
|
4
5
|
import { escsource, str } from '../source';
|
|
5
|
-
import { invalid } from '../util';
|
|
6
|
-
import { unshift, push } from 'spica/array';
|
|
6
|
+
import { unwrap, invalid } from '../util';
|
|
7
7
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
9
|
export const template: TemplateParser = lazy(() => surround(
|
|
@@ -12,35 +12,36 @@ export const template: TemplateParser = lazy(() => surround(
|
|
|
12
12
|
some(union([bracket, escsource]), '}')),
|
|
13
13
|
str('}}'),
|
|
14
14
|
true,
|
|
15
|
-
([as, bs =
|
|
16
|
-
|
|
15
|
+
([as, bs = new List(), cs]) => new List([
|
|
16
|
+
new Data(html('span', { class: 'template' }, defrag(unwrap(as.import(bs as List<Data<string>>).import(cs)))))
|
|
17
|
+
]),
|
|
17
18
|
([, bs], context) =>
|
|
18
|
-
bs && [
|
|
19
|
-
html('span',
|
|
19
|
+
bs && new List([
|
|
20
|
+
new Data(html('span',
|
|
20
21
|
{
|
|
21
22
|
class: 'invalid',
|
|
22
23
|
...invalid('template', 'syntax', `Missing the closing symbol "}}"`),
|
|
23
24
|
},
|
|
24
|
-
context.source.slice(context.position - context.range!, context.position))
|
|
25
|
-
]
|
|
25
|
+
context.source.slice(context.position - context.range!, context.position)))
|
|
26
|
+
]),
|
|
26
27
|
[3 | Backtrack.doublebracket, 3 | Backtrack.escbracket]));
|
|
27
28
|
|
|
28
29
|
const bracket: TemplateParser.BracketParser = lazy(() => union([
|
|
29
30
|
surround(str('('), recursion(Recursion.terminal, some(union([bracket, escsource]), ')')), str(')'), true,
|
|
30
|
-
undefined, () =>
|
|
31
|
+
undefined, () => new List(), [3 | Backtrack.escbracket]),
|
|
31
32
|
surround(str('['), recursion(Recursion.terminal, some(union([bracket, escsource]), ']')), str(']'), true,
|
|
32
|
-
undefined, () =>
|
|
33
|
+
undefined, () => new List(), [3 | Backtrack.escbracket]),
|
|
33
34
|
surround(str('{'), recursion(Recursion.terminal, some(union([bracket, escsource]), '}')), str('}'), true,
|
|
34
|
-
undefined, () =>
|
|
35
|
+
undefined, () => new List(), [3 | Backtrack.escbracket]),
|
|
35
36
|
surround(
|
|
36
37
|
str('"'),
|
|
37
38
|
precedence(2, recursion(Recursion.terminal, some(escsource, /["\n]/y, [['"', 2], ['\n', 3]]))),
|
|
38
39
|
str('"'),
|
|
39
40
|
true,
|
|
40
|
-
([as, bs =
|
|
41
|
+
([as, bs = new List(), cs], context) =>
|
|
41
42
|
context.linebreak === 0
|
|
42
|
-
?
|
|
43
|
-
: (context.position -= 1,
|
|
44
|
-
([as, bs]) => bs &&
|
|
43
|
+
? as.import(bs as List<Data<string>>).import(cs)
|
|
44
|
+
: (context.position -= 1, as.import(bs as List<Data<string>>)),
|
|
45
|
+
([as, bs]) => bs && as.import(bs as List<Data<string>>),
|
|
45
46
|
[3 | Backtrack.escbracket]),
|
|
46
47
|
]));
|
|
@@ -56,9 +56,9 @@ describe('Unit: parser/inline', () => {
|
|
|
56
56
|
assert.deepStrictEqual(inspect(parser('**a*b***'), ctx), [['<strong>a<em>b</em></strong>'], '']);
|
|
57
57
|
assert.deepStrictEqual(inspect(parser('**a*b***c'), ctx), [['<strong>a<em>b</em></strong>', 'c'], '']);
|
|
58
58
|
assert.deepStrictEqual(inspect(parser('**a**b****'), ctx), [['<strong>a</strong>', 'b', '****'], '']);
|
|
59
|
-
assert.deepStrictEqual(inspect(parser('**a**b****c'), ctx), [['<strong>a</strong>', 'b', '****
|
|
59
|
+
assert.deepStrictEqual(inspect(parser('**a**b****c'), ctx), [['<strong>a</strong>', 'b', '****c'], '']);
|
|
60
60
|
assert.deepStrictEqual(inspect(parser('**a***b*****'), ctx), [['<strong>a</strong>', '<em>b</em>', '****'], '']);
|
|
61
|
-
assert.deepStrictEqual(inspect(parser('**a***b*****c'), ctx), [['<strong>a</strong>', '<em>b</em>', '****
|
|
61
|
+
assert.deepStrictEqual(inspect(parser('**a***b*****c'), ctx), [['<strong>a</strong>', '<em>b</em>', '****c'], '']);
|
|
62
62
|
assert.deepStrictEqual(inspect(parser('**a *b***'), ctx), [['<strong>a <em>b</em></strong>'], '']);
|
|
63
63
|
assert.deepStrictEqual(inspect(parser('**a *b***c'), ctx), [['<strong>a <em>b</em></strong>', 'c'], '']);
|
|
64
64
|
assert.deepStrictEqual(inspect(parser('**a **b****'), ctx), [['<strong>a <strong>b</strong></strong>'], '']);
|
|
@@ -68,7 +68,7 @@ describe('Unit: parser/inline', () => {
|
|
|
68
68
|
assert.deepStrictEqual(inspect(parser('***a*'), ctx), [['**', '<em>a</em>'], '']);
|
|
69
69
|
assert.deepStrictEqual(inspect(parser('***a*b'), ctx), [['**', '<em>a</em>', 'b'], '']);
|
|
70
70
|
assert.deepStrictEqual(inspect(parser('***a*b*'), ctx), [['**', '<em>a</em>', 'b', '*'], '']);
|
|
71
|
-
assert.deepStrictEqual(inspect(parser('***a*b*c'), ctx), [['**', '<em>a</em>', 'b*c'], '']);
|
|
71
|
+
assert.deepStrictEqual(inspect(parser('***a*b*c'), ctx), [['**', '<em>a</em>', 'b', '*', 'c'], '']);
|
|
72
72
|
assert.deepStrictEqual(inspect(parser('***a*b*c*'), ctx), [['**', '<em>a</em>', 'b', '<em>c</em>'], '']);
|
|
73
73
|
assert.deepStrictEqual(inspect(parser('***a*b*c**'), ctx), [['**', '<em>a</em>', 'b', '<em>c</em>', '*'], '']);
|
|
74
74
|
assert.deepStrictEqual(inspect(parser('***a*b*c***'), ctx), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
|
package/src/parser/segment.ts
CHANGED
|
@@ -53,7 +53,9 @@ export function* segment(source: string): Generator<string, undefined, undefined
|
|
|
53
53
|
const result = parser(input)!;
|
|
54
54
|
assert(result);
|
|
55
55
|
assert(context.position > position);
|
|
56
|
-
const segs = eval(result).length
|
|
56
|
+
const segs = eval(result).length > 0
|
|
57
|
+
? eval(result).foldl<string[]>((acc, { value }) => void acc.push(value) || acc, [])
|
|
58
|
+
: [source.slice(position, context.position)];
|
|
57
59
|
assert(segs.join('') === source.slice(position, context.position));
|
|
58
60
|
for (let i = 0; i < segs.length; ++i) {
|
|
59
61
|
const seg = segs[i];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EscapableSourceParser } from '../source';
|
|
2
2
|
import { Command } from '../context';
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
3
4
|
import { consume } from '../../combinator';
|
|
4
5
|
import { next } from './text';
|
|
5
6
|
import { html } from 'typed-dom/dom';
|
|
@@ -16,33 +17,33 @@ export const escsource: EscapableSourceParser = ({ context }) => {
|
|
|
16
17
|
case '\r':
|
|
17
18
|
assert(!source.includes('\r', position + 1));
|
|
18
19
|
consume(-1, context);
|
|
19
|
-
return
|
|
20
|
+
return new List();
|
|
20
21
|
case Command.Escape:
|
|
21
22
|
consume(1, context);
|
|
22
23
|
context.position += 1;
|
|
23
|
-
return [
|
|
24
|
+
return new List([new Data(source.slice(position + 1, position + 2))]);
|
|
24
25
|
case '\\':
|
|
25
26
|
switch (source[position + 1]) {
|
|
26
27
|
case undefined:
|
|
27
|
-
return [
|
|
28
|
+
return new List([new Data(char)]);
|
|
28
29
|
case '\n':
|
|
29
|
-
return [
|
|
30
|
+
return new List([new Data(char)]);
|
|
30
31
|
default:
|
|
31
32
|
consume(1, context);
|
|
32
33
|
context.position += 1;
|
|
33
|
-
return [
|
|
34
|
+
return new List([new Data(source.slice(position, position + 2))]);
|
|
34
35
|
}
|
|
35
36
|
case '\n':
|
|
36
37
|
context.linebreak ||= source.length - position;
|
|
37
|
-
return [
|
|
38
|
+
return new List([new Data(html('br'))]);
|
|
38
39
|
default:
|
|
39
40
|
assert(char !== '\n');
|
|
40
|
-
if (context.sequential) return [
|
|
41
|
+
if (context.sequential) return new List([new Data(char)]);
|
|
41
42
|
let i = next(source, position, delimiter);
|
|
42
43
|
assert(i > position);
|
|
43
44
|
i -= position;
|
|
44
45
|
consume(i - 1, context);
|
|
45
46
|
context.position += i - 1;
|
|
46
|
-
return [
|
|
47
|
+
return new List([new Data(source.slice(position, context.position))]);
|
|
47
48
|
}
|
|
48
49
|
};
|