securemark 0.294.7 → 0.294.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/index.js +185 -205
- package/markdown.d.ts +13 -36
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +2 -2
- package/src/combinator/control/constraint/line.ts +7 -5
- package/src/combinator/control/manipulation/convert.ts +2 -1
- package/src/combinator/control/manipulation/fence.ts +4 -4
- package/src/combinator/control/manipulation/surround.ts +9 -9
- package/src/combinator/data/parser/some.ts +1 -1
- package/src/combinator/data/parser/union.ts +6 -2
- package/src/parser/api/bind.test.ts +0 -1
- package/src/parser/api/normalize.test.ts +5 -8
- package/src/parser/api/normalize.ts +4 -2
- package/src/parser/autolink.ts +1 -2
- package/src/parser/block/blockquote.ts +3 -3
- package/src/parser/block/extension/fig.ts +4 -1
- package/src/parser/block/heading.ts +12 -2
- package/src/parser/block/reply/quote.ts +1 -2
- package/src/parser/block/ulist.ts +1 -1
- package/src/parser/block.ts +0 -4
- package/src/parser/header.ts +28 -40
- package/src/parser/inline/annotation.ts +2 -3
- package/src/parser/inline/autolink/account.ts +49 -17
- package/src/parser/inline/autolink/anchor.test.ts +0 -1
- package/src/parser/inline/autolink/anchor.ts +16 -15
- package/src/parser/inline/autolink/email.test.ts +1 -1
- package/src/parser/inline/autolink/email.ts +10 -11
- package/src/parser/inline/autolink/hashnum.ts +17 -13
- package/src/parser/inline/autolink/hashtag.ts +19 -15
- package/src/parser/inline/autolink/url.ts +24 -19
- package/src/parser/inline/autolink.ts +1 -2
- package/src/parser/inline/bracket.ts +14 -14
- package/src/parser/inline/deletion.ts +2 -1
- package/src/parser/inline/emphasis.ts +2 -1
- package/src/parser/inline/emstrong.ts +2 -1
- package/src/parser/inline/extension/index.ts +4 -4
- package/src/parser/inline/extension/indexer.ts +1 -1
- package/src/parser/inline/extension/label.ts +1 -1
- package/src/parser/inline/extension/placeholder.ts +4 -3
- package/src/parser/inline/html.ts +4 -4
- package/src/parser/inline/htmlentity.ts +2 -2
- package/src/parser/inline/insertion.ts +2 -1
- package/src/parser/inline/italic.ts +2 -1
- package/src/parser/inline/link.ts +12 -24
- package/src/parser/inline/mark.ts +2 -1
- package/src/parser/inline/math.ts +4 -2
- package/src/parser/inline/media.ts +28 -33
- package/src/parser/inline/reference.ts +4 -4
- package/src/parser/inline/remark.ts +2 -1
- package/src/parser/inline/ruby.ts +3 -4
- package/src/parser/inline/strong.ts +2 -1
- package/src/parser/inline/template.ts +10 -10
- package/src/parser/segment.ts +2 -2
- package/src/parser/source/escapable.ts +3 -4
- package/src/parser/source/line.ts +3 -1
- package/src/parser/source/text.ts +5 -10
- package/src/parser/source/unescapable.ts +2 -4
- package/src/parser/source.ts +1 -2
- package/src/parser/inline/autolink/channel.ts +0 -44
|
@@ -2,10 +2,10 @@ import { MarkdownParser } from '../../../markdown';
|
|
|
2
2
|
import { LinkParser } from '../inline';
|
|
3
3
|
import { State, Backtrack, Command } from '../context';
|
|
4
4
|
import { List, Data } from '../../combinator/data/parser';
|
|
5
|
-
import { union, inits,
|
|
5
|
+
import { union, inits, sequence, subsequence, some, consume, precedence, state, constraint, surround, open, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
|
|
6
6
|
import { inline, media, shortmedia } from '../inline';
|
|
7
7
|
import { attributes } from './html';
|
|
8
|
-
import {
|
|
8
|
+
import { str } from '../source';
|
|
9
9
|
import { trimBlankStart, trimBlankNodeEnd } from '../visibility';
|
|
10
10
|
import { unwrap, invalid, stringify } from '../util';
|
|
11
11
|
import { ReadonlyURL } from 'spica/url';
|
|
@@ -16,7 +16,7 @@ const optspec = {
|
|
|
16
16
|
} as const;
|
|
17
17
|
Object.setPrototypeOf(optspec, null);
|
|
18
18
|
|
|
19
|
-
export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.link,
|
|
19
|
+
export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.link,
|
|
20
20
|
precedence(1, state(State.linkers,
|
|
21
21
|
bind(subsequence([
|
|
22
22
|
dup(surround(
|
|
@@ -24,12 +24,11 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
24
24
|
trimBlankStart(some(union([inline]), ']', [[']', 1]])),
|
|
25
25
|
']',
|
|
26
26
|
true,
|
|
27
|
+
[3 | Backtrack.bracket, 3 | Backtrack.link, 2 | Backtrack.ruby],
|
|
27
28
|
([, ns = new List()], context) =>
|
|
28
29
|
context.linebreak === 0
|
|
29
30
|
? ns.push(new Data(Command.Separator)) && ns
|
|
30
|
-
: undefined,
|
|
31
|
-
undefined,
|
|
32
|
-
[3 | Backtrack.bracket, 3 | Backtrack.link, 2 | Backtrack.ruby])),
|
|
31
|
+
: undefined)),
|
|
33
32
|
// `{ `と`{`で個別にバックトラックが発生し+1nされる。
|
|
34
33
|
// 自己再帰的にパースしてもオプションの不要なパースによる計算量の増加により相殺される。
|
|
35
34
|
dup(surround(
|
|
@@ -37,14 +36,14 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
37
36
|
inits([uri, some(option)]),
|
|
38
37
|
/ ?}/y,
|
|
39
38
|
false,
|
|
39
|
+
[3 | Backtrack.link],
|
|
40
40
|
undefined,
|
|
41
41
|
([as, bs], context) => {
|
|
42
42
|
if (!bs) return;
|
|
43
43
|
const head = context.position - context.range!;
|
|
44
44
|
setBacktrack(context, [2 | Backtrack.link], head);
|
|
45
45
|
return as.import(bs).push(new Data(Command.Cancel)) && as;
|
|
46
|
-
},
|
|
47
|
-
[3 | Backtrack.link])),
|
|
46
|
+
})),
|
|
48
47
|
]),
|
|
49
48
|
([{ value: content }, { value: params = undefined } = {}], context) => {
|
|
50
49
|
if (content.last!.value === Command.Separator) {
|
|
@@ -73,9 +72,9 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
73
72
|
assert(content.head?.value !== '');
|
|
74
73
|
if (content.length !== 0 && trimBlankNodeEnd(content).length === 0) return;
|
|
75
74
|
return new List([new Data(parse(content, params as List<Data<string>>, context))]);
|
|
76
|
-
})))))
|
|
75
|
+
})))));
|
|
77
76
|
|
|
78
|
-
export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State.link | State.media,
|
|
77
|
+
export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State.link | State.media,
|
|
79
78
|
state(State.linkers,
|
|
80
79
|
bind(sequence([
|
|
81
80
|
dup(surround(
|
|
@@ -85,19 +84,7 @@ export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State
|
|
|
85
84
|
dup(surround(/{(?![{}])/y, inits([uri, some(option)]), / ?}/y)),
|
|
86
85
|
]),
|
|
87
86
|
([{ value: content }, { value: params }], context) =>
|
|
88
|
-
new List([new Data(parse(content, params as List<Data<string>>, context))])))))
|
|
89
|
-
|
|
90
|
-
export const unsafelink: LinkParser.UnsafeLinkParser = lazy(() =>
|
|
91
|
-
creation(10,
|
|
92
|
-
bind(reverse(tails([
|
|
93
|
-
dup(surround(
|
|
94
|
-
'[',
|
|
95
|
-
some(union([unescsource]), ']'),
|
|
96
|
-
']')),
|
|
97
|
-
dup(surround(/{(?![{}])/y, inits([uri, some(option)]), / ?}/y)),
|
|
98
|
-
])),
|
|
99
|
-
([{ value: params }, { value: content } = new Data(new List<Data<string>>())], context) =>
|
|
100
|
-
new List([new Data(parse(content, params, context))]))));
|
|
87
|
+
new List([new Data(parse(content, params as List<Data<string>>, context))])))));
|
|
101
88
|
|
|
102
89
|
export const uri: LinkParser.ParameterParser.UriParser = union([
|
|
103
90
|
open(/ /y, str(/\S+/y)),
|
|
@@ -110,7 +97,7 @@ export const option: LinkParser.ParameterParser.OptionParser = union([
|
|
|
110
97
|
str(/ [^\s{}]+/y),
|
|
111
98
|
]);
|
|
112
99
|
|
|
113
|
-
function parse(
|
|
100
|
+
export function parse(
|
|
114
101
|
content: List<Data<string | HTMLElement>>,
|
|
115
102
|
params: List<Data<string>>,
|
|
116
103
|
context: MarkdownParser.Context,
|
|
@@ -119,6 +106,7 @@ function parse(
|
|
|
119
106
|
const INSECURE_URI = params.shift()!.value;
|
|
120
107
|
assert(INSECURE_URI === INSECURE_URI.trim());
|
|
121
108
|
assert(!INSECURE_URI.match(/\s/));
|
|
109
|
+
consume(10, context);
|
|
122
110
|
let uri: ReadonlyURL | undefined;
|
|
123
111
|
try{
|
|
124
112
|
uri = new ReadonlyURL(
|
|
@@ -16,7 +16,8 @@ export const mark: MarkParser = lazy(() => constraint(State.linkers & ~State.mar
|
|
|
16
16
|
some(inline, blankWith('==')),
|
|
17
17
|
open(some(inline, '='), inline),
|
|
18
18
|
])))),
|
|
19
|
-
'==',
|
|
19
|
+
'==',
|
|
20
|
+
false, [],
|
|
20
21
|
([, bs], { buffer }) => buffer!.import(bs),
|
|
21
22
|
([, bs], { buffer }) => bs && buffer!.import(bs).push(new Data(Command.Cancel)) && buffer!),
|
|
22
23
|
(nodes, { id }) => {
|
|
@@ -14,7 +14,8 @@ export const math: MathParser = lazy(() => rewrite(
|
|
|
14
14
|
/\$(?={)/y,
|
|
15
15
|
precedence(4, bracket),
|
|
16
16
|
'$',
|
|
17
|
-
false,
|
|
17
|
+
false,
|
|
18
|
+
[3 | Backtrack.bracket]),
|
|
18
19
|
surround(
|
|
19
20
|
/\$(?![\s{}])/y,
|
|
20
21
|
precedence(2, some(union([
|
|
@@ -22,7 +23,8 @@ export const math: MathParser = lazy(() => rewrite(
|
|
|
22
23
|
precedence(4, bracket),
|
|
23
24
|
]))),
|
|
24
25
|
/\$(?![-0-9A-Za-z])/y,
|
|
25
|
-
false,
|
|
26
|
+
false,
|
|
27
|
+
[3 | Backtrack.bracket]),
|
|
26
28
|
]),
|
|
27
29
|
({ context: { source, caches: { math: cache } = {} } }) => new List([
|
|
28
30
|
new Data(cache?.get(source)?.cloneNode(true) ||
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { MediaParser } from '../inline';
|
|
2
2
|
import { State, Recursion, Backtrack, Command } from '../context';
|
|
3
|
-
import { List, Data
|
|
4
|
-
import { union, inits, tails, some,
|
|
5
|
-
import {
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
4
|
+
import { union, inits, tails, some, consume, recursion, precedence, constraint, surround, open, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
|
|
5
|
+
import { uri, option as linkoption, resolve, decode, parse } from './link';
|
|
6
6
|
import { attributes } from './html';
|
|
7
7
|
import { unsafehtmlentity } from './htmlentity';
|
|
8
8
|
import { txt, str } from '../source';
|
|
@@ -18,7 +18,7 @@ const optspec = {
|
|
|
18
18
|
} as const;
|
|
19
19
|
Object.setPrototypeOf(optspec, null);
|
|
20
20
|
|
|
21
|
-
export const media: MediaParser = lazy(() => constraint(State.media,
|
|
21
|
+
export const media: MediaParser = lazy(() => constraint(State.media, open(
|
|
22
22
|
'!',
|
|
23
23
|
bind(fmap(tails([
|
|
24
24
|
dup(surround(
|
|
@@ -30,25 +30,24 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
|
|
|
30
30
|
]), ']')),
|
|
31
31
|
']',
|
|
32
32
|
true,
|
|
33
|
+
[3 | Backtrack.escbracket],
|
|
33
34
|
([, ns = new List()], context) =>
|
|
34
35
|
context.linebreak === 0
|
|
35
36
|
? ns
|
|
36
|
-
: undefined,
|
|
37
|
-
undefined,
|
|
38
|
-
[3 | Backtrack.escbracket])),
|
|
37
|
+
: undefined)),
|
|
39
38
|
dup(surround(
|
|
40
39
|
/{(?![{}])/y,
|
|
41
40
|
inits([uri, some(option)]),
|
|
42
41
|
/ ?}/y,
|
|
43
42
|
false,
|
|
43
|
+
[3 | Backtrack.link],
|
|
44
44
|
undefined,
|
|
45
45
|
([as, bs], context) => {
|
|
46
46
|
if (!bs) return;
|
|
47
47
|
const head = context.position - context.range!;
|
|
48
48
|
setBacktrack(context, [2 | Backtrack.link], head);
|
|
49
49
|
return as.import(bs).push(new Data(Command.Cancel)) && as;
|
|
50
|
-
},
|
|
51
|
-
[3 | Backtrack.link])),
|
|
50
|
+
})),
|
|
52
51
|
]),
|
|
53
52
|
nodes =>
|
|
54
53
|
nodes.length === 1
|
|
@@ -57,6 +56,7 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
|
|
|
57
56
|
([{ value: [{ value: text }] }, { value: params }], context) => {
|
|
58
57
|
if (text && text.trimStart() === '') return;
|
|
59
58
|
text = text.trim();
|
|
59
|
+
consume(100, context);
|
|
60
60
|
if (params.last!.value === Command.Cancel) {
|
|
61
61
|
params.pop();
|
|
62
62
|
return new List([
|
|
@@ -98,26 +98,26 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
|
|
|
98
98
|
}
|
|
99
99
|
if (context.state! & State.link) return new List([new Data(el)]);
|
|
100
100
|
if (cache && cache.tagName !== 'IMG') return new List([new Data(el)]);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
}))))
|
|
101
|
+
return new List([new Data(define(
|
|
102
|
+
parse(
|
|
103
|
+
new List(),
|
|
104
|
+
linkparams.reduce(
|
|
105
|
+
(acc, p) => acc.push(new Data(p)) && acc,
|
|
106
|
+
new List([new Data(INSECURE_URI)])),
|
|
107
|
+
context),
|
|
108
|
+
{ class: null, target: '_blank' }, [el]))
|
|
109
|
+
]);
|
|
110
|
+
}))));
|
|
111
111
|
|
|
112
112
|
const bracket: MediaParser.TextParser.BracketParser = lazy(() => recursion(Recursion.terminal, union([
|
|
113
|
-
surround(str('('), some(union([unsafehtmlentity, bracket, txt]), ')'), str(')'),
|
|
114
|
-
undefined, () => new List()
|
|
115
|
-
surround(str('['), some(union([unsafehtmlentity, bracket, txt]), ']'), str(']'),
|
|
116
|
-
undefined, () => new List()
|
|
117
|
-
surround(str('{'), some(union([unsafehtmlentity, bracket, txt]), '}'), str('}'),
|
|
118
|
-
undefined, () => new List()
|
|
119
|
-
surround(str('"'), precedence(2, some(union([unsafehtmlentity, txt]), '"')), str('"'),
|
|
120
|
-
undefined, () => new List()
|
|
113
|
+
surround(str('('), some(union([unsafehtmlentity, bracket, txt]), ')'), str(')'),
|
|
114
|
+
true, [3 | Backtrack.escbracket], undefined, () => new List()),
|
|
115
|
+
surround(str('['), some(union([unsafehtmlentity, bracket, txt]), ']'), str(']'),
|
|
116
|
+
true, [3 | Backtrack.escbracket], undefined, () => new List()),
|
|
117
|
+
surround(str('{'), some(union([unsafehtmlentity, bracket, txt]), '}'), str('}'),
|
|
118
|
+
true, [3 | Backtrack.escbracket], undefined, () => new List()),
|
|
119
|
+
surround(str('"'), precedence(2, some(union([unsafehtmlentity, txt]), '"')), str('"'),
|
|
120
|
+
true, [3 | Backtrack.escbracket], undefined, () => new List()),
|
|
121
121
|
])));
|
|
122
122
|
|
|
123
123
|
const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
|
|
@@ -125,7 +125,7 @@ const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
|
|
|
125
125
|
open(/ /y, str(/[1-9][0-9]*/y)),
|
|
126
126
|
str(/[x:]/y),
|
|
127
127
|
str(/[1-9][0-9]*(?=[ }])/y),
|
|
128
|
-
false,
|
|
128
|
+
false, [],
|
|
129
129
|
([[{ value: a }], [{ value: b }], [{ value: c }]]) =>
|
|
130
130
|
b === 'x'
|
|
131
131
|
? new List([new Data(`width="${a}"`), new Data(`height="${c}"`)])
|
|
@@ -154,11 +154,6 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL | undefined): boolean {
|
|
|
154
154
|
type = 'argument';
|
|
155
155
|
message = 'Invalid protocol';
|
|
156
156
|
}
|
|
157
|
-
//else {
|
|
158
|
-
// target.setAttribute('alt', alt.replace(CmdRegExp.Error, ''));
|
|
159
|
-
// type = 'argument';
|
|
160
|
-
// message = `Invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`;
|
|
161
|
-
//}
|
|
162
157
|
define(target, {
|
|
163
158
|
'data-src': null,
|
|
164
159
|
class: 'invalid',
|
|
@@ -18,6 +18,7 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
18
18
|
]))),
|
|
19
19
|
']]',
|
|
20
20
|
false,
|
|
21
|
+
[1 | Backtrack.bracket, 3 | Backtrack.doublebracket],
|
|
21
22
|
([, ns], context) => {
|
|
22
23
|
const { position, range = 0, linebreak = 0 } = context;
|
|
23
24
|
if (linebreak === 0) {
|
|
@@ -69,7 +70,7 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
69
70
|
'',
|
|
70
71
|
some(inline, ']', [[']', 1]]),
|
|
71
72
|
str(']'),
|
|
72
|
-
true,
|
|
73
|
+
true, [],
|
|
73
74
|
([, cs = new List(), ds]) =>
|
|
74
75
|
cs.import(ds),
|
|
75
76
|
([, cs = new List()]) => {
|
|
@@ -86,15 +87,14 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
86
87
|
return state & State.annotation
|
|
87
88
|
? as.import(bs as List<Data<string>>)
|
|
88
89
|
: undefined;
|
|
89
|
-
}
|
|
90
|
-
[1 | Backtrack.bracket, 3 | Backtrack.doublebracket])));
|
|
90
|
+
})));
|
|
91
91
|
|
|
92
92
|
// Chicago-Style
|
|
93
93
|
const abbr: ReferenceParser.AbbrParser = surround(
|
|
94
94
|
str('^'),
|
|
95
95
|
union([str(/(?=[A-Z])(?:[0-9A-Za-z]'?|(?:[-.:]|\.?\??,? ?)(?!['\-.:?, ]))+/y)]),
|
|
96
96
|
/\|?(?=]])|\|/y,
|
|
97
|
-
true,
|
|
97
|
+
true, [],
|
|
98
98
|
([, ns], context) => {
|
|
99
99
|
const { source, position, range = 0 } = context;
|
|
100
100
|
if (!ns) return new List([new Data(''), new Data(source.slice(position - range, source[position - 1] === '|' ? position - 1 : position))]);
|
|
@@ -11,7 +11,8 @@ export const remark: RemarkParser = lazy(() => fallback(surround(
|
|
|
11
11
|
str(/\[%(?=[ \n])/y),
|
|
12
12
|
precedence(3, recursion(Recursion.inline,
|
|
13
13
|
some(union([inline]), /[ \n]%\]/y, [[/[ \n]%\]/y, 3]]))),
|
|
14
|
-
close(text, str(`%]`)),
|
|
14
|
+
close(text, str(`%]`)),
|
|
15
|
+
true, [],
|
|
15
16
|
([as, bs = new List(), cs]) => new List([
|
|
16
17
|
new Data(html('span', { class: 'remark' }, [
|
|
17
18
|
html('input', { type: 'checkbox' }),
|
|
@@ -13,15 +13,14 @@ export const ruby: RubyParser = lazy(() => bind(
|
|
|
13
13
|
dup(surround(
|
|
14
14
|
'[', text, ']',
|
|
15
15
|
false,
|
|
16
|
+
[1 | Backtrack.bracket, 3 | Backtrack.ruby],
|
|
16
17
|
([, ns]) => {
|
|
17
18
|
ns && ns.last?.value === '' && ns.pop();
|
|
18
19
|
return isTightNodeStart(ns) ? ns : undefined;
|
|
19
|
-
},
|
|
20
|
-
undefined,
|
|
21
|
-
[1 | Backtrack.bracket, 3 | Backtrack.ruby])),
|
|
20
|
+
})),
|
|
22
21
|
dup(surround(
|
|
23
22
|
'(', text, ')',
|
|
24
|
-
false,
|
|
23
|
+
false,
|
|
25
24
|
[1 | Backtrack.bracket, 3 | Backtrack.ruby])),
|
|
26
25
|
]),
|
|
27
26
|
([{ value: texts }, { value: rubies = undefined } = {}], context) => {
|
|
@@ -17,6 +17,7 @@ export const strong: StrongParser = lazy(() => surround(
|
|
|
17
17
|
some(inline, blankWith('*')),
|
|
18
18
|
open(some(inline, '*'), inline),
|
|
19
19
|
]))))),
|
|
20
|
-
str('**'),
|
|
20
|
+
str('**'),
|
|
21
|
+
false, [],
|
|
21
22
|
([, bs]) => new List([new Data(html('strong', defrag(unwrap(bs))))]),
|
|
22
23
|
([as, bs]) => bs && as.import(bs as List<Data<string>>)));
|
|
@@ -12,6 +12,7 @@ export const template: TemplateParser = lazy(() => surround(
|
|
|
12
12
|
some(union([bracket, escsource]), '}')),
|
|
13
13
|
str('}}'),
|
|
14
14
|
true,
|
|
15
|
+
[3 | Backtrack.doublebracket, 3 | Backtrack.escbracket],
|
|
15
16
|
([as, bs = new List(), cs]) => new List([
|
|
16
17
|
new Data(html('span', { class: 'template' }, defrag(unwrap(as.import(bs as List<Data<string>>).import(cs)))))
|
|
17
18
|
]),
|
|
@@ -23,25 +24,24 @@ export const template: TemplateParser = lazy(() => surround(
|
|
|
23
24
|
...invalid('template', 'syntax', `Missing the closing symbol "}}"`),
|
|
24
25
|
},
|
|
25
26
|
context.source.slice(context.position - context.range!, context.position)))
|
|
26
|
-
])
|
|
27
|
-
[3 | Backtrack.doublebracket, 3 | Backtrack.escbracket]));
|
|
27
|
+
])));
|
|
28
28
|
|
|
29
29
|
const bracket: TemplateParser.BracketParser = lazy(() => union([
|
|
30
|
-
surround(str('('), recursion(Recursion.terminal, some(union([bracket, escsource]), ')')), str(')'),
|
|
31
|
-
undefined, () => new List()
|
|
32
|
-
surround(str('['), recursion(Recursion.terminal, some(union([bracket, escsource]), ']')), str(']'),
|
|
33
|
-
undefined, () => new List()
|
|
34
|
-
surround(str('{'), recursion(Recursion.terminal, some(union([bracket, escsource]), '}')), str('}'),
|
|
35
|
-
undefined, () => new List()
|
|
30
|
+
surround(str('('), recursion(Recursion.terminal, some(union([bracket, escsource]), ')')), str(')'),
|
|
31
|
+
true, [3 | Backtrack.escbracket], undefined, () => new List()),
|
|
32
|
+
surround(str('['), recursion(Recursion.terminal, some(union([bracket, escsource]), ']')), str(']'),
|
|
33
|
+
true, [3 | Backtrack.escbracket], undefined, () => new List()),
|
|
34
|
+
surround(str('{'), recursion(Recursion.terminal, some(union([bracket, escsource]), '}')), str('}'),
|
|
35
|
+
true, [3 | Backtrack.escbracket], undefined, () => new List()),
|
|
36
36
|
surround(
|
|
37
37
|
str('"'),
|
|
38
38
|
precedence(2, recursion(Recursion.terminal, some(escsource, /["\n]/y, [['"', 2], ['\n', 3]]))),
|
|
39
39
|
str('"'),
|
|
40
40
|
true,
|
|
41
|
+
[3 | Backtrack.escbracket],
|
|
41
42
|
([as, bs = new List(), cs], context) =>
|
|
42
43
|
context.linebreak === 0
|
|
43
44
|
? as.import(bs as List<Data<string>>).import(cs)
|
|
44
45
|
: (context.position -= 1, as.import(bs as List<Data<string>>)),
|
|
45
|
-
([as, bs]) => bs && as.import(bs as List<Data<string>>),
|
|
46
|
-
[3 | Backtrack.escbracket]),
|
|
46
|
+
([as, bs]) => bs && as.import(bs as List<Data<string>>)),
|
|
47
47
|
]));
|
package/src/parser/segment.ts
CHANGED
|
@@ -13,6 +13,7 @@ export const MAX_SEGMENT_SIZE = 100_000; // 100,000 bytes (Max value size of FDB
|
|
|
13
13
|
export const MAX_INPUT_SIZE = MAX_SEGMENT_SIZE * 10;
|
|
14
14
|
|
|
15
15
|
const parser: SegmentParser = union([
|
|
16
|
+
some(emptyline),
|
|
16
17
|
input => {
|
|
17
18
|
const { context: { source, position } } = input;
|
|
18
19
|
if (position === source.length) return;
|
|
@@ -35,8 +36,7 @@ const parser: SegmentParser = union([
|
|
|
35
36
|
return extension(input);
|
|
36
37
|
}
|
|
37
38
|
},
|
|
38
|
-
some(contentline
|
|
39
|
-
some(emptyline, MAX_SEGMENT_SIZE + 1),
|
|
39
|
+
some(contentline),
|
|
40
40
|
]) as any;
|
|
41
41
|
|
|
42
42
|
export function* segment(source: string): Generator<string, undefined, undefined> {
|
|
@@ -14,10 +14,6 @@ export const escsource: EscapableSourceParser = ({ context }) => {
|
|
|
14
14
|
consume(1, context);
|
|
15
15
|
context.position += 1;
|
|
16
16
|
switch (char) {
|
|
17
|
-
case '\r':
|
|
18
|
-
assert(!source.includes('\r', position + 1));
|
|
19
|
-
consume(-1, context);
|
|
20
|
-
return new List();
|
|
21
17
|
case Command.Escape:
|
|
22
18
|
consume(1, context);
|
|
23
19
|
context.position += 1;
|
|
@@ -33,6 +29,9 @@ export const escsource: EscapableSourceParser = ({ context }) => {
|
|
|
33
29
|
context.position += 1;
|
|
34
30
|
return new List([new Data(source.slice(position, position + 2))]);
|
|
35
31
|
}
|
|
32
|
+
case '\r':
|
|
33
|
+
consume(-1, context);
|
|
34
|
+
return new List();
|
|
36
35
|
case '\n':
|
|
37
36
|
context.linebreak ||= source.length - position;
|
|
38
37
|
return new List([new Data(html('br'))]);
|
|
@@ -8,6 +8,7 @@ export const anyline: AnyLineParser = input => {
|
|
|
8
8
|
context.position = source.indexOf('\n', position) + 1 || source.length;
|
|
9
9
|
return new List();
|
|
10
10
|
};
|
|
11
|
+
|
|
11
12
|
const regEmptyline = /[^\S\n]*(?:$|\n)/y;
|
|
12
13
|
export const emptyline: EmptyLineParser = input => {
|
|
13
14
|
const { context } = input;
|
|
@@ -21,6 +22,7 @@ export const emptyline: EmptyLineParser = input => {
|
|
|
21
22
|
context.position = i;
|
|
22
23
|
return new List();
|
|
23
24
|
};
|
|
25
|
+
|
|
24
26
|
const regContentline = /[^\S\n]*\S[^\n]*(?:$|\n)/y;
|
|
25
27
|
export const contentline: ContentLineParser = input => {
|
|
26
28
|
const { context } = input;
|
|
@@ -33,4 +35,4 @@ export const contentline: ContentLineParser = input => {
|
|
|
33
35
|
if (i === 0) return;
|
|
34
36
|
context.position = i;
|
|
35
37
|
return new List();
|
|
36
|
-
}
|
|
38
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { TextParser, TxtParser
|
|
1
|
+
import { TextParser, TxtParser } from '../source';
|
|
2
2
|
import { Command } from '../context';
|
|
3
3
|
import { List, Data } from '../../combinator/data/parser';
|
|
4
|
-
import { union, consume
|
|
4
|
+
import { union, consume } from '../../combinator';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
7
|
//const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
|
|
@@ -15,10 +15,6 @@ export const text: TextParser = input => {
|
|
|
15
15
|
consume(1, context);
|
|
16
16
|
context.position += 1;
|
|
17
17
|
switch (char) {
|
|
18
|
-
case '\r':
|
|
19
|
-
assert(!source.includes('\r', position + 1));
|
|
20
|
-
consume(-1, context);
|
|
21
|
-
return new List();
|
|
22
18
|
case Command.Escape:
|
|
23
19
|
case '\\':
|
|
24
20
|
switch (source[position + 1]) {
|
|
@@ -32,6 +28,9 @@ export const text: TextParser = input => {
|
|
|
32
28
|
context.position += 1;
|
|
33
29
|
return new List([new Data(source.slice(position + 1, context.position))]);
|
|
34
30
|
}
|
|
31
|
+
case '\r':
|
|
32
|
+
consume(-1, context);
|
|
33
|
+
return new List();
|
|
35
34
|
case '\n':
|
|
36
35
|
context.linebreak ||= source.length - position;
|
|
37
36
|
return new List([new Data(html('br'))]);
|
|
@@ -64,10 +63,6 @@ export const txt: TxtParser = union([
|
|
|
64
63
|
text,
|
|
65
64
|
]) as TxtParser;
|
|
66
65
|
|
|
67
|
-
export const linebreak: LinebreakParser = focus(/[\r\n]/y, union([
|
|
68
|
-
text,
|
|
69
|
-
])) as LinebreakParser;
|
|
70
|
-
|
|
71
66
|
export function canSkip(source: string, position: number): boolean {
|
|
72
67
|
assert(position < source.length);
|
|
73
68
|
if (!isWhitespace(source[position], false)) return false;
|
|
@@ -14,14 +14,12 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
|
|
|
14
14
|
consume(1, context);
|
|
15
15
|
context.position += 1;
|
|
16
16
|
switch (char) {
|
|
17
|
-
case '\r':
|
|
18
|
-
assert(!source.includes('\r', position + 1));
|
|
19
|
-
consume(-1, context);
|
|
20
|
-
return new List();
|
|
21
17
|
case Command.Escape:
|
|
22
18
|
consume(1, context);
|
|
23
19
|
context.position += 1;
|
|
24
20
|
return new List([new Data(source.slice(position + 1, position + 2))]);
|
|
21
|
+
case '\r':
|
|
22
|
+
return new List();
|
|
25
23
|
case '\n':
|
|
26
24
|
context.linebreak ||= source.length - position;
|
|
27
25
|
return new List([new Data(html('br'))]);
|
package/src/parser/source.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { MarkdownParser } from '../../markdown';
|
|
|
3
3
|
import SourceParser = MarkdownParser.SourceParser;
|
|
4
4
|
export import TextParser = SourceParser.TextParser;
|
|
5
5
|
export import TxtParser = SourceParser.TxtParser;
|
|
6
|
-
export import LinebreakParser = SourceParser.LinebreakParser;
|
|
7
6
|
export import EscapableSourceParser = SourceParser.EscapableSourceParser;
|
|
8
7
|
export import UnescapableSourceParser = SourceParser.UnescapableSourceParser;
|
|
9
8
|
export import StrParser = SourceParser.StrParser;
|
|
@@ -11,7 +10,7 @@ export import ContentLineParser = SourceParser.ContentLineParser;
|
|
|
11
10
|
export import EmptyLineParser = SourceParser.EmptyLineParser;
|
|
12
11
|
export import AnyLineParser = SourceParser.AnyLineParser;
|
|
13
12
|
|
|
14
|
-
export { text, txt
|
|
13
|
+
export { text, txt } from './source/text';
|
|
15
14
|
export { escsource } from './source/escapable';
|
|
16
15
|
export { unescsource } from './source/unescapable';
|
|
17
16
|
export { str, strs } from './source/str';
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { State, Backtrack } from '../../context';
|
|
3
|
-
import { List, Data } from '../../../combinator/data/parser';
|
|
4
|
-
import { union, sequence, some, state, constraint, verify, rewrite, surround, convert, fmap, lazy } from '../../../combinator';
|
|
5
|
-
import { unsafelink } from '../link';
|
|
6
|
-
import { emoji } from './hashtag';
|
|
7
|
-
import { str } from '../../source';
|
|
8
|
-
import { define } from 'typed-dom/dom';
|
|
9
|
-
|
|
10
|
-
// https://example/@user?ch=a+b must be a user channel page or a redirect page going there.
|
|
11
|
-
|
|
12
|
-
export const channel: AutolinkParser.ChannelParser = lazy(() => rewrite(
|
|
13
|
-
sequence([
|
|
14
|
-
surround(
|
|
15
|
-
/(?<![0-9a-z])@/yi,
|
|
16
|
-
/[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi,
|
|
17
|
-
/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@]|>>|:\S)/yi,
|
|
18
|
-
true, undefined, undefined,
|
|
19
|
-
[3 | Backtrack.autolink]),
|
|
20
|
-
some(verify(surround(
|
|
21
|
-
'#',
|
|
22
|
-
str(new RegExp([
|
|
23
|
-
/(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source,
|
|
24
|
-
].join('|').replace(/emoji/g, emoji.source), 'yu')),
|
|
25
|
-
new RegExp([
|
|
26
|
-
/(?![0-9a-z@]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
|
|
27
|
-
].join('|').replace(/emoji/g, emoji.source), 'yu'),
|
|
28
|
-
false, undefined, undefined,
|
|
29
|
-
[3 | Backtrack.autolink]),
|
|
30
|
-
([{ value }]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(value as string))),
|
|
31
|
-
]),
|
|
32
|
-
constraint(State.autolink, state(State.autolink, fmap(convert(
|
|
33
|
-
source =>
|
|
34
|
-
`[${source}]{ ${source.includes('/')
|
|
35
|
-
? `https://${source.slice(1, source.indexOf('#')).replace('/', '/@')}`
|
|
36
|
-
: `/${source.slice(0, source.indexOf('#'))}`
|
|
37
|
-
} }`,
|
|
38
|
-
union([unsafelink]),
|
|
39
|
-
false),
|
|
40
|
-
([{ value: el }], { source, position, range = 0 }) => {
|
|
41
|
-
const src = source.slice(position - range, position);
|
|
42
|
-
const url = `${el.getAttribute('href')}?ch=${src.slice(src.indexOf('#') + 1).replace(/#/g, '+')}`;
|
|
43
|
-
return new List([new Data(define(el, { class: 'channel', href: url }, src))]);
|
|
44
|
-
})))));
|