securemark 0.283.2 → 0.283.4
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 +144 -92
- package/markdown.d.ts +16 -8
- package/package.json +1 -1
- package/src/combinator/control/manipulation/indent.ts +1 -1
- package/src/combinator/control/manipulation/surround.ts +4 -4
- package/src/combinator/data/parser/context/delimiter.ts +46 -21
- package/src/combinator/data/parser/context.ts +6 -2
- package/src/parser/block/dlist.ts +1 -1
- package/src/parser/context.ts +10 -11
- package/src/parser/inline/annotation.ts +2 -2
- package/src/parser/inline/autolink/account.ts +14 -13
- package/src/parser/inline/autolink/anchor.ts +13 -12
- package/src/parser/inline/autolink/channel.ts +6 -3
- package/src/parser/inline/autolink/email.ts +4 -3
- package/src/parser/inline/autolink/hashnum.ts +10 -8
- package/src/parser/inline/autolink/hashtag.ts +10 -8
- package/src/parser/inline/autolink/url.ts +25 -19
- package/src/parser/inline/autolink.ts +3 -4
- package/src/parser/inline/bracket.ts +17 -14
- package/src/parser/inline/code.ts +1 -1
- package/src/parser/inline/deletion.ts +1 -1
- package/src/parser/inline/emphasis.test.ts +2 -2
- package/src/parser/inline/emphasis.ts +3 -3
- package/src/parser/inline/emstrong.ts +7 -7
- package/src/parser/inline/extension/index.ts +3 -3
- package/src/parser/inline/extension/indexee.ts +4 -2
- package/src/parser/inline/extension/placeholder.ts +2 -2
- package/src/parser/inline/html.ts +45 -43
- package/src/parser/inline/htmlentity.ts +5 -5
- package/src/parser/inline/insertion.ts +1 -1
- package/src/parser/inline/link.ts +11 -10
- package/src/parser/inline/mark.test.ts +2 -2
- package/src/parser/inline/mark.ts +3 -3
- package/src/parser/inline/math.ts +2 -2
- package/src/parser/inline/media.ts +2 -2
- package/src/parser/inline/reference.ts +2 -2
- package/src/parser/inline/remark.ts +3 -3
- package/src/parser/inline/ruby.ts +5 -4
- package/src/parser/inline/strong.test.ts +2 -2
- package/src/parser/inline/strong.ts +3 -3
- package/src/parser/inline/template.ts +2 -2
- package/src/parser/inline.test.ts +5 -2
- package/src/parser/visibility.ts +4 -4
package/markdown.d.ts
CHANGED
|
@@ -603,7 +603,7 @@ export namespace MarkdownParser {
|
|
|
603
603
|
Block<'reply/cite'>,
|
|
604
604
|
Parser<HTMLSpanElement | HTMLBRElement, Context, [
|
|
605
605
|
SourceParser.StrParser,
|
|
606
|
-
Parser<HTMLAnchorElement, Context, [
|
|
606
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
607
607
|
InlineParser.AutolinkParser.AnchorParser,
|
|
608
608
|
Parser<HTMLAnchorElement, Context, []>,
|
|
609
609
|
Parser<HTMLAnchorElement, Context, []>,
|
|
@@ -1147,8 +1147,9 @@ export namespace MarkdownParser {
|
|
|
1147
1147
|
export interface UrlParser extends
|
|
1148
1148
|
// https://host
|
|
1149
1149
|
Inline<'url'>,
|
|
1150
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1150
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1151
1151
|
LinkParser.UnsafeLinkParser,
|
|
1152
|
+
Parser<string, Context, []>,
|
|
1152
1153
|
]> {
|
|
1153
1154
|
}
|
|
1154
1155
|
export namespace UrlParser {
|
|
@@ -1156,7 +1157,10 @@ export namespace MarkdownParser {
|
|
|
1156
1157
|
Inline<'url/lineurl'>,
|
|
1157
1158
|
Parser<string | HTMLElement, Context, [
|
|
1158
1159
|
SourceParser.StrParser,
|
|
1159
|
-
|
|
1160
|
+
Parser<string | HTMLElement, Context, [
|
|
1161
|
+
InlineParser.LinkParser.UnsafeLinkParser,
|
|
1162
|
+
Parser<string, Context, []>,
|
|
1163
|
+
]>,
|
|
1160
1164
|
]> {
|
|
1161
1165
|
}
|
|
1162
1166
|
export interface BracketParser extends
|
|
@@ -1188,7 +1192,7 @@ export namespace MarkdownParser {
|
|
|
1188
1192
|
export interface ChannelParser extends
|
|
1189
1193
|
// @user#tag
|
|
1190
1194
|
Inline<'channel'>,
|
|
1191
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1195
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1192
1196
|
InlineParser.AutolinkParser.AccountParser,
|
|
1193
1197
|
InlineParser.AutolinkParser.HashtagParser,
|
|
1194
1198
|
]> {
|
|
@@ -1196,29 +1200,33 @@ export namespace MarkdownParser {
|
|
|
1196
1200
|
export interface AccountParser extends
|
|
1197
1201
|
// @user
|
|
1198
1202
|
Inline<'account'>,
|
|
1199
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1203
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1200
1204
|
LinkParser.UnsafeLinkParser,
|
|
1205
|
+
Parser<string, Context, []>,
|
|
1201
1206
|
]> {
|
|
1202
1207
|
}
|
|
1203
1208
|
export interface HashtagParser extends
|
|
1204
1209
|
// #tag
|
|
1205
1210
|
Inline<'hashtag'>,
|
|
1206
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1211
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1207
1212
|
LinkParser.UnsafeLinkParser,
|
|
1213
|
+
Parser<string, Context, []>,
|
|
1208
1214
|
]> {
|
|
1209
1215
|
}
|
|
1210
1216
|
export interface HashnumParser extends
|
|
1211
1217
|
// #1
|
|
1212
1218
|
Inline<'hashnum'>,
|
|
1213
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1219
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1214
1220
|
LinkParser.UnsafeLinkParser,
|
|
1221
|
+
Parser<string, Context, []>,
|
|
1215
1222
|
]> {
|
|
1216
1223
|
}
|
|
1217
1224
|
export interface AnchorParser extends
|
|
1218
1225
|
// >>1
|
|
1219
1226
|
Inline<'anchor'>,
|
|
1220
|
-
Parser<HTMLAnchorElement, Context, [
|
|
1227
|
+
Parser<string | HTMLAnchorElement, Context, [
|
|
1221
1228
|
LinkParser.UnsafeLinkParser,
|
|
1229
|
+
Parser<string, Context, []>,
|
|
1222
1230
|
]> {
|
|
1223
1231
|
}
|
|
1224
1232
|
}
|
package/package.json
CHANGED
|
@@ -32,7 +32,7 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
|
|
|
32
32
|
|
|
33
33
|
function trimBlockEnd(block: string): string {
|
|
34
34
|
return block === ''
|
|
35
|
-
|| block
|
|
35
|
+
|| block.at(-1) !== '\n'
|
|
36
36
|
? block
|
|
37
37
|
: block.slice(0, -1);
|
|
38
38
|
}
|
|
@@ -59,10 +59,10 @@ export function surround<T>(
|
|
|
59
59
|
const { logger = {}, offset = 0 } = context;
|
|
60
60
|
for (let i = 0; i < source.length - mr_.length; ++i) {
|
|
61
61
|
if (source[i] !== source[0]) break;
|
|
62
|
-
const
|
|
63
|
-
if (!(
|
|
62
|
+
const pos = source.length + offset - i - 1;
|
|
63
|
+
if (!(pos in logger)) continue;
|
|
64
64
|
assert(log >>> 2);
|
|
65
|
-
if (logger[
|
|
65
|
+
if (logger[pos] & 1 << (log >>> 2)) return;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
const res2 = mr_ !== '' ? parser({ source: mr_, context }) : undefined;
|
|
@@ -77,7 +77,7 @@ export function surround<T>(
|
|
|
77
77
|
if (rest.length === lmr_.length) return;
|
|
78
78
|
if (log & 2 && rr === undefined) {
|
|
79
79
|
const { logger = {}, offset = 0 } = context;
|
|
80
|
-
logger[source.length + offset] |= 1 << (log >>> 2);
|
|
80
|
+
logger[source.length + offset - 1] |= 1 << (log >>> 2);
|
|
81
81
|
}
|
|
82
82
|
return rr
|
|
83
83
|
? f
|
|
@@ -5,6 +5,7 @@ interface Delimiter {
|
|
|
5
5
|
readonly signature: string;
|
|
6
6
|
readonly matcher: (source: string) => boolean | undefined;
|
|
7
7
|
readonly precedence: number;
|
|
8
|
+
state: boolean;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export class Delimiters {
|
|
@@ -32,7 +33,8 @@ export class Delimiters {
|
|
|
32
33
|
this.signature);
|
|
33
34
|
private readonly registry = memoize<(signature: string) => Delimiter[]>(() => []);
|
|
34
35
|
private readonly delimiters: Delimiter[] = [];
|
|
35
|
-
private readonly
|
|
36
|
+
private readonly stack: number[] = [];
|
|
37
|
+
private readonly states: (readonly number[])[] = [];
|
|
36
38
|
public push(
|
|
37
39
|
delims: readonly {
|
|
38
40
|
readonly signature: string;
|
|
@@ -40,53 +42,76 @@ export class Delimiters {
|
|
|
40
42
|
readonly precedence: number;
|
|
41
43
|
}[]
|
|
42
44
|
): void {
|
|
43
|
-
const { registry, delimiters,
|
|
45
|
+
const { registry, delimiters, stack } = this;
|
|
46
|
+
// シグネチャ数以下
|
|
47
|
+
assert(delimiters.length < 100);
|
|
44
48
|
for (let i = 0; i < delims.length; ++i) {
|
|
45
49
|
const { signature, matcher, precedence } = delims[i];
|
|
46
|
-
const
|
|
47
|
-
const index =
|
|
48
|
-
if (
|
|
50
|
+
const memory = registry(signature);
|
|
51
|
+
const index = memory[0]?.index ?? delimiters.length;
|
|
52
|
+
if (memory.length === 0 || precedence > delimiters[index].precedence) {
|
|
49
53
|
const delimiter: Delimiter = {
|
|
50
54
|
index,
|
|
51
55
|
signature,
|
|
52
56
|
matcher,
|
|
53
57
|
precedence,
|
|
58
|
+
state: true,
|
|
54
59
|
};
|
|
55
60
|
delimiters[index] = delimiter;
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
memory.push(delimiter);
|
|
62
|
+
stack.push(index);
|
|
58
63
|
}
|
|
59
64
|
else {
|
|
60
|
-
|
|
65
|
+
stack.push(-1);
|
|
61
66
|
}
|
|
67
|
+
// 現状各優先順位は固定
|
|
68
|
+
assert(memory.length === 1);
|
|
62
69
|
}
|
|
63
70
|
}
|
|
64
71
|
public pop(count: number): void {
|
|
65
72
|
assert(count > 0);
|
|
66
|
-
const { registry, delimiters,
|
|
73
|
+
const { registry, delimiters, stack } = this;
|
|
67
74
|
for (let i = 0; i < count; ++i) {
|
|
68
|
-
assert(this.
|
|
69
|
-
const index =
|
|
75
|
+
assert(this.stack.length > 0);
|
|
76
|
+
const index = stack.pop()!;
|
|
70
77
|
if (index === -1) continue;
|
|
71
|
-
const
|
|
72
|
-
assert(
|
|
73
|
-
if (
|
|
78
|
+
const memory = registry(delimiters[index].signature);
|
|
79
|
+
assert(memory.length > 0);
|
|
80
|
+
if (memory.length === 1) {
|
|
74
81
|
assert(index === delimiters.length - 1);
|
|
75
|
-
assert(
|
|
76
|
-
|
|
82
|
+
assert(memory[0] === delimiters.at(-1));
|
|
83
|
+
memory.pop();
|
|
77
84
|
delimiters.pop();
|
|
78
85
|
}
|
|
79
86
|
else {
|
|
80
|
-
|
|
81
|
-
delimiters[index] =
|
|
87
|
+
memory.pop();
|
|
88
|
+
delimiters[index] = memory.at(-1)!;
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
}
|
|
85
|
-
public
|
|
92
|
+
public shift(precedence: number): void {
|
|
86
93
|
const { delimiters } = this;
|
|
87
|
-
|
|
94
|
+
const indexes: number[] = [];
|
|
95
|
+
for (let i = delimiters.length; i--;) {
|
|
88
96
|
const delimiter = delimiters[i];
|
|
89
|
-
if (precedence >= delimiter.
|
|
97
|
+
if (delimiter.precedence >= precedence || !delimiter.state) continue;
|
|
98
|
+
delimiter.state = false;
|
|
99
|
+
indexes.push(i)
|
|
100
|
+
}
|
|
101
|
+
this.states.push(indexes);
|
|
102
|
+
}
|
|
103
|
+
public unshift(): void {
|
|
104
|
+
const { delimiters } = this;
|
|
105
|
+
const indexes = this.states.pop()!;
|
|
106
|
+
for (let i = indexes.length; i--;) {
|
|
107
|
+
delimiters[indexes[i]].state = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
public match(source: string, precedence = 0): boolean {
|
|
111
|
+
const { delimiters } = this;
|
|
112
|
+
for (let i = delimiters.length; i--;) {
|
|
113
|
+
const delimiter = delimiters[i];
|
|
114
|
+
if (delimiter.precedence <= precedence || !delimiter.state) continue;
|
|
90
115
|
switch (delimiter.matcher(source)) {
|
|
91
116
|
case true:
|
|
92
117
|
return true;
|
|
@@ -93,11 +93,15 @@ export function creation(cost: number, recursion: number, parser: Parser<unknown
|
|
|
93
93
|
|
|
94
94
|
export function precedence<P extends Parser<unknown>>(precedence: number, parser: P): P;
|
|
95
95
|
export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T> {
|
|
96
|
-
assert(precedence
|
|
96
|
+
assert(precedence >= 0);
|
|
97
97
|
return ({ source, context }) => {
|
|
98
|
-
const p = context
|
|
98
|
+
const { delimiters, precedence: p = 0 } = context;
|
|
99
|
+
const shift = delimiters && precedence > p;
|
|
99
100
|
context.precedence = precedence;
|
|
101
|
+
// デリミタはシフト後に設定しなければならない
|
|
102
|
+
shift && delimiters.shift(precedence);
|
|
100
103
|
const result = parser({ source, context });
|
|
104
|
+
shift && delimiters.unshift();
|
|
101
105
|
context.precedence = p;
|
|
102
106
|
return result;
|
|
103
107
|
};
|
|
@@ -33,7 +33,7 @@ const desc: DListParser.DescriptionParser = block(fmap(open(
|
|
|
33
33
|
false);
|
|
34
34
|
|
|
35
35
|
function fillTrailingDescription(es: HTMLElement[]): HTMLElement[] {
|
|
36
|
-
return es.length > 0 && es
|
|
36
|
+
return es.length > 0 && es.at(-1)!.tagName === 'DT'
|
|
37
37
|
? push(es, [html('dd')])
|
|
38
38
|
: es;
|
|
39
39
|
}
|
package/src/parser/context.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
export const enum State {
|
|
2
|
-
annotation = 1 <<
|
|
3
|
-
reference = 1 <<
|
|
4
|
-
index = 1 <<
|
|
5
|
-
label = 1 <<
|
|
6
|
-
link = 1 <<
|
|
7
|
-
media = 1 <<
|
|
8
|
-
mark = 1 <<
|
|
9
|
-
autolink = 1 <<
|
|
10
|
-
shortcut = 1 << 0,
|
|
2
|
+
annotation = 1 << 7,
|
|
3
|
+
reference = 1 << 6,
|
|
4
|
+
index = 1 << 5,
|
|
5
|
+
label = 1 << 4,
|
|
6
|
+
link = 1 << 3,
|
|
7
|
+
media = 1 << 2,
|
|
8
|
+
mark = 1 << 1,
|
|
9
|
+
autolink = 1 << 0,
|
|
11
10
|
none = 0,
|
|
12
11
|
all = ~0,
|
|
13
12
|
linkers = 0
|
|
@@ -32,10 +31,10 @@ export const enum Recursion {
|
|
|
32
31
|
|
|
33
32
|
export const enum Backtrack {
|
|
34
33
|
template = 7 << 2,
|
|
35
|
-
|
|
34
|
+
index = 6 << 2,
|
|
36
35
|
ruby = 5 << 2,
|
|
37
36
|
link = 4 << 2,
|
|
38
|
-
|
|
37
|
+
media = 3 << 2,
|
|
39
38
|
url = 2 << 2,
|
|
40
39
|
bracket = 1 << 2,
|
|
41
40
|
}
|
|
@@ -8,8 +8,8 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
8
8
|
export const annotation: AnnotationParser = lazy(() => creation(1, Recursion.ignore, surround(
|
|
9
9
|
'((',
|
|
10
10
|
constraint(State.annotation, false,
|
|
11
|
-
syntax(
|
|
12
|
-
trimBlankStart(some(union([inline]), ')', [[/^\\?\n/, 9], [')',
|
|
11
|
+
syntax(1, State.annotation | State.media,
|
|
12
|
+
trimBlankStart(some(union([inline]), ')', [[/^\\?\n/, 9], [')', 1]])))),
|
|
13
13
|
'))',
|
|
14
14
|
false,
|
|
15
15
|
([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNodeEnd(defrag(ns)))])], rest],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { union, tails, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
2
|
+
import { union, tails, syntax, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
3
|
import { unsafelink } from '../link';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { State } from '../../context';
|
|
@@ -7,20 +7,21 @@ 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(() =>
|
|
11
|
-
constraint(State.shortcut, false,
|
|
10
|
+
export const account: AutolinkParser.AccountParser = lazy(() => rewrite(
|
|
12
11
|
open(
|
|
13
12
|
'@',
|
|
14
13
|
tails([
|
|
15
14
|
str(/^[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])?)*\//i),
|
|
16
15
|
str(/^[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*/i),
|
|
17
|
-
]))
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
])),
|
|
17
|
+
union([
|
|
18
|
+
constraint(State.autolink, false, syntax(0, State.autolink, fmap(convert(
|
|
19
|
+
source =>
|
|
20
|
+
`[${source}]{ ${source.includes('/')
|
|
21
|
+
? `https://${source.slice(1).replace('/', '/@')}`
|
|
22
|
+
: `/${source}`
|
|
23
|
+
} }`,
|
|
24
|
+
unsafelink),
|
|
25
|
+
([el]) => [define(el, { class: 'account' })]))),
|
|
26
|
+
({ source }) => [[source], ''],
|
|
27
|
+
])));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { union, constraint, validate, focus, convert, fmap, lazy } from '../../../combinator';
|
|
2
|
+
import { union, syntax, constraint, validate, focus, convert, fmap, lazy } from '../../../combinator';
|
|
3
3
|
import { unsafelink } from '../link';
|
|
4
4
|
import { State } from '../../context';
|
|
5
5
|
import { define } from 'typed-dom/dom';
|
|
@@ -14,16 +14,17 @@ import { define } from 'typed-dom/dom';
|
|
|
14
14
|
// 内部表現はUnixTimeに統一する(時系列順)
|
|
15
15
|
// 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
|
|
16
16
|
|
|
17
|
-
export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>',
|
|
18
|
-
constraint(State.shortcut, false,
|
|
17
|
+
export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>',
|
|
19
18
|
focus(
|
|
20
19
|
/^>>(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?![0-9a-z@#:])/i,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
union([
|
|
21
|
+
constraint(State.autolink, false, syntax(0, State.autolink, fmap(convert(
|
|
22
|
+
source =>
|
|
23
|
+
`[${source}]{ ${source.includes('/')
|
|
24
|
+
? `/@${source.slice(2).replace('/', '/timeline?at=')}`
|
|
25
|
+
: `?at=${source.slice(2)}`
|
|
26
|
+
} }`,
|
|
27
|
+
unsafelink),
|
|
28
|
+
([el]) => [define(el, { class: 'anchor' })]))),
|
|
29
|
+
({ source }) => [[source], ''],
|
|
30
|
+
]))));
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { sequence, some, validate, bind } from '../../../combinator';
|
|
2
|
+
import { sequence, some, constraint, validate, bind } from '../../../combinator';
|
|
3
3
|
import { account } from './account';
|
|
4
4
|
import { hashtag } from './hashtag';
|
|
5
|
+
import { State } from '../../context';
|
|
5
6
|
import { stringify } from '../../util';
|
|
6
7
|
import { define } from 'typed-dom/dom';
|
|
7
8
|
|
|
8
9
|
// https://example/@user?ch=a+b must be a user channel page or a redirect page going there.
|
|
9
10
|
|
|
10
|
-
export const channel: AutolinkParser.ChannelParser = validate('@',
|
|
11
|
+
export const channel: AutolinkParser.ChannelParser = validate('@',
|
|
12
|
+
constraint(State.autolink, false, bind(
|
|
11
13
|
sequence([
|
|
12
14
|
account,
|
|
13
15
|
some(hashtag),
|
|
@@ -15,6 +17,7 @@ export const channel: AutolinkParser.ChannelParser = validate('@', bind(
|
|
|
15
17
|
(es, rest) => {
|
|
16
18
|
const source = stringify(es);
|
|
17
19
|
const el = es[0];
|
|
20
|
+
if (typeof el === 'string') return [es, rest];
|
|
18
21
|
const url = `${el.getAttribute('href')}?ch=${source.slice(source.indexOf('#') + 1).replace(/#/g, '+')}`;
|
|
19
22
|
return [[define(el, { class: 'channel', href: url }, source)], rest];
|
|
20
|
-
}));
|
|
23
|
+
})));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { creation, verify, rewrite } from '../../../combinator';
|
|
2
|
+
import { syntax, creation, constraint, verify, rewrite } from '../../../combinator';
|
|
3
3
|
import { str } from '../../source';
|
|
4
|
-
import { Recursion } from '../../context';
|
|
4
|
+
import { State, Recursion } from '../../context';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
7
|
// https://html.spec.whatwg.org/multipage/input.html
|
|
@@ -9,4 +9,5 @@ import { html } from 'typed-dom/dom';
|
|
|
9
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
|
-
(
|
|
12
|
+
constraint(State.autolink, false, syntax(0, State.autolink,
|
|
13
|
+
({ source }) => [[html('a', { class: 'email', href: `mailto:${source}` }, source)], '']))));
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { union, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
2
|
+
import { union, syntax, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
3
|
import { unsafelink } from '../link';
|
|
4
4
|
import { emoji } from './hashtag';
|
|
5
5
|
import { str } from '../../source';
|
|
6
6
|
import { State } from '../../context';
|
|
7
7
|
import { define } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
|
-
export const hashnum: AutolinkParser.HashnumParser = lazy(() =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
export const hashnum: AutolinkParser.HashnumParser = lazy(() => rewrite(
|
|
10
|
+
open('#', str(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, emoji), 'u'))),
|
|
11
|
+
union([
|
|
12
|
+
constraint(State.autolink, false, syntax(0, State.autolink, fmap(convert(
|
|
13
|
+
source => `[${source}]{ ${source.slice(1)} }`,
|
|
14
|
+
unsafelink),
|
|
15
|
+
([el]) => [define(el, { class: 'hashnum', href: null })]))),
|
|
16
|
+
({ source }) => [[source], ''],
|
|
17
|
+
])));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { union, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
2
|
+
import { union, syntax, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
3
|
import { unsafelink } from '../link';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { State } from '../../context';
|
|
@@ -10,15 +10,17 @@ 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(() =>
|
|
14
|
-
constraint(State.shortcut, false,
|
|
13
|
+
export const hashtag: AutolinkParser.HashtagParser = lazy(() => rewrite(
|
|
15
14
|
open(
|
|
16
15
|
'#',
|
|
17
16
|
str(new RegExp([
|
|
18
17
|
/^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|')))/u.source,
|
|
19
18
|
/(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source,
|
|
20
|
-
].join('').replace(/emoji/g, emoji), 'u')))
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
].join('').replace(/emoji/g, emoji), 'u'))),
|
|
20
|
+
union([
|
|
21
|
+
constraint(State.autolink, false, syntax(0, State.autolink, fmap(convert(
|
|
22
|
+
source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`,
|
|
23
|
+
unsafelink),
|
|
24
|
+
([el]) => [define(el, { class: 'hashtag' })]))),
|
|
25
|
+
({ source }) => [[source], ''],
|
|
26
|
+
])));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { union, tails, some, creation, precedence, validate, focus, rewrite, convert, surround, open, lazy } from '../../../combinator';
|
|
2
|
+
import { union, tails, some, syntax, creation, precedence, constraint, validate, focus, rewrite, convert, surround, open, lazy } from '../../../combinator';
|
|
3
3
|
import { unsafelink } from '../link';
|
|
4
4
|
import { linebreak, unescsource, str } from '../../source';
|
|
5
|
-
import {
|
|
5
|
+
import { State, Recursion, Backtrack } from '../../context';
|
|
6
6
|
|
|
7
7
|
const closer = /^[-+*=~^_,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
|
|
8
8
|
|
|
@@ -10,24 +10,30 @@ export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'ht
|
|
|
10
10
|
open(
|
|
11
11
|
/^https?:\/\/(?=[\x21-\x7E])/,
|
|
12
12
|
focus(/^[\x21-\x7E]+/, some(union([bracket, some(unescsource, closer)])))),
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
union([
|
|
14
|
+
constraint(State.autolink, false, syntax(0, State.autolink, convert(
|
|
15
|
+
url => `{ ${url} }`,
|
|
16
|
+
unsafelink))),
|
|
17
|
+
({ source }) => [[source], ''],
|
|
18
|
+
]))));
|
|
16
19
|
|
|
17
20
|
export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => open(
|
|
18
21
|
linebreak,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
focus(
|
|
23
|
+
/^!?https?:\/\/\S+(?=[^\S\n]*(?:$|\n))/,
|
|
24
|
+
tails([
|
|
25
|
+
str('!'),
|
|
26
|
+
union([
|
|
27
|
+
constraint(State.autolink, false, syntax(0, State.autolink, convert(
|
|
28
|
+
url => `{ ${url} }`,
|
|
29
|
+
unsafelink))),
|
|
30
|
+
({ source }) => [[source], ''],
|
|
31
|
+
]),
|
|
32
|
+
]))));
|
|
27
33
|
|
|
28
|
-
const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creation(0, Recursion.terminal,
|
|
29
|
-
surround(str('('), some(union([bracket, unescsource]), ')'), str(')'), true, undefined, undefined, 3 | Backtrack.url),
|
|
30
|
-
surround(str('['), some(union([bracket, unescsource]), ']'), str(']'), true, undefined, undefined, 3 | Backtrack.url),
|
|
31
|
-
surround(str('{'), some(union([bracket, unescsource]), '}'), str('}'), true, undefined, undefined, 3 | Backtrack.url),
|
|
32
|
-
surround(str('"'), precedence(
|
|
33
|
-
])))
|
|
34
|
+
const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creation(0, Recursion.terminal, union([
|
|
35
|
+
surround(str('('), precedence(1, some(union([bracket, unescsource]), ')')), str(')'), true, undefined, undefined, 3 | Backtrack.url),
|
|
36
|
+
surround(str('['), precedence(1, some(union([bracket, unescsource]), ']')), str(']'), true, undefined, undefined, 3 | Backtrack.url),
|
|
37
|
+
surround(str('{'), precedence(1, some(union([bracket, unescsource]), '}')), str('}'), true, undefined, undefined, 3 | Backtrack.url),
|
|
38
|
+
surround(str('"'), precedence(2, some(unescsource, '"')), str('"'), true, undefined, undefined, 3 | Backtrack.url),
|
|
39
|
+
])));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutolinkParser } from '../inline';
|
|
2
|
-
import { union, some, syntax,
|
|
2
|
+
import { union, some, syntax, validate, lazy, fmap } from '../../combinator';
|
|
3
3
|
import { url, lineurl } from './autolink/url';
|
|
4
4
|
import { email } from './autolink/email';
|
|
5
5
|
import { channel } from './autolink/channel';
|
|
@@ -13,8 +13,7 @@ import { stringify } from '../util';
|
|
|
13
13
|
|
|
14
14
|
export const autolink: AutolinkParser = lazy(() =>
|
|
15
15
|
validate(/^(?:[@#>0-9a-z]|\S[#>]|[\r\n]!?https?:\/\/)/iu,
|
|
16
|
-
|
|
17
|
-
syntax(1, ~State.shortcut,
|
|
16
|
+
syntax(0, ~State.autolink,
|
|
18
17
|
union([
|
|
19
18
|
some(union([lineurl])),
|
|
20
19
|
fmap(some(union([
|
|
@@ -37,4 +36,4 @@ export const autolink: AutolinkParser = lazy(() =>
|
|
|
37
36
|
anchor,
|
|
38
37
|
])),
|
|
39
38
|
ns => ns.length === 1 ? ns : [stringify(ns)]),
|
|
40
|
-
]))))
|
|
39
|
+
]))));
|
|
@@ -6,42 +6,45 @@ import { State, Recursion, Backtrack } from '../context';
|
|
|
6
6
|
import { unshift, push } from 'spica/array';
|
|
7
7
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const indexA = /^[0-9A-Za-z]+(?:(?:[.-]|, )[0-9A-Za-z]+)*/;
|
|
10
|
+
const indexF = new RegExp(indexA.source.replace(', ', '[,、]')
|
|
11
|
+
.replace(/[09AZaz.]|\-(?!\w)/g, c => String.fromCodePoint(c.codePointAt(0)! + 0xFEE0)));
|
|
10
12
|
|
|
11
13
|
export const bracket: BracketParser = lazy(() => union([
|
|
12
|
-
surround(str('('), creation(0, Recursion.bracket, syntax(
|
|
13
|
-
surround(str('('), creation(0, Recursion.bracket, syntax(
|
|
14
|
+
surround(str('('), creation(0, Recursion.bracket, syntax(1, State.none, str(indexA))), str(')')),
|
|
15
|
+
surround(str('('), creation(0, Recursion.bracket, syntax(1, State.none, some(inline, ')', [[')', 1]]))), str(')'), true,
|
|
14
16
|
([as, bs = [], cs], rest) => [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))], rest],
|
|
15
17
|
([as, bs = []], rest) => [unshift(as, bs), rest], 3 | Backtrack.bracket),
|
|
16
|
-
surround(str('('), creation(0, Recursion.bracket, syntax(
|
|
17
|
-
surround(str('('), creation(0, Recursion.bracket, syntax(
|
|
18
|
+
surround(str('('), creation(0, Recursion.bracket, syntax(1, State.none, str(indexF))), str(')')),
|
|
19
|
+
surround(str('('), creation(0, Recursion.bracket, syntax(1, State.none, some(inline, ')', [[')', 1]]))), str(')'), true,
|
|
18
20
|
([as, bs = [], cs], rest) => [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))], rest],
|
|
19
21
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
20
|
-
surround(str('['), creation(0, Recursion.bracket, syntax(
|
|
22
|
+
surround(str('['), creation(0, Recursion.bracket, syntax(1, State.none, some(inline, ']', [[']', 1]]))), str(']'), true,
|
|
21
23
|
undefined,
|
|
22
24
|
([as, bs = []], rest) => [unshift(as, bs), rest], 3 | Backtrack.bracket),
|
|
23
|
-
surround(str('['), creation(0, Recursion.bracket, syntax(
|
|
25
|
+
surround(str('['), creation(0, Recursion.bracket, syntax(1, State.none, some(inline, ']', [[']', 1]]))), str(']'), true,
|
|
24
26
|
undefined,
|
|
25
27
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
26
|
-
surround(str('{'), creation(0, Recursion.bracket, syntax(
|
|
28
|
+
surround(str('{'), creation(0, Recursion.bracket, syntax(1, State.none, some(inline, '}', [['}', 1]]))), str('}'), true,
|
|
27
29
|
undefined,
|
|
28
30
|
([as, bs = []], rest) => [unshift(as, bs), rest], 3 | Backtrack.bracket),
|
|
29
|
-
surround(str('{'), creation(0, Recursion.bracket, syntax(
|
|
31
|
+
surround(str('{'), creation(0, Recursion.bracket, syntax(1, State.none, some(inline, '}', [['}', 1]]))), str('}'), true,
|
|
30
32
|
undefined,
|
|
31
33
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
32
|
-
|
|
34
|
+
// 改行禁止はバックトラックなしでは内側の構文を破壊するため安易に行えない。
|
|
35
|
+
surround(str('"'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '"', [[/^\\?\n/, 9], ['"', 2]]))), str('"'), true,
|
|
33
36
|
undefined,
|
|
34
37
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
35
|
-
surround(str('“'), creation(0, Recursion.bracket, syntax(
|
|
38
|
+
surround(str('“'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '”', [[/^\\?\n/, 9], ['”', 2]]))), str('”'), true,
|
|
36
39
|
undefined,
|
|
37
40
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
38
|
-
surround(str('‘'), creation(0, Recursion.bracket, syntax(
|
|
41
|
+
surround(str('‘'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '’', [[/^\\?\n/, 9], ['’', 2]]))), str('’'), true,
|
|
39
42
|
undefined,
|
|
40
43
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
41
|
-
surround(str('「'), creation(0, Recursion.bracket, syntax(
|
|
44
|
+
surround(str('「'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '」', [[/^\\?\n/, 9], ['」', 2]]))), str('」'), true,
|
|
42
45
|
undefined,
|
|
43
46
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
44
|
-
surround(str('『'), creation(0, Recursion.bracket, syntax(
|
|
47
|
+
surround(str('『'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '』', [[/^\\?\n/, 9], ['』', 2]]))), str('』'), true,
|
|
45
48
|
undefined,
|
|
46
49
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
47
50
|
]));
|