securemark 0.283.6 → 0.284.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 +8 -0
- package/README.md +11 -11
- package/dist/index.js +172 -121
- package/markdown.d.ts +44 -30
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +3 -2
- package/src/combinator/control/constraint/contract.ts +6 -4
- package/src/combinator/control/manipulation/lazy.ts +3 -1
- package/src/combinator/control/manipulation/match.ts +3 -2
- package/src/combinator/control/manipulation/scope.ts +3 -2
- package/src/combinator/control/monad/bind.ts +3 -2
- package/src/combinator/data/parser/context.ts +12 -8
- package/src/parser/inline/autolink/account.ts +5 -4
- package/src/parser/inline/autolink/anchor.ts +5 -4
- package/src/parser/inline/autolink/channel.ts +3 -2
- package/src/parser/inline/autolink/email.ts +7 -4
- package/src/parser/inline/autolink/hashnum.ts +5 -4
- package/src/parser/inline/autolink/hashtag.ts +5 -4
- package/src/parser/inline/autolink/url.test.ts +1 -1
- package/src/parser/inline/code.ts +3 -3
- package/src/parser/inline/extension/index.ts +2 -2
- package/src/parser/inline/extension/label.ts +5 -5
- package/src/parser/inline/extension/placeholder.ts +3 -3
- package/src/parser/inline/extension.ts +3 -3
- package/src/parser/inline/html.ts +2 -2
- package/src/parser/inline/italic.test.ts +53 -0
- package/src/parser/inline/italic.ts +21 -0
- package/src/parser/inline/link.ts +2 -2
- package/src/parser/inline/math.ts +1 -1
- package/src/parser/inline/remark.ts +3 -3
- package/src/parser/inline/ruby.ts +3 -3
- package/src/parser/inline/template.ts +1 -1
- package/src/parser/inline.test.ts +5 -3
- package/src/parser/inline.ts +59 -21
- package/src/parser/source/text.ts +1 -0
- package/src/parser/visibility.ts +3 -2
package/markdown.d.ts
CHANGED
|
@@ -661,11 +661,12 @@ export namespace MarkdownParser {
|
|
|
661
661
|
InlineParser.EmStrongParser,
|
|
662
662
|
InlineParser.StrongParser,
|
|
663
663
|
InlineParser.EmphasisParser,
|
|
664
|
+
InlineParser.ItalicParser,
|
|
664
665
|
InlineParser.MathParser,
|
|
665
666
|
InlineParser.CodeParser,
|
|
666
667
|
InlineParser.HTMLEntityParser,
|
|
667
|
-
InlineParser.AutolinkParser,
|
|
668
668
|
InlineParser.BracketParser,
|
|
669
|
+
InlineParser.AutolinkParser,
|
|
669
670
|
SourceParser.TextParser,
|
|
670
671
|
]> {
|
|
671
672
|
}
|
|
@@ -1066,6 +1067,14 @@ export namespace MarkdownParser {
|
|
|
1066
1067
|
]>,
|
|
1067
1068
|
]> {
|
|
1068
1069
|
}
|
|
1070
|
+
export interface ItalicParser extends
|
|
1071
|
+
// ///abc///
|
|
1072
|
+
Inline<'italic'>,
|
|
1073
|
+
Parser<HTMLElement | string, Context, [
|
|
1074
|
+
InlineParser,
|
|
1075
|
+
ItalicParser,
|
|
1076
|
+
]> {
|
|
1077
|
+
}
|
|
1069
1078
|
export interface MathParser extends
|
|
1070
1079
|
// $expr$
|
|
1071
1080
|
// ${expr}$
|
|
@@ -1121,6 +1130,28 @@ export namespace MarkdownParser {
|
|
|
1121
1130
|
]> {
|
|
1122
1131
|
}
|
|
1123
1132
|
}
|
|
1133
|
+
export interface BracketParser extends
|
|
1134
|
+
// ()
|
|
1135
|
+
// []
|
|
1136
|
+
// {}
|
|
1137
|
+
// ""
|
|
1138
|
+
Inline<'bracket'>,
|
|
1139
|
+
Parser<HTMLElement | string, Context, [
|
|
1140
|
+
SourceParser.StrParser,
|
|
1141
|
+
InlineParser,
|
|
1142
|
+
SourceParser.StrParser,
|
|
1143
|
+
InlineParser,
|
|
1144
|
+
InlineParser,
|
|
1145
|
+
InlineParser,
|
|
1146
|
+
InlineParser,
|
|
1147
|
+
InlineParser,
|
|
1148
|
+
InlineParser,
|
|
1149
|
+
InlineParser,
|
|
1150
|
+
InlineParser,
|
|
1151
|
+
InlineParser,
|
|
1152
|
+
InlineParser,
|
|
1153
|
+
]> {
|
|
1154
|
+
}
|
|
1124
1155
|
export interface AutolinkParser extends
|
|
1125
1156
|
Inline<'autolink'>,
|
|
1126
1157
|
Parser<HTMLElement | string, Context, [
|
|
@@ -1185,14 +1216,15 @@ export namespace MarkdownParser {
|
|
|
1185
1216
|
export interface EmailParser extends
|
|
1186
1217
|
// user@host
|
|
1187
1218
|
Inline<'email'>,
|
|
1188
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1189
|
-
|
|
1219
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1220
|
+
Parser<HTMLAnchorElement, Context, []>,
|
|
1221
|
+
Parser<string, Context, []>,
|
|
1190
1222
|
]> {
|
|
1191
1223
|
}
|
|
1192
1224
|
export interface ChannelParser extends
|
|
1193
1225
|
// @user#tag
|
|
1194
1226
|
Inline<'channel'>,
|
|
1195
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1227
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1196
1228
|
InlineParser.AutolinkParser.AccountParser,
|
|
1197
1229
|
InlineParser.AutolinkParser.HashtagParser,
|
|
1198
1230
|
]> {
|
|
@@ -1200,54 +1232,36 @@ export namespace MarkdownParser {
|
|
|
1200
1232
|
export interface AccountParser extends
|
|
1201
1233
|
// @user
|
|
1202
1234
|
Inline<'account'>,
|
|
1203
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1235
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1204
1236
|
LinkParser.UnsafeLinkParser,
|
|
1237
|
+
Parser<string, Context, []>,
|
|
1205
1238
|
]> {
|
|
1206
1239
|
}
|
|
1207
1240
|
export interface HashtagParser extends
|
|
1208
1241
|
// #tag
|
|
1209
1242
|
Inline<'hashtag'>,
|
|
1210
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1243
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1211
1244
|
LinkParser.UnsafeLinkParser,
|
|
1245
|
+
Parser<string, Context, []>,
|
|
1212
1246
|
]> {
|
|
1213
1247
|
}
|
|
1214
1248
|
export interface HashnumParser extends
|
|
1215
1249
|
// #1
|
|
1216
1250
|
Inline<'hashnum'>,
|
|
1217
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1251
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1218
1252
|
LinkParser.UnsafeLinkParser,
|
|
1253
|
+
Parser<string, Context, []>,
|
|
1219
1254
|
]> {
|
|
1220
1255
|
}
|
|
1221
1256
|
export interface AnchorParser extends
|
|
1222
1257
|
// >>1
|
|
1223
1258
|
Inline<'anchor'>,
|
|
1224
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1259
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1225
1260
|
LinkParser.UnsafeLinkParser,
|
|
1261
|
+
Parser<string, Context, []>,
|
|
1226
1262
|
]> {
|
|
1227
1263
|
}
|
|
1228
1264
|
}
|
|
1229
|
-
export interface BracketParser extends
|
|
1230
|
-
// ()
|
|
1231
|
-
// []
|
|
1232
|
-
// {}
|
|
1233
|
-
// ""
|
|
1234
|
-
Inline<'bracket'>,
|
|
1235
|
-
Parser<HTMLElement | string, Context, [
|
|
1236
|
-
SourceParser.StrParser,
|
|
1237
|
-
InlineParser,
|
|
1238
|
-
SourceParser.StrParser,
|
|
1239
|
-
InlineParser,
|
|
1240
|
-
InlineParser,
|
|
1241
|
-
InlineParser,
|
|
1242
|
-
InlineParser,
|
|
1243
|
-
InlineParser,
|
|
1244
|
-
InlineParser,
|
|
1245
|
-
InlineParser,
|
|
1246
|
-
InlineParser,
|
|
1247
|
-
InlineParser,
|
|
1248
|
-
InlineParser,
|
|
1249
|
-
]> {
|
|
1250
|
-
}
|
|
1251
1265
|
}
|
|
1252
1266
|
export interface AutolinkParser extends
|
|
1253
1267
|
Markdown<'autolink'>,
|
package/package.json
CHANGED
|
@@ -4,9 +4,10 @@ import { firstline, isBlank } from './line';
|
|
|
4
4
|
export function block<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
|
|
5
5
|
export function block<T>(parser: Parser<T>, separation = true): Parser<T> {
|
|
6
6
|
assert(parser);
|
|
7
|
-
return
|
|
7
|
+
return input => {
|
|
8
|
+
const { source } = input;
|
|
8
9
|
if (source === '') return;
|
|
9
|
-
const result = parser(
|
|
10
|
+
const result = parser(input);
|
|
10
11
|
if (result === undefined) return;
|
|
11
12
|
const rest = exec(result);
|
|
12
13
|
if (separation && !isBlank(firstline(rest))) return;
|
|
@@ -23,10 +23,11 @@ export function validate<T>(patterns: string | RegExp | (string | RegExp)[] | ((
|
|
|
23
23
|
? `|| source.slice(0, ${pattern.length}) === '${pattern}'`
|
|
24
24
|
: `|| /${pattern.source}/${pattern.flags}.test(source)`).join('').slice(2),
|
|
25
25
|
].join(''));
|
|
26
|
-
return
|
|
26
|
+
return input => {
|
|
27
|
+
const { source } = input;
|
|
27
28
|
if (source === '') return;
|
|
28
29
|
if (!match(source)) return;
|
|
29
|
-
const result = parser(
|
|
30
|
+
const result = parser(input);
|
|
30
31
|
assert(check(source, result));
|
|
31
32
|
if (result === undefined) return;
|
|
32
33
|
assert(exec(result).length < source.length);
|
|
@@ -47,9 +48,10 @@ function guard<T>(f: (input: Input<Ctx>) => boolean, parser: Parser<T>): Parser<
|
|
|
47
48
|
export function verify<P extends Parser<unknown>>(parser: P, cond: (results: readonly Tree<P>[], rest: string, context: Context<P>) => boolean): P;
|
|
48
49
|
export function verify<T>(parser: Parser<T>, cond: (results: readonly T[], rest: string, context: Ctx) => boolean): Parser<T> {
|
|
49
50
|
assert(parser);
|
|
50
|
-
return
|
|
51
|
+
return input => {
|
|
52
|
+
const { source, context } = input;
|
|
51
53
|
if (source === '') return;
|
|
52
|
-
const result = parser(
|
|
54
|
+
const result = parser(input);
|
|
53
55
|
assert(check(source, result));
|
|
54
56
|
if (result === undefined) return;
|
|
55
57
|
if (!cond(eval(result), exec(result), context)) return;
|
|
@@ -4,5 +4,7 @@ export function lazy<P extends Parser<unknown>>(builder: () => P): P;
|
|
|
4
4
|
export function lazy<T>(builder: () => Parser<T>): Parser<T> {
|
|
5
5
|
let parser: Parser<T>;
|
|
6
6
|
return input =>
|
|
7
|
-
|
|
7
|
+
parser !== undefined
|
|
8
|
+
? parser(input)
|
|
9
|
+
: (parser = builder())(input);
|
|
8
10
|
}
|
|
@@ -3,12 +3,13 @@ import { Parser, exec, check } from '../../data/parser';
|
|
|
3
3
|
export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
|
|
4
4
|
export function match<T>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<T>): Parser<T> {
|
|
5
5
|
assert(!pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^'));
|
|
6
|
-
return
|
|
6
|
+
return input => {
|
|
7
|
+
const { source } = input;
|
|
7
8
|
if (source === '') return;
|
|
8
9
|
const param = source.match(pattern);
|
|
9
10
|
if (!param) return;
|
|
10
11
|
assert(source.startsWith(param[0]));
|
|
11
|
-
const result = f(param)(
|
|
12
|
+
const result = f(param)(input);
|
|
12
13
|
assert(check(source, result, false));
|
|
13
14
|
if (result === undefined) return;
|
|
14
15
|
return exec(result).length < source.length && exec(result).length <= source.length
|
|
@@ -32,13 +32,14 @@ export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Contex
|
|
|
32
32
|
export function rewrite<T>(scope: Parser<unknown>, parser: Parser<T>): Parser<T> {
|
|
33
33
|
assert(scope);
|
|
34
34
|
assert(parser);
|
|
35
|
-
return
|
|
35
|
+
return input => {
|
|
36
|
+
const { source, context } = input;
|
|
36
37
|
if (source === '') return;
|
|
37
38
|
const { logger } = context;
|
|
38
39
|
context.logger = {};
|
|
39
40
|
//const { resources = { clock: 0 } } = context;
|
|
40
41
|
//const clock = resources.clock;
|
|
41
|
-
const res1 = scope(
|
|
42
|
+
const res1 = scope(input);
|
|
42
43
|
assert(check(source, res1));
|
|
43
44
|
//resources.clock = clock;
|
|
44
45
|
context.logger = logger;
|
|
@@ -6,9 +6,10 @@ export function bind<T, P extends Parser<unknown>>(parser: Parser<T, Context<P>,
|
|
|
6
6
|
export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: Tree<P>[], rest: string, context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
|
|
7
7
|
export function bind<T, U>(parser: Parser<T>, f: (nodes: T[], rest: string, context: Ctx) => Result<U>): Parser<U> {
|
|
8
8
|
assert(parser);
|
|
9
|
-
return
|
|
9
|
+
return input => {
|
|
10
|
+
const { source, context } = input;
|
|
10
11
|
if (source === '') return;
|
|
11
|
-
const res1 = parser(
|
|
12
|
+
const res1 = parser(input);
|
|
12
13
|
assert(check(source, res1));
|
|
13
14
|
if (res1 === undefined) return;
|
|
14
15
|
const res2 = f(eval(res1), exec(res1), context);
|
|
@@ -62,14 +62,15 @@ export function creation<P extends Parser<unknown>>(cost: number, recursion: num
|
|
|
62
62
|
export function creation(cost: number, recursion: number, parser: Parser<unknown>): Parser<unknown> {
|
|
63
63
|
assert(cost >= 0);
|
|
64
64
|
assert(recursion >= 0);
|
|
65
|
-
return
|
|
65
|
+
return input => {
|
|
66
|
+
const { context } = input;
|
|
66
67
|
const resources = context.resources ?? { clock: cost || 1, recursions: [1] };
|
|
67
68
|
const { recursions } = resources;
|
|
68
69
|
assert(recursions.length > 0);
|
|
69
70
|
const rec = min(recursion, recursions.length);
|
|
70
71
|
if (rec > 0 && recursions[rec - 1] < 1) throw new Error('Too much recursion');
|
|
71
72
|
rec > 0 && --recursions[rec - 1];
|
|
72
|
-
const result = parser(
|
|
73
|
+
const result = parser(input);
|
|
73
74
|
rec > 0 && ++recursions[rec - 1];
|
|
74
75
|
if (result === undefined) return;
|
|
75
76
|
if (resources.clock < cost) throw new Error('Too many creations');
|
|
@@ -81,13 +82,14 @@ export function creation(cost: number, recursion: number, parser: Parser<unknown
|
|
|
81
82
|
export function precedence<P extends Parser<unknown>>(precedence: number, parser: P): P;
|
|
82
83
|
export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T> {
|
|
83
84
|
assert(precedence >= 0);
|
|
84
|
-
return
|
|
85
|
+
return input => {
|
|
86
|
+
const { context } = input;
|
|
85
87
|
const { delimiters, precedence: p = 0 } = context;
|
|
86
88
|
const shift = delimiters && precedence > p;
|
|
87
89
|
context.precedence = precedence;
|
|
88
90
|
// デリミタはシフト後に設定しなければならない
|
|
89
91
|
shift && delimiters.shift(precedence);
|
|
90
|
-
const result = parser(
|
|
92
|
+
const result = parser(input);
|
|
91
93
|
shift && delimiters.unshift();
|
|
92
94
|
context.precedence = p;
|
|
93
95
|
return result;
|
|
@@ -103,12 +105,13 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
|
|
|
103
105
|
}
|
|
104
106
|
assert(state);
|
|
105
107
|
assert(parser = parser!);
|
|
106
|
-
return
|
|
108
|
+
return input => {
|
|
109
|
+
const { context } = input;
|
|
107
110
|
const s = context.state ?? 0;
|
|
108
111
|
context.state = positive
|
|
109
112
|
? s | state
|
|
110
113
|
: s & ~state;
|
|
111
|
-
const result = parser(
|
|
114
|
+
const result = parser(input);
|
|
112
115
|
context.state = s;
|
|
113
116
|
return result;
|
|
114
117
|
};
|
|
@@ -123,12 +126,13 @@ export function constraint<T>(state: number, positive: boolean | Parser<T>, pars
|
|
|
123
126
|
}
|
|
124
127
|
assert(state);
|
|
125
128
|
assert(parser = parser!);
|
|
126
|
-
return
|
|
129
|
+
return input => {
|
|
130
|
+
const { context } = input;
|
|
127
131
|
const s = positive
|
|
128
132
|
? state & context.state!
|
|
129
133
|
: state & ~context.state!;
|
|
130
134
|
return s === state
|
|
131
|
-
? parser(
|
|
135
|
+
? parser(input)
|
|
132
136
|
: undefined;
|
|
133
137
|
};
|
|
134
138
|
}
|
|
@@ -7,7 +7,7 @@ import { define } from 'typed-dom/dom';
|
|
|
7
7
|
|
|
8
8
|
// https://example/@user must be a user page or a redirect page going there.
|
|
9
9
|
|
|
10
|
-
export const account: AutolinkParser.AccountParser = lazy(() =>
|
|
10
|
+
export const account: AutolinkParser.AccountParser = lazy(() => rewrite(
|
|
11
11
|
open(
|
|
12
12
|
'@',
|
|
13
13
|
tails([
|
|
@@ -15,12 +15,13 @@ export const account: AutolinkParser.AccountParser = lazy(() => constraint(State
|
|
|
15
15
|
str(/^[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*/i),
|
|
16
16
|
])),
|
|
17
17
|
union([
|
|
18
|
-
state(State.autolink, fmap(convert(
|
|
18
|
+
constraint(State.autolink, false, state(State.autolink, fmap(convert(
|
|
19
19
|
source =>
|
|
20
20
|
`[${source}]{ ${source.includes('/')
|
|
21
21
|
? `https://${source.slice(1).replace('/', '/@')}`
|
|
22
22
|
: `/${source}`
|
|
23
23
|
} }`,
|
|
24
24
|
unsafelink),
|
|
25
|
-
([el]) => [define(el, { class: 'account' })])),
|
|
26
|
-
|
|
25
|
+
([el]) => [define(el, { class: 'account' })]))),
|
|
26
|
+
({ source }) => [[source], ''],
|
|
27
|
+
])));
|
|
@@ -14,16 +14,17 @@ import { define } from 'typed-dom/dom';
|
|
|
14
14
|
// 内部表現はUnixTimeに統一する(時系列順)
|
|
15
15
|
// 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
|
|
16
16
|
|
|
17
|
-
export const anchor: AutolinkParser.AnchorParser = lazy(() =>
|
|
17
|
+
export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>',
|
|
18
18
|
focus(
|
|
19
19
|
/^>>(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?![0-9a-z@#:])/i,
|
|
20
20
|
union([
|
|
21
|
-
state(State.autolink, fmap(convert(
|
|
21
|
+
constraint(State.autolink, false, state(State.autolink, fmap(convert(
|
|
22
22
|
source =>
|
|
23
23
|
`[${source}]{ ${source.includes('/')
|
|
24
24
|
? `/@${source.slice(2).replace('/', '/timeline?at=')}`
|
|
25
25
|
: `?at=${source.slice(2)}`
|
|
26
26
|
} }`,
|
|
27
27
|
unsafelink),
|
|
28
|
-
([el]) => [define(el, { class: 'anchor' })])),
|
|
29
|
-
|
|
28
|
+
([el]) => [define(el, { class: 'anchor' })]))),
|
|
29
|
+
({ source }) => [[source], ''],
|
|
30
|
+
]))));
|
|
@@ -8,12 +8,13 @@ import { define } from 'typed-dom/dom';
|
|
|
8
8
|
|
|
9
9
|
// https://example/@user?ch=a+b must be a user channel page or a redirect page going there.
|
|
10
10
|
|
|
11
|
-
export const channel: AutolinkParser.ChannelParser =
|
|
11
|
+
export const channel: AutolinkParser.ChannelParser = validate('@',
|
|
12
|
+
constraint(State.autolink, false, bind(
|
|
12
13
|
sequence([
|
|
13
14
|
account,
|
|
14
15
|
some(hashtag),
|
|
15
16
|
]),
|
|
16
|
-
(es, rest) => {
|
|
17
|
+
(es: [HTMLAnchorElement], rest) => {
|
|
17
18
|
const source = stringify(es);
|
|
18
19
|
const el = es[0];
|
|
19
20
|
const url = `${el.getAttribute('href')}?ch=${source.slice(source.indexOf('#') + 1).replace(/#/g, '+')}`;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { State, Recursion } from '../../context';
|
|
3
|
-
import { creation, state, constraint, verify, rewrite } from '../../../combinator';
|
|
3
|
+
import { union, creation, state, constraint, verify, rewrite } from '../../../combinator';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
7
|
// https://html.spec.whatwg.org/multipage/input.html
|
|
8
8
|
|
|
9
|
-
export const email: AutolinkParser.EmailParser =
|
|
9
|
+
export const email: AutolinkParser.EmailParser = creation(1, Recursion.ignore, rewrite(verify(
|
|
10
10
|
str(/^[0-9a-z](?:[_.+-](?=[0-9a-z])|[0-9a-z]){0,255}@[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])?)*(?![0-9a-z])/i),
|
|
11
11
|
([source]) => source.length <= 255),
|
|
12
|
-
|
|
13
|
-
(
|
|
12
|
+
union([
|
|
13
|
+
constraint(State.autolink, false, state(State.autolink,
|
|
14
|
+
({ source }) => [[html('a', { class: 'email', href: `mailto:${source}` }, source)], ''])),
|
|
15
|
+
({ source }) => [[source], ''],
|
|
16
|
+
])));
|
|
@@ -6,11 +6,12 @@ import { emoji } from './hashtag';
|
|
|
6
6
|
import { str } from '../../source';
|
|
7
7
|
import { define } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
|
-
export const hashnum: AutolinkParser.HashnumParser = lazy(() =>
|
|
9
|
+
export const hashnum: AutolinkParser.HashnumParser = lazy(() => rewrite(
|
|
10
10
|
open('#', str(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, emoji), 'u'))),
|
|
11
11
|
union([
|
|
12
|
-
state(State.autolink, fmap(convert(
|
|
12
|
+
constraint(State.autolink, false, state(State.autolink, fmap(convert(
|
|
13
13
|
source => `[${source}]{ ${source.slice(1)} }`,
|
|
14
14
|
unsafelink),
|
|
15
|
-
([el]) => [define(el, { class: 'hashnum', href: null })])),
|
|
16
|
-
|
|
15
|
+
([el]) => [define(el, { class: 'hashnum', href: null })]))),
|
|
16
|
+
({ source }) => [[source], ''],
|
|
17
|
+
])));
|
|
@@ -10,7 +10,7 @@ import { define } from 'typed-dom/dom';
|
|
|
10
10
|
// https://github.com/tc39/proposal-regexp-unicode-property-escapes#matching-emoji
|
|
11
11
|
export const emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
|
|
12
12
|
|
|
13
|
-
export const hashtag: AutolinkParser.HashtagParser = lazy(() =>
|
|
13
|
+
export const hashtag: AutolinkParser.HashtagParser = lazy(() => rewrite(
|
|
14
14
|
open(
|
|
15
15
|
'#',
|
|
16
16
|
str(new RegExp([
|
|
@@ -18,8 +18,9 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => constraint(State
|
|
|
18
18
|
/(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source,
|
|
19
19
|
].join('').replace(/emoji/g, emoji), 'u'))),
|
|
20
20
|
union([
|
|
21
|
-
state(State.autolink, fmap(convert(
|
|
21
|
+
constraint(State.autolink, false, state(State.autolink, fmap(convert(
|
|
22
22
|
source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`,
|
|
23
23
|
unsafelink),
|
|
24
|
-
([el]) => [define(el, { class: 'hashtag' })])),
|
|
25
|
-
|
|
24
|
+
([el]) => [define(el, { class: 'hashtag' })]))),
|
|
25
|
+
({ source }) => [[source], ''],
|
|
26
|
+
])));
|
|
@@ -16,7 +16,7 @@ describe('Unit: parser/inline/autolink/url', () => {
|
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('Http://host')), [['Http'], '://host']);
|
|
17
17
|
//assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0%1]')), [['<a class="invalid">http://[::ffff:0:0%1]</a>'], '']);
|
|
18
18
|
//assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0/96]')), [['<a class="invalid">http://[::ffff:0:0/96]</a>'], '']);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser(' http://
|
|
19
|
+
assert.deepStrictEqual(inspect(parser(' http://host')), undefined);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it('basic', () => {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CodeParser } from '../inline';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
-
import { creation,
|
|
3
|
+
import { creation, match } from '../../combinator';
|
|
4
4
|
import { html } from 'typed-dom/dom';
|
|
5
5
|
|
|
6
|
-
export const code: CodeParser = creation(1, Recursion.ignore,
|
|
6
|
+
export const code: CodeParser = creation(1, Recursion.ignore, match(
|
|
7
7
|
/^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/,
|
|
8
8
|
([whole, , body]) => ({ source }) =>
|
|
9
|
-
[[html('code', { 'data-src': whole }, format(body))], source.slice(whole.length)]))
|
|
9
|
+
[[html('code', { 'data-src': whole }, format(body))], source.slice(whole.length)]));
|
|
10
10
|
|
|
11
11
|
function format(text: string): string {
|
|
12
12
|
assert(text.length > 0);
|
|
@@ -9,7 +9,7 @@ import { html, define, defrag } from 'typed-dom/dom';
|
|
|
9
9
|
|
|
10
10
|
import IndexParser = ExtensionParser.IndexParser;
|
|
11
11
|
|
|
12
|
-
export const index: IndexParser = lazy(() =>
|
|
12
|
+
export const index: IndexParser = lazy(() => constraint(State.index, false, creation(1, Recursion.ignore, fmap(indexee(surround(
|
|
13
13
|
'[#',
|
|
14
14
|
precedence(1, state(State.linkers | State.media,
|
|
15
15
|
startTight(
|
|
@@ -31,7 +31,7 @@ export const index: IndexParser = lazy(() => validate('[#', constraint(State.ind
|
|
|
31
31
|
class: 'index',
|
|
32
32
|
href: el.id ? `#${el.id}` : undefined,
|
|
33
33
|
}),
|
|
34
|
-
]))))
|
|
34
|
+
]))));
|
|
35
35
|
|
|
36
36
|
export const signature: IndexParser.SignatureParser = lazy(() => validate('|', creation(1, Recursion.ignore, fmap(open(
|
|
37
37
|
/^\|(?!\\?\s)/,
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../inline';
|
|
2
2
|
import { State, Recursion } from '../../context';
|
|
3
|
-
import { union, constraint, creation,
|
|
3
|
+
import { union, constraint, creation, surround, clear, fmap } from '../../../combinator';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
7
|
const body = str(/^\$[A-Za-z]*(?:(?:-[A-Za-z][0-9A-Za-z]*)+|-(?:(?:0|[1-9][0-9]*)\.)*(?:0|[1-9][0-9]*)(?![0-9A-Za-z]))/);
|
|
8
8
|
|
|
9
|
-
export const segment: ExtensionParser.LabelParser.SegmentParser = clear(
|
|
9
|
+
export const segment: ExtensionParser.LabelParser.SegmentParser = clear(union([
|
|
10
10
|
surround('[', body, ']'),
|
|
11
11
|
body,
|
|
12
|
-
]))
|
|
12
|
+
]));
|
|
13
13
|
|
|
14
|
-
export const label: ExtensionParser.LabelParser = constraint(State.label, false,
|
|
14
|
+
export const label: ExtensionParser.LabelParser = constraint(State.label, false, creation(1, Recursion.ignore, fmap(
|
|
15
15
|
union([
|
|
16
16
|
surround('[', body, ']'),
|
|
17
17
|
body,
|
|
18
18
|
]),
|
|
19
19
|
([text]) => [
|
|
20
20
|
html('a', { class: 'label', 'data-label': text.slice(text[1] === '-' ? 0 : 1).toLowerCase() }, text),
|
|
21
|
-
])))
|
|
21
|
+
])));
|
|
22
22
|
|
|
23
23
|
export function number(label: string, base: string): string {
|
|
24
24
|
return isFixed(label)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../inline';
|
|
2
2
|
import { Recursion, Backtrack } from '../../context';
|
|
3
|
-
import { union, some, creation, precedence,
|
|
3
|
+
import { union, some, creation, precedence, surround, lazy } from '../../../combinator';
|
|
4
4
|
import { inline } from '../../inline';
|
|
5
5
|
import { str } from '../../source';
|
|
6
6
|
import { startTight } from '../../visibility';
|
|
@@ -11,7 +11,7 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
11
11
|
|
|
12
12
|
// All syntax surrounded by square brackets shouldn't contain line breaks.
|
|
13
13
|
|
|
14
|
-
export const placeholder: ExtensionParser.PlaceholderParser = lazy(() =>
|
|
14
|
+
export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => creation(1, Recursion.inline, surround(
|
|
15
15
|
str(/^\[[:^|]/),
|
|
16
16
|
precedence(1,
|
|
17
17
|
startTight(some(union([inline]), ']', [[']', 1]]))),
|
|
@@ -24,4 +24,4 @@ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => validat
|
|
|
24
24
|
'data-invalid-message': `Invalid start symbol or linebreak`,
|
|
25
25
|
}, defrag(bs)),
|
|
26
26
|
], rest],
|
|
27
|
-
([as, bs], rest) => [unshift(as, bs), rest], 3 | Backtrack.bracket)))
|
|
27
|
+
([as, bs], rest) => [unshift(as, bs), rest], 3 | Backtrack.bracket)));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ExtensionParser } from '../inline';
|
|
2
|
-
import { union
|
|
2
|
+
import { union } from '../../combinator';
|
|
3
3
|
import { index } from './extension/index';
|
|
4
4
|
import { label } from './extension/label';
|
|
5
5
|
import { placeholder } from './extension/placeholder';
|
|
6
6
|
|
|
7
|
-
export const extension: ExtensionParser =
|
|
7
|
+
export const extension: ExtensionParser = union([
|
|
8
8
|
index,
|
|
9
9
|
label,
|
|
10
10
|
placeholder,
|
|
11
|
-
])
|
|
11
|
+
]);
|
|
@@ -18,7 +18,7 @@ const attrspecs = {
|
|
|
18
18
|
Object.setPrototypeOf(attrspecs, null);
|
|
19
19
|
Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
|
|
20
20
|
|
|
21
|
-
export const html: HTMLParser = lazy(() => validate(
|
|
21
|
+
export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, creation(1, Recursion.inline,
|
|
22
22
|
union([
|
|
23
23
|
focus(
|
|
24
24
|
/^<wbr[^\S\n]*>/i,
|
|
@@ -66,7 +66,7 @@ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^
|
|
|
66
66
|
[[elem(tag, as, bs, [])], rest]),
|
|
67
67
|
([, tag]) => tag,
|
|
68
68
|
new Clock(10000))),
|
|
69
|
-
]))))
|
|
69
|
+
]))));
|
|
70
70
|
|
|
71
71
|
export const attribute: HTMLParser.AttributeParser = union([
|
|
72
72
|
str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="[^"\n]*")?(?=[^\S\n]|>)/i),
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { italic } from './italic';
|
|
2
|
+
import { some } from '../../combinator';
|
|
3
|
+
import { inspect } from '../../debug.test';
|
|
4
|
+
|
|
5
|
+
describe('Unit: parser/inline/italic', () => {
|
|
6
|
+
describe('italic', () => {
|
|
7
|
+
const parser = (source: string) => some(italic)({ source, context: {} });
|
|
8
|
+
|
|
9
|
+
it('invalid', () => {
|
|
10
|
+
assert.deepStrictEqual(inspect(parser('///')), undefined);
|
|
11
|
+
assert.deepStrictEqual(inspect(parser('///a')), [['///', 'a'], '']);
|
|
12
|
+
assert.deepStrictEqual(inspect(parser('///a ///')), [['///', 'a'], ' ///']);
|
|
13
|
+
assert.deepStrictEqual(inspect(parser('///a ///')), [['///', 'a', ' '], ' ///']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser('///a\n///')), [['///', 'a'], '\n///']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('///a\\ ///')), [['///', 'a'], '\\ ///']);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser('///a\\\n///')), [['///', 'a'], '\\\n///']);
|
|
17
|
+
assert.deepStrictEqual(inspect(parser('///a/b')), [['///', 'a', '/', 'b'], '']);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser('///a//b')), [['///', 'a', '//', 'b'], '']);
|
|
19
|
+
assert.deepStrictEqual(inspect(parser('///a*b///')), [['///', 'a', '*', 'b', '///'], '']);
|
|
20
|
+
assert.deepStrictEqual(inspect(parser('/// ///')), undefined);
|
|
21
|
+
assert.deepStrictEqual(inspect(parser('/// a///')), undefined);
|
|
22
|
+
assert.deepStrictEqual(inspect(parser('/// a ///')), undefined);
|
|
23
|
+
assert.deepStrictEqual(inspect(parser('///\n///')), undefined);
|
|
24
|
+
assert.deepStrictEqual(inspect(parser('///\na///')), undefined);
|
|
25
|
+
assert.deepStrictEqual(inspect(parser('///\\ a///')), undefined);
|
|
26
|
+
assert.deepStrictEqual(inspect(parser('///\\\na///')), undefined);
|
|
27
|
+
assert.deepStrictEqual(inspect(parser('///<wbr>a///')), undefined);
|
|
28
|
+
assert.deepStrictEqual(inspect(parser('////a////')), undefined);
|
|
29
|
+
assert.deepStrictEqual(inspect(parser('/////a/////')), undefined);
|
|
30
|
+
assert.deepStrictEqual(inspect(parser(' ///a///')), undefined);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('basic', () => {
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('///a///')), [['<i>a</i>'], '']);
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('///ab///')), [['<i>ab</i>'], '']);
|
|
36
|
+
assert.deepStrictEqual(inspect(parser('///a////')), [['<i>a</i>'], '/']);
|
|
37
|
+
assert.deepStrictEqual(inspect(parser('///a\nb///')), [['<i>a<br>b</i>'], '']);
|
|
38
|
+
assert.deepStrictEqual(inspect(parser('///a\\\nb///')), [['<i>a<br>b</i>'], '']);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('nest', () => {
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('///a ///b//////')), [['<i>a <i>b</i></i>'], '']);
|
|
43
|
+
assert.deepStrictEqual(inspect(parser('///a\\ ///b//////')), [['<i>a <i>b</i></i>'], '']);
|
|
44
|
+
assert.deepStrictEqual(inspect(parser('///a	///b//////')), [['<i>a\t<i>b</i></i>'], '']);
|
|
45
|
+
assert.deepStrictEqual(inspect(parser('///a<wbr>///b//////')), [['<i>a<wbr><i>b</i></i>'], '']);
|
|
46
|
+
assert.deepStrictEqual(inspect(parser('///`a`///')), [['<i><code data-src="`a`">a</code></i>'], '']);
|
|
47
|
+
assert.deepStrictEqual(inspect(parser('///(///a///)///')), [['<i><span class="paren">(<i>a</i>)</span></i>'], '']);
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('///{http://host/}///')), [['<i><a class="url" href="http://host/" target="_blank">http://host/</a></i>'], '']);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
});
|