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.
@@ -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(5000);
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(5000);
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(`${'.'.repeat(8)}((${'['.repeat(16)}http://{{${'.'.repeat(7684)}`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
355
- [`<p>${'.'.repeat(8)}((${'['.repeat(16)}http://{{${'.'.repeat(7684)}</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(`${'.'.repeat(8)}((${'['.repeat(16)}http://{{${'.'.repeat(7685)}`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
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">${'.'.repeat(8)}((${'['.repeat(16)}http://{{${'.'.repeat(1000 - 8 - 2 - 16 - 9 - 3)}...</pre>`,
366
+ `<pre class="error" translate="no">${str.slice(0, 1000 - 3)}...</pre>`,
365
367
  ]);
366
368
  });
367
369
 
@@ -29,15 +29,17 @@ export const enum Recursion {
29
29
  }
30
30
 
31
31
  export const enum Backtrack {
32
- template = 8 << 2,
33
- index = 7 << 2,
34
- link = 6 << 2,
35
- ruby = 5 << 2,
36
- media = 4 << 2,
37
- linebracket = 3 << 2,
38
- bracket = 2 << 2,
39
- url = 1 << 2,
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|['_])/u.source.replace(/emoji/, emoji), 'u'))),
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`)), [[`#'0`], '']);
25
- assert.deepStrictEqual(inspect(parser(`#'00`)), [[`#'00`], '']);
26
- assert.deepStrictEqual(inspect(parser('#_')), [['#_'], '']);
27
- assert.deepStrictEqual(inspect(parser('#_a')), [['#_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(`#1'`)), [[`<a class="hashtag" href="/hashtags/1'">#1'</a>`], '']);
62
- assert.deepStrictEqual(inspect(parser(`#1''`)), [[`<a class="hashtag" href="/hashtags/1''">#1''</a>`], '']);
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'_b`)), [[`<a class="hashtag" href="/hashtags/a'_b">#a'_b</a>`], '']);
67
- assert.deepStrictEqual(inspect(parser(`#a_'b`)), [[`<a class="hashtag" href="/hashtags/a_'b">#a_'b</a>`], '']);
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|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|')))/u.source,
18
- /(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source,
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(() => recursion(Recursion.terminal, union([
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[#>]|[\r\n]!?https?:\/\/)/iu,
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]+)*(?:@(?:[0-9a-z]+(?:[.-][0-9a-z]+)*)?)*/i),
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]*(?:[-.][0-9a-z]+)*/i),
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|['_])(?=#)/u.source.replace('emoji', emoji), 'u')),
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|['_])*/u.source.replace('emoji', emoji), 'u')),
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(() => recursion(Recursion.inline, validate('~~',
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(() => recursion(Recursion.inline, surround(
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(() => recursion(Recursion.inline, validate('***',
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(() => recursion(Recursion.terminal, union([
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(() => recursion(Recursion.inline, surround(
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, recursion(Recursion.inline,
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(() => recursion(Recursion.inline, validate('++',
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(() => recursion(Recursion.inline, validate('///',
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, recursion(Recursion.inline, validate('==',
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(() => recursion(Recursion.terminal, surround(
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
- undefined, 1 | Backtrack.linebracket, Backtrack.bracket | BacktrackState.nobreak)));
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(() => recursion(Recursion.inline, match(
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, some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`), [[new RegExp(String.raw`^\s+${fence}\]`), 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('[&Tab; a](b)')), undefined);
18
18
  assert.deepStrictEqual(inspect(parser('[&a;](b)')), [['<ruby class="invalid">&amp;a;<rp>(</rp><rt>b</rt><rp>)</rp></ruby>'], '']);
19
19
  assert.deepStrictEqual(inspect(parser('[a](&a;)')), [['<ruby class="invalid">a<rp>(</rp><rt>&amp;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('[&copy;](&copy;)')), [['<ruby>©<rp>(</rp><rt>©</rt><rp>)</rp></ruby>'], '']);
52
54
  assert.deepStrictEqual(inspect(parser('[&amp;copy;](&amp;copy;)')), [['<ruby>&amp;copy;<rp>(</rp><rt>&amp;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>&lt;wbr&gt;<rp>(</rp><rt>&lt;wbr&gt;</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, bind } from '../../combinator';
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
- bind(surround('[', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ']', false, undefined, undefined, 3 | Backtrack.ruby), ([source], rest, context) => {
14
- const ns = eval(text({ source, context }), [undefined])[0];
15
- ns && ns.at(-1) === '' && ns.pop();
16
- return ns && isTightNodeStart(ns) ? [[ns], rest] : undefined;
17
- }),
18
- bind(surround('(', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ')', false, undefined, undefined, 3 | Backtrack.ruby), ([source], rest, context) => {
19
- const ns = eval(text({ source, context }), [undefined])[0];
20
- return ns && [[ns], rest];
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(() => recursion(Recursion.inline, surround(
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(() => recursion(Recursion.terminal, union([
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
+ ]));