securemark 0.286.1 → 0.286.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/design.md +59 -16
- package/dist/index.js +50 -45
- package/markdown.d.ts +1 -1
- package/package.json +1 -1
- package/src/combinator/control/manipulation/surround.ts +0 -1
- package/src/parser/api/parse.test.ts +8 -6
- package/src/parser/context.ts +10 -8
- package/src/parser/inline/autolink/hashnum.test.ts +2 -1
- package/src/parser/inline/autolink/hashnum.ts +1 -1
- package/src/parser/inline/autolink/hashtag.test.ts +10 -11
- package/src/parser/inline/autolink/hashtag.ts +2 -2
- package/src/parser/inline/autolink/url.ts +6 -6
- package/src/parser/inline/autolink.ts +5 -5
- package/src/parser/inline/deletion.ts +4 -3
- package/src/parser/inline/emphasis.ts +4 -4
- package/src/parser/inline/emstrong.ts +4 -3
- package/src/parser/inline/extension/index.ts +6 -6
- package/src/parser/inline/extension/placeholder.ts +4 -4
- package/src/parser/inline/html.ts +6 -6
- package/src/parser/inline/insertion.ts +4 -3
- package/src/parser/inline/italic.ts +5 -3
- package/src/parser/inline/link.test.ts +1 -0
- package/src/parser/inline/mark.ts +5 -4
- package/src/parser/inline/math.ts +4 -3
- package/src/parser/inline/reference.ts +6 -1
- package/src/parser/inline/remark.ts +4 -3
- package/src/parser/inline/ruby.test.ts +2 -1
- package/src/parser/inline/ruby.ts +18 -10
- package/src/parser/inline/strong.ts +4 -4
- package/src/parser/inline/template.ts +6 -6
- package/src/parser/inline.test.ts +9 -8
- package/src/parser/source/str.ts +4 -1
- package/src/parser/source/text.ts +2 -2
|
@@ -66,7 +66,6 @@ export function surround<T>(
|
|
|
66
66
|
if (source[i] !== source[0]) break;
|
|
67
67
|
const pos = source.length + offset - i - 1;
|
|
68
68
|
if (!(pos in backtracks)) continue;
|
|
69
|
-
assert(backtrack >>> 2);
|
|
70
69
|
// bracket only
|
|
71
70
|
const shift = backtrack >>> 2 === state >>> 2 ? state & 3 : 0;
|
|
72
71
|
if (backtracks[pos] & 1 << (backtrack >>> 2) + shift) return;
|
|
@@ -332,14 +332,14 @@ describe('Unit: parser/api/parse', () => {
|
|
|
332
332
|
if (!navigator.userAgent.includes('Chrome')) return;
|
|
333
333
|
|
|
334
334
|
it('creation', function () {
|
|
335
|
-
this.timeout(
|
|
335
|
+
this.timeout(10000);
|
|
336
336
|
assert.deepStrictEqual(
|
|
337
337
|
[...parse('.'.repeat(100000)).children].map(el => el.outerHTML),
|
|
338
338
|
[`<p>${'.'.repeat(100000)}</p>`]);
|
|
339
339
|
});
|
|
340
340
|
|
|
341
341
|
it.skip('creation error', function () {
|
|
342
|
-
this.timeout(
|
|
342
|
+
this.timeout(10000);
|
|
343
343
|
assert.deepStrictEqual(
|
|
344
344
|
[...parse('.'.repeat(100001)).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
345
345
|
[
|
|
@@ -350,18 +350,20 @@ describe('Unit: parser/api/parse', () => {
|
|
|
350
350
|
|
|
351
351
|
it('backtrack', function () {
|
|
352
352
|
this.timeout(5000);
|
|
353
|
+
const str = `${'.'.repeat(9)}((${'['.repeat(13)}{{http://[[[${'.'.repeat(7134)}`;
|
|
353
354
|
assert.deepStrictEqual(
|
|
354
|
-
[...parse(
|
|
355
|
-
[`<p>${
|
|
355
|
+
[...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
356
|
+
[`<p>${str}</p>`]);
|
|
356
357
|
});
|
|
357
358
|
|
|
358
359
|
it('backtrack error', function () {
|
|
359
360
|
this.timeout(5000);
|
|
361
|
+
const str = `${'.'.repeat(9)}((${'['.repeat(13)}{{http://[[[${'.'.repeat(7134 + 1)}`;
|
|
360
362
|
assert.deepStrictEqual(
|
|
361
|
-
[...parse(
|
|
363
|
+
[...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
362
364
|
[
|
|
363
365
|
'<h1 id="error:rnd" class="error">Error: Too many creations</h1>',
|
|
364
|
-
`<pre class="error" translate="no">${
|
|
366
|
+
`<pre class="error" translate="no">${str.slice(0, 1000 - 3)}...</pre>`,
|
|
365
367
|
]);
|
|
366
368
|
});
|
|
367
369
|
|
package/src/parser/context.ts
CHANGED
|
@@ -29,15 +29,17 @@ export const enum Recursion {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export const enum Backtrack {
|
|
32
|
-
template =
|
|
33
|
-
index =
|
|
34
|
-
link =
|
|
35
|
-
ruby =
|
|
36
|
-
media =
|
|
37
|
-
linebracket =
|
|
38
|
-
bracket =
|
|
39
|
-
url =
|
|
32
|
+
template = 7 << 2,
|
|
33
|
+
index = 6 << 2,
|
|
34
|
+
link = 5 << 2,
|
|
35
|
+
ruby = 4 << 2,
|
|
36
|
+
media = 3 << 2,
|
|
37
|
+
linebracket = 2 << 2,
|
|
38
|
+
bracket = 1 << 2,
|
|
39
|
+
url = 0 << 2,
|
|
40
40
|
}
|
|
41
|
+
// バックトラックを削減するため括弧派生構文を改行禁止し
|
|
42
|
+
// 括弧派生構文内のバックトラック状態を統一する。
|
|
41
43
|
export const enum BacktrackState {
|
|
42
44
|
nobreak = 1,
|
|
43
45
|
}
|
|
@@ -10,7 +10,6 @@ describe('Unit: parser/inline/autolink/hashnum', () => {
|
|
|
10
10
|
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
11
11
|
assert.deepStrictEqual(inspect(parser('#')), [['#'], '']);
|
|
12
12
|
assert.deepStrictEqual(inspect(parser('# ')), [['#'], ' ']);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser('#1_')), [['#1_'], '']);
|
|
14
13
|
assert.deepStrictEqual(inspect(parser('#1#')), [['#1#'], '']);
|
|
15
14
|
assert.deepStrictEqual(inspect(parser('#1#2')), [['#1#2'], '']);
|
|
16
15
|
assert.deepStrictEqual(inspect(parser('#1#2#3')), [['#1#2#3'], '']);
|
|
@@ -44,6 +43,8 @@ describe('Unit: parser/inline/autolink/hashnum', () => {
|
|
|
44
43
|
assert.deepStrictEqual(inspect(parser('#1\\')), [['<a class="hashnum">#1</a>'], '\\']);
|
|
45
44
|
assert.deepStrictEqual(inspect(parser('#1\\ ')), [['<a class="hashnum">#1</a>'], '\\ ']);
|
|
46
45
|
assert.deepStrictEqual(inspect(parser('#1\\\n')), [['<a class="hashnum">#1</a>'], '\\\n']);
|
|
46
|
+
assert.deepStrictEqual(inspect(parser(`#1'`)), [[`<a class="hashnum">#1</a>`], `'`]);
|
|
47
|
+
assert.deepStrictEqual(inspect(parser(`#1''`)), [[`<a class="hashnum">#1</a>`], `''`]);
|
|
47
48
|
assert.deepStrictEqual(inspect(parser('#123456789')), [['<a class="hashnum">#123456789</a>'], '']);
|
|
48
49
|
});
|
|
49
50
|
|
|
@@ -7,7 +7,7 @@ import { str } from '../../source';
|
|
|
7
7
|
import { define } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
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
|
|
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
12
|
constraint(State.autolink, false, state(State.autolink, fmap(convert(
|
|
13
13
|
source => `[${source}]{ ${source.slice(1)} }`,
|
|
@@ -20,11 +20,11 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
|
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('##')), [['##'], '']);
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('##a')), [['##a'], '']);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('###a')), [['###a'], '']);
|
|
23
|
-
assert.deepStrictEqual(inspect(parser(`#'`)), [[
|
|
24
|
-
assert.deepStrictEqual(inspect(parser(`#'0`)), [[
|
|
25
|
-
assert.deepStrictEqual(inspect(parser(`#'00`)), [[
|
|
26
|
-
assert.deepStrictEqual(inspect(parser('#_')), [['#
|
|
27
|
-
assert.deepStrictEqual(inspect(parser('#_a')), [['#
|
|
23
|
+
assert.deepStrictEqual(inspect(parser(`#'`)), [['#'], `'`]);
|
|
24
|
+
assert.deepStrictEqual(inspect(parser(`#'0`)), [[`#`], `'0`]);
|
|
25
|
+
assert.deepStrictEqual(inspect(parser(`#'00`)), [[`#`], `'00`]);
|
|
26
|
+
assert.deepStrictEqual(inspect(parser('#_')), [['#'], '_']);
|
|
27
|
+
assert.deepStrictEqual(inspect(parser('#_a')), [['#'], '_a']);
|
|
28
28
|
assert.deepStrictEqual(inspect(parser('#(a)')), [['#'], '(a)']);
|
|
29
29
|
assert.deepStrictEqual(inspect(parser('#{}')), [['#'], '{}']);
|
|
30
30
|
assert.deepStrictEqual(inspect(parser('#{{}')), [['#'], '{{}']);
|
|
@@ -58,13 +58,12 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
|
|
|
58
58
|
assert.deepStrictEqual(inspect(parser('#1a')), [['<a class="hashtag" href="/hashtags/1a">#1a</a>'], '']);
|
|
59
59
|
assert.deepStrictEqual(inspect(parser('#1あ')), [['<a class="hashtag" href="/hashtags/1あ">#1あ</a>'], '']);
|
|
60
60
|
assert.deepStrictEqual(inspect(parser('#1👩')), [['<a class="hashtag" href="/hashtags/1👩">#1👩</a>'], '']);
|
|
61
|
-
assert.deepStrictEqual(inspect(parser(`#
|
|
62
|
-
assert.deepStrictEqual(inspect(parser(`#
|
|
63
|
-
assert.deepStrictEqual(inspect(parser(`#a'`)), [[`<a class="hashtag" href="/hashtags/a'">#a'</a>`], '']);
|
|
64
|
-
assert.deepStrictEqual(inspect(parser(`#a''`)), [[`<a class="hashtag" href="/hashtags/a''">#a''</a>`], '']);
|
|
61
|
+
assert.deepStrictEqual(inspect(parser(`#a'`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `'`]);
|
|
62
|
+
assert.deepStrictEqual(inspect(parser(`#a''`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `''`]);
|
|
65
63
|
assert.deepStrictEqual(inspect(parser(`#a'b`)), [[`<a class="hashtag" href="/hashtags/a'b">#a'b</a>`], '']);
|
|
66
|
-
assert.deepStrictEqual(inspect(parser(`#a'
|
|
67
|
-
assert.deepStrictEqual(inspect(parser(`#
|
|
64
|
+
assert.deepStrictEqual(inspect(parser(`#a'0`)), [[`<a class="hashtag" href="/hashtags/a'0">#a'0</a>`], '']);
|
|
65
|
+
assert.deepStrictEqual(inspect(parser(`#a'_b`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `'_b`]);
|
|
66
|
+
assert.deepStrictEqual(inspect(parser(`#a_'b`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `_'b`]);
|
|
68
67
|
assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(9)}a`)), [[`<a class="hashtag" href="/hashtags/${'1'.repeat(9)}a">#${'1'.repeat(9)}a</a>`], '']);
|
|
69
68
|
});
|
|
70
69
|
|
|
@@ -14,8 +14,8 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => rewrite(
|
|
|
14
14
|
open(
|
|
15
15
|
'#',
|
|
16
16
|
str(new RegExp([
|
|
17
|
-
/^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[
|
|
18
|
-
/(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[
|
|
17
|
+
/^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^'\p{C}\p{S}\p{P}\s]|emoji)))/u.source,
|
|
18
|
+
/(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^'\p{C}\p{S}\p{P}\s]|emoji))+/u.source,
|
|
19
19
|
].join('').replace(/emoji/g, emoji), 'u'))),
|
|
20
20
|
union([
|
|
21
21
|
constraint(State.autolink, false, state(State.autolink, fmap(convert(
|
|
@@ -34,13 +34,13 @@ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => open(
|
|
|
34
34
|
]),
|
|
35
35
|
]))));
|
|
36
36
|
|
|
37
|
-
const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() =>
|
|
38
|
-
surround(str('('), some(union([bracket, unescsource]), ')'), str(')'), true,
|
|
37
|
+
const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => union([
|
|
38
|
+
surround(str('('), recursion(Recursion.terminal, some(union([bracket, unescsource]), ')')), str(')'), true,
|
|
39
39
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
|
|
40
|
-
surround(str('['), some(union([bracket, unescsource]), ']'), str(']'), true,
|
|
40
|
+
surround(str('['), recursion(Recursion.terminal, some(union([bracket, unescsource]), ']')), str(']'), true,
|
|
41
41
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
|
|
42
|
-
surround(str('{'), some(union([bracket, unescsource]), '}'), str('}'), true,
|
|
42
|
+
surround(str('{'), recursion(Recursion.terminal, some(union([bracket, unescsource]), '}')), str('}'), true,
|
|
43
43
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
|
|
44
|
-
surround(str('"'), precedence(2, some(unescsource, '"')), str('"'), true,
|
|
44
|
+
surround(str('"'), precedence(2, recursion(Recursion.terminal, some(unescsource, '"'))), str('"'), true,
|
|
45
45
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
|
|
46
|
-
]))
|
|
46
|
+
]));
|
|
@@ -12,7 +12,7 @@ import { str } from '../source';
|
|
|
12
12
|
import { stringify } from '../util';
|
|
13
13
|
|
|
14
14
|
export const autolink: AutolinkParser = lazy(() =>
|
|
15
|
-
validate(/^(?:[@#>0-9a-z]|\S[
|
|
15
|
+
validate(/^(?:[@#>0-9a-z]|\S#|[0-9a-z]>|[\r\n]!?https?:\/\/)/iu,
|
|
16
16
|
state(~State.autolink,
|
|
17
17
|
union([
|
|
18
18
|
some(union([lineurl])),
|
|
@@ -20,17 +20,17 @@ export const autolink: AutolinkParser = lazy(() =>
|
|
|
20
20
|
url,
|
|
21
21
|
email,
|
|
22
22
|
// Escape unmatched email-like strings.
|
|
23
|
-
str(/^[0-9a-z]+(?:[_.+-][0-9a-z]
|
|
23
|
+
str(/^[0-9a-z]+(?:[_.+-][0-9a-z]+|@(?=@))*/i),
|
|
24
24
|
channel,
|
|
25
25
|
account,
|
|
26
26
|
// Escape unmatched account-like strings.
|
|
27
|
-
str(/^@+[0-9a-z]
|
|
27
|
+
str(/^@+(?:[0-9a-z]+(?:[_.+-][0-9a-z]+)*)?/i),
|
|
28
28
|
// Escape invalid leading characters.
|
|
29
|
-
str(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji
|
|
29
|
+
str(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji)(?=#)/u.source.replace('emoji', emoji), 'u')),
|
|
30
30
|
hashtag,
|
|
31
31
|
hashnum,
|
|
32
32
|
// Escape unmatched hashtag-like strings.
|
|
33
|
-
str(new RegExp(/^#+(?:[^\p{C}\p{S}\p{P}\s]|emoji
|
|
33
|
+
str(new RegExp(/^#+(?:(?:[^\p{C}\p{S}\p{P}\s]|emoji)+(?:['_.+-](?:[^\p{C}\p{S}\p{P}\s]|emoji)+)*)?/u.source.replace(/emoji/g, emoji), 'u')),
|
|
34
34
|
// Escape invalid leading characters.
|
|
35
35
|
str(/^[0-9a-z](?=>)/iu),
|
|
36
36
|
anchor,
|
|
@@ -7,14 +7,15 @@ import { repeat } from '../util';
|
|
|
7
7
|
import { push } from 'spica/array';
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
|
-
export const deletion: DeletionParser = lazy(() =>
|
|
10
|
+
export const deletion: DeletionParser = lazy(() => validate('~~',
|
|
11
11
|
precedence(0, repeat('~~', surround(
|
|
12
12
|
'',
|
|
13
|
+
recursion(Recursion.inline,
|
|
13
14
|
some(union([
|
|
14
15
|
some(inline, blankWith('\n', '~~')),
|
|
15
16
|
open('\n', some(deletion, '~'), true),
|
|
16
|
-
])),
|
|
17
|
+
]))),
|
|
17
18
|
'~~', false,
|
|
18
19
|
([, bs], rest) => [bs, rest],
|
|
19
20
|
([, bs], rest) => [push(bs, [Command.Escape]), rest]),
|
|
20
|
-
nodes => [html('del', defrag(nodes))]))))
|
|
21
|
+
nodes => [html('del', defrag(nodes))]))));
|
|
@@ -9,9 +9,9 @@ import { tightStart, blankWith } from '../visibility';
|
|
|
9
9
|
import { unshift } from 'spica/array';
|
|
10
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
11
11
|
|
|
12
|
-
export const emphasis: EmphasisParser = lazy(() =>
|
|
12
|
+
export const emphasis: EmphasisParser = lazy(() => surround(
|
|
13
13
|
str('*', '*'),
|
|
14
|
-
precedence(0,
|
|
14
|
+
precedence(0, recursion(Recursion.inline,
|
|
15
15
|
tightStart(some(union([
|
|
16
16
|
strong,
|
|
17
17
|
some(inline, blankWith('*')),
|
|
@@ -20,7 +20,7 @@ export const emphasis: EmphasisParser = lazy(() => recursion(Recursion.inline, s
|
|
|
20
20
|
strong,
|
|
21
21
|
emphasis,
|
|
22
22
|
])),
|
|
23
|
-
])))),
|
|
23
|
+
]))))),
|
|
24
24
|
str('*'), false,
|
|
25
25
|
([, bs], rest) => [[html('em', defrag(bs))], rest],
|
|
26
|
-
([as, bs], rest) => [unshift(as, bs), rest]))
|
|
26
|
+
([as, bs], rest) => [unshift(as, bs), rest]));
|
|
@@ -30,13 +30,14 @@ const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
|
|
|
30
30
|
|
|
31
31
|
// 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず早く閉じるよう解析しなければならない。
|
|
32
32
|
// このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
|
|
33
|
-
export const emstrong: EmStrongParser = lazy(() =>
|
|
33
|
+
export const emstrong: EmStrongParser = lazy(() => validate('***',
|
|
34
34
|
precedence(0, repeat('***', surround(
|
|
35
35
|
'',
|
|
36
|
+
recursion(Recursion.inline,
|
|
36
37
|
tightStart(some(union([
|
|
37
38
|
some(inline, blankWith('*')),
|
|
38
39
|
open(some(inline, '*'), inline),
|
|
39
|
-
]))),
|
|
40
|
+
])))),
|
|
40
41
|
str(/^\*{1,3}/), false,
|
|
41
42
|
([, bs, cs], rest, context): Result<HTMLElement | string, typeof context> => {
|
|
42
43
|
assert(cs.length === 1);
|
|
@@ -93,4 +94,4 @@ export const emstrong: EmStrongParser = lazy(() => recursion(Recursion.inline, v
|
|
|
93
94
|
rest = rest.slice(postfix);
|
|
94
95
|
}
|
|
95
96
|
return [nodes, rest];
|
|
96
|
-
}))))
|
|
97
|
+
}))));
|
|
@@ -40,16 +40,16 @@ export const signature: IndexParser.SignatureParser = lazy(() => validate('|', f
|
|
|
40
40
|
html('span', { class: 'indexer', 'data-index': identity('index', undefined, ns.join(''))!.slice(7) }),
|
|
41
41
|
])));
|
|
42
42
|
|
|
43
|
-
const bracket: IndexParser.SignatureParser.BracketParser = lazy(() =>
|
|
44
|
-
surround(str('('), some(union([bracket, txt]), ')'), str(')'), true,
|
|
43
|
+
const bracket: IndexParser.SignatureParser.BracketParser = lazy(() => union([
|
|
44
|
+
surround(str('('), recursion(Recursion.terminal, some(union([bracket, txt]), ')')), str(')'), true,
|
|
45
45
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
|
|
46
|
-
surround(str('['), some(union([bracket, txt]), ']'), str(']'), true,
|
|
46
|
+
surround(str('['), recursion(Recursion.terminal, some(union([bracket, txt]), ']')), str(']'), true,
|
|
47
47
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
|
|
48
|
-
surround(str('{'), some(union([bracket, txt]), '}'), str('}'), true,
|
|
48
|
+
surround(str('{'), recursion(Recursion.terminal, some(union([bracket, txt]), '}')), str('}'), true,
|
|
49
49
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
|
|
50
|
-
surround(str('"'), precedence(2, some(txt, '"')), str('"'), true,
|
|
50
|
+
surround(str('"'), precedence(2, recursion(Recursion.terminal, some(txt, '"'))), str('"'), true,
|
|
51
51
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
|
|
52
|
-
]))
|
|
52
|
+
]));
|
|
53
53
|
|
|
54
54
|
export function dataindex(ns: readonly (string | HTMLElement)[]): string | undefined {
|
|
55
55
|
if (ns.length === 0) return;
|
|
@@ -11,10 +11,10 @@ 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(() => surround(
|
|
15
15
|
str(/^\[[:^|]/),
|
|
16
|
-
precedence(1,
|
|
17
|
-
tightStart(some(union([inline]), ']', [[']', 1]]))),
|
|
16
|
+
precedence(1, recursion(Recursion.inline,
|
|
17
|
+
tightStart(some(union([inline]), ']', [[']', 1]])))),
|
|
18
18
|
str(']'), false,
|
|
19
19
|
([, bs], rest) => [[
|
|
20
20
|
html('span', {
|
|
@@ -24,4 +24,4 @@ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => recursi
|
|
|
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));
|
|
@@ -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(/^<[a-z]+(?=[^\S\n]|>)/i,
|
|
21
|
+
export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i,
|
|
22
22
|
union([
|
|
23
23
|
focus(
|
|
24
24
|
/^<wbr[^\S\n]*>/i,
|
|
@@ -36,11 +36,11 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, re
|
|
|
36
36
|
([, tag]) =>
|
|
37
37
|
surround<HTMLParser.TagParser, string>(
|
|
38
38
|
surround(str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
39
|
-
precedence(3,
|
|
39
|
+
precedence(3, recursion(Recursion.inline,
|
|
40
40
|
subsequence([
|
|
41
41
|
focus(/^[^\S\n]*\n/, some(inline)),
|
|
42
42
|
some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`), [[blankWith('\n', `</${tag}>`), 3]]), true)),
|
|
43
|
-
])),
|
|
43
|
+
]))),
|
|
44
44
|
str(`</${tag}>`), true,
|
|
45
45
|
([as, bs = [], cs], rest) =>
|
|
46
46
|
[[elem(tag, as, bs, cs)], rest],
|
|
@@ -54,11 +54,11 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, re
|
|
|
54
54
|
([, tag]) =>
|
|
55
55
|
surround<HTMLParser.TagParser, string>(surround(
|
|
56
56
|
str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
57
|
-
precedence(3,
|
|
57
|
+
precedence(3, recursion(Recursion.inline,
|
|
58
58
|
subsequence([
|
|
59
59
|
focus(/^[^\S\n]*\n/, some(inline)),
|
|
60
60
|
some(inline, `</${tag}>`, [[`</${tag}>`, 3]]),
|
|
61
|
-
])),
|
|
61
|
+
]))),
|
|
62
62
|
str(`</${tag}>`), true,
|
|
63
63
|
([as, bs = [], cs], rest) =>
|
|
64
64
|
[[elem(tag, as, bs, cs)], rest],
|
|
@@ -66,7 +66,7 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, re
|
|
|
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),
|
|
@@ -7,14 +7,15 @@ import { repeat } from '../util';
|
|
|
7
7
|
import { push } from 'spica/array';
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
|
-
export const insertion: InsertionParser = lazy(() =>
|
|
10
|
+
export const insertion: InsertionParser = lazy(() => validate('++',
|
|
11
11
|
precedence(0, repeat('++', surround(
|
|
12
12
|
'',
|
|
13
|
+
recursion(Recursion.inline,
|
|
13
14
|
some(union([
|
|
14
15
|
some(inline, blankWith('\n', '++')),
|
|
15
16
|
open('\n', some(insertion, '+'), true),
|
|
16
|
-
])),
|
|
17
|
+
]))),
|
|
17
18
|
'++', false,
|
|
18
19
|
([, bs], rest) => [bs, rest],
|
|
19
20
|
([, bs], rest) => [push(bs, [Command.Escape]), rest]),
|
|
20
|
-
nodes => [html('ins', defrag(nodes))]))))
|
|
21
|
+
nodes => [html('ins', defrag(nodes))]))));
|
|
@@ -7,16 +7,18 @@ import { repeat } from '../util';
|
|
|
7
7
|
import { push } from 'spica/array';
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
|
+
// 可読性のため実際にはオブリーク体を指定する。
|
|
10
11
|
// 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
|
|
11
12
|
// ある程度の長さのある文に使うのが望ましい。
|
|
12
|
-
export const italic: ItalicParser = lazy(() =>
|
|
13
|
+
export const italic: ItalicParser = lazy(() => validate('///',
|
|
13
14
|
precedence(0, repeat('///', surround(
|
|
14
15
|
'',
|
|
16
|
+
recursion(Recursion.inline,
|
|
15
17
|
tightStart(some(union([
|
|
16
18
|
some(inline, blankWith('///')),
|
|
17
19
|
open(some(inline, '/'), italic),
|
|
18
|
-
]))),
|
|
20
|
+
])))),
|
|
19
21
|
'///', false,
|
|
20
22
|
([, bs], rest) => [bs, rest],
|
|
21
23
|
([, bs], rest) => [push(bs, [Command.Escape]), rest]),
|
|
22
|
-
nodes => [html('i', defrag(nodes))]))))
|
|
24
|
+
nodes => [html('i', defrag(nodes))]))));
|
|
@@ -180,6 +180,7 @@ describe('Unit: parser/inline/link', () => {
|
|
|
180
180
|
assert.deepStrictEqual(inspect(parser('[@a]{b}')), [['<a class="link" href="b">@a</a>'], '']);
|
|
181
181
|
assert.deepStrictEqual(inspect(parser('[@a@b]{c}')), [['<a class="link" href="c">@a@b</a>'], '']);
|
|
182
182
|
assert.deepStrictEqual(inspect(parser('[a@b]{c}')), [['<a class="link" href="c">a@b</a>'], '']);
|
|
183
|
+
assert.deepStrictEqual(inspect(parser('[==a==]{b}')), [['<a class="link" href="b">==a==</a>'], '']);
|
|
183
184
|
assert.deepStrictEqual(inspect(parser('[*a*]{b}')), [['<a class="link" href="b"><em>a</em></a>'], '']);
|
|
184
185
|
});
|
|
185
186
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MarkParser } from '../inline';
|
|
2
2
|
import { State, Recursion, Command } from '../context';
|
|
3
|
-
import { union, some, recursion, precedence, constraint, validate, surround, open, lazy } from '../../combinator';
|
|
3
|
+
import { union, some, recursion, precedence, state, constraint, validate, surround, open, lazy } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
5
|
import { identity, signature } from './extension/indexee';
|
|
6
6
|
import { tightStart, blankWith } from '../visibility';
|
|
@@ -8,13 +8,14 @@ import { repeat } from '../util';
|
|
|
8
8
|
import { push } from 'spica/array';
|
|
9
9
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
|
-
export const mark: MarkParser = lazy(() => constraint(State.mark, false,
|
|
12
|
-
precedence(0, repeat('==', surround(
|
|
11
|
+
export const mark: MarkParser = lazy(() => constraint(State.linkers & ~State.mark, false, validate('==',
|
|
12
|
+
precedence(0, state(State.mark, repeat('==', surround(
|
|
13
13
|
'',
|
|
14
|
+
recursion(Recursion.inline,
|
|
14
15
|
tightStart(some(union([
|
|
15
16
|
some(inline, blankWith('==')),
|
|
16
17
|
open(some(inline, '='), mark),
|
|
17
|
-
]))),
|
|
18
|
+
])))),
|
|
18
19
|
'==', false,
|
|
19
20
|
([, bs], rest) => [bs, rest],
|
|
20
21
|
([, bs], rest) => [push(bs, [Command.Escape]), rest]),
|
|
@@ -32,11 +32,12 @@ export const math: MathParser = lazy(() => validate('$', rewrite(
|
|
|
32
32
|
source)
|
|
33
33
|
], ''])));
|
|
34
34
|
|
|
35
|
-
const bracket: MathParser.BracketParser = lazy(() =>
|
|
35
|
+
const bracket: MathParser.BracketParser = lazy(() => surround(
|
|
36
36
|
str('{'),
|
|
37
|
+
recursion(Recursion.terminal,
|
|
37
38
|
some(union([
|
|
38
39
|
bracket,
|
|
39
40
|
some(escsource, /^[{}$\n]/),
|
|
40
|
-
])),
|
|
41
|
+
]))),
|
|
41
42
|
str('}'),
|
|
42
|
-
true))
|
|
43
|
+
true));
|
|
@@ -5,6 +5,7 @@ import { inline } from '../inline';
|
|
|
5
5
|
import { str } from '../source';
|
|
6
6
|
import { blank, trimBlankStart, trimBlankNodeEnd } from '../visibility';
|
|
7
7
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
|
+
import { unshift } from 'spica/array';
|
|
8
9
|
|
|
9
10
|
export const reference: ReferenceParser = lazy(() => constraint(State.reference, false, surround(
|
|
10
11
|
'[[',
|
|
@@ -19,7 +20,11 @@ export const reference: ReferenceParser = lazy(() => constraint(State.reference,
|
|
|
19
20
|
trimBlankNodeEnd(ns).length > 0
|
|
20
21
|
? [[html('sup', attributes(ns), [html('span', defrag(ns))])], rest]
|
|
21
22
|
: undefined,
|
|
22
|
-
|
|
23
|
+
([as, bs], rest, { state = 0 }) =>
|
|
24
|
+
state & State.annotation
|
|
25
|
+
? [unshift(as, bs), rest]
|
|
26
|
+
: undefined,
|
|
27
|
+
1 | Backtrack.linebracket, Backtrack.bracket | BacktrackState.nobreak)));
|
|
23
28
|
|
|
24
29
|
// Chicago-Style
|
|
25
30
|
const abbr: ReferenceParser.AbbrParser = surround(
|
|
@@ -7,13 +7,14 @@ import { memoize } from 'spica/memoize';
|
|
|
7
7
|
import { unshift, push } from 'spica/array';
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
|
-
export const remark: RemarkParser = lazy(() =>
|
|
10
|
+
export const remark: RemarkParser = lazy(() => match(
|
|
11
11
|
/^\[(%+)\s/,
|
|
12
12
|
memoize(
|
|
13
13
|
([, fence]) =>
|
|
14
14
|
surround(
|
|
15
15
|
str(`[${fence}`),
|
|
16
|
-
precedence(4,
|
|
16
|
+
precedence(4, recursion(Recursion.inline,
|
|
17
|
+
some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`), [[new RegExp(String.raw`^\s+${fence}\]`), 4]]))),
|
|
17
18
|
close(some(text, '%'), str(`${fence}]`)), true,
|
|
18
19
|
([as, bs = [], cs], rest) => [[
|
|
19
20
|
html('span', { class: 'remark' }, [
|
|
@@ -22,4 +23,4 @@ export const remark: RemarkParser = lazy(() => recursion(Recursion.inline, match
|
|
|
22
23
|
]),
|
|
23
24
|
], rest],
|
|
24
25
|
([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
25
|
-
([, fence]) => fence.length, {})))
|
|
26
|
+
([, fence]) => fence.length, {})));
|
|
@@ -17,6 +17,8 @@ describe('Unit: parser/inline/ruby', () => {
|
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('[	 a](b)')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('[&a;](b)')), [['<ruby class="invalid">&a;<rp>(</rp><rt>b</rt><rp>)</rp></ruby>'], '']);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('[a](&a;)')), [['<ruby class="invalid">a<rp>(</rp><rt>&a;</rt><rp>)</rp></ruby>'], '']);
|
|
20
|
+
assert.deepStrictEqual(inspect(parser('[<wbr>](a)')), undefined);
|
|
21
|
+
assert.deepStrictEqual(inspect(parser('[a](<wbr>)')), undefined);
|
|
20
22
|
assert.deepStrictEqual(inspect(parser('[a]()')), undefined);
|
|
21
23
|
assert.deepStrictEqual(inspect(parser('[a]( )')), undefined);
|
|
22
24
|
assert.deepStrictEqual(inspect(parser('[a\nb](c)')), undefined);
|
|
@@ -51,7 +53,6 @@ describe('Unit: parser/inline/ruby', () => {
|
|
|
51
53
|
assert.deepStrictEqual(inspect(parser('[©](©)')), [['<ruby>©<rp>(</rp><rt>©</rt><rp>)</rp></ruby>'], '']);
|
|
52
54
|
assert.deepStrictEqual(inspect(parser('[&copy;](&copy;)')), [['<ruby>&copy;<rp>(</rp><rt>&copy;</rt><rp>)</rp></ruby>'], '']);
|
|
53
55
|
assert.deepStrictEqual(inspect(parser('[*A*](*a*)')), [['<ruby>*A*<rp>(</rp><rt>*a*</rt><rp>)</rp></ruby>'], '']);
|
|
54
|
-
assert.deepStrictEqual(inspect(parser('[<wbr>](<wbr>)')), [['<ruby><wbr><rp>(</rp><rt><wbr></rt><rp>)</rp></ruby>'], '']);
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RubyParser } from '../inline';
|
|
2
2
|
import { Backtrack, Command, CmdRegExp } from '../context';
|
|
3
3
|
import { eval, exec } from '../../combinator/data/parser';
|
|
4
|
-
import { sequence, surround, lazy, fmap
|
|
4
|
+
import { sequence, surround, dup, lazy, fmap } from '../../combinator';
|
|
5
5
|
import { unsafehtmlentity } from './htmlentity';
|
|
6
6
|
import { text as txt, str } from '../source';
|
|
7
7
|
import { isTightNodeStart } from '../visibility';
|
|
@@ -10,15 +10,23 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
10
10
|
|
|
11
11
|
export const ruby: RubyParser = lazy(() => fmap(
|
|
12
12
|
sequence([
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
dup(surround(
|
|
14
|
+
'[', str(/^(?:\\[^\n]|[^\\[\](){}<>"\n])+/u), ']',
|
|
15
|
+
false,
|
|
16
|
+
([, [source]], rest, context) => {
|
|
17
|
+
const ns = eval(text({ source, context }), [undefined])[0];
|
|
18
|
+
ns && ns.at(-1) === '' && ns.pop();
|
|
19
|
+
return ns && isTightNodeStart(ns) ? [ns, rest] : undefined;
|
|
20
|
+
},
|
|
21
|
+
undefined, 3 | Backtrack.ruby)),
|
|
22
|
+
dup(surround(
|
|
23
|
+
'(', str(/^(?:\\[^\n]|[^\\[\](){}<>"\n])+/u), ')',
|
|
24
|
+
false,
|
|
25
|
+
([, [source]], rest, context) => {
|
|
26
|
+
const ns = eval(text({ source, context }), [undefined])[0];
|
|
27
|
+
return ns && [ns, rest];
|
|
28
|
+
},
|
|
29
|
+
undefined, 3 | Backtrack.ruby)),
|
|
22
30
|
]),
|
|
23
31
|
([texts, rubies]) => {
|
|
24
32
|
switch (true) {
|
|
@@ -8,16 +8,16 @@ import { tightStart, blankWith } from '../visibility';
|
|
|
8
8
|
import { unshift } from 'spica/array';
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
|
-
export const strong: StrongParser = lazy(() =>
|
|
11
|
+
export const strong: StrongParser = lazy(() => surround(
|
|
12
12
|
str('**', '*'),
|
|
13
|
-
precedence(0,
|
|
13
|
+
precedence(0, recursion(Recursion.inline,
|
|
14
14
|
tightStart(some(union([
|
|
15
15
|
some(inline, blankWith('**')),
|
|
16
16
|
open(some(inline, '*'), union([
|
|
17
17
|
emstrong,
|
|
18
18
|
strong,
|
|
19
19
|
])),
|
|
20
|
-
])))),
|
|
20
|
+
]))))),
|
|
21
21
|
str('**'), false,
|
|
22
22
|
([, bs], rest) => [[html('strong', defrag(bs))], rest],
|
|
23
|
-
([as, bs], rest) => [unshift(as, bs), rest]))
|
|
23
|
+
([as, bs], rest) => [unshift(as, bs), rest]));
|
|
@@ -12,13 +12,13 @@ export const template: TemplateParser = lazy(() => surround(
|
|
|
12
12
|
([, ns = []], rest) => [[html('span', { class: 'template' }, `{{${ns.join('')}}}`)], rest],
|
|
13
13
|
undefined, 3 | Backtrack.template));
|
|
14
14
|
|
|
15
|
-
const bracket: TemplateParser.BracketParser = lazy(() =>
|
|
16
|
-
surround(str('('), some(union([bracket, escsource]), ')'), str(')'), true,
|
|
15
|
+
const bracket: TemplateParser.BracketParser = lazy(() => union([
|
|
16
|
+
surround(str('('), recursion(Recursion.terminal, some(union([bracket, escsource]), ')')), str(')'), true,
|
|
17
17
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
|
|
18
|
-
surround(str('['), some(union([bracket, escsource]), ']'), str(']'), true,
|
|
18
|
+
surround(str('['), recursion(Recursion.terminal, some(union([bracket, escsource]), ']')), str(']'), true,
|
|
19
19
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
|
|
20
|
-
surround(str('{'), some(union([bracket, escsource]), '}'), str('}'), true,
|
|
20
|
+
surround(str('{'), recursion(Recursion.terminal, some(union([bracket, escsource]), '}')), str('}'), true,
|
|
21
21
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
|
|
22
|
-
surround(str('"'), precedence(2, some(escsource, /^["\n]/)), str('"'), true,
|
|
22
|
+
surround(str('"'), precedence(2, recursion(Recursion.terminal, some(escsource, /^["\n]/))), str('"'), true,
|
|
23
23
|
undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
|
|
24
|
-
]))
|
|
24
|
+
]));
|