securemark 0.255.0 → 0.257.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.js +209 -170
  3. package/markdown.d.ts +18 -11
  4. package/package.json +1 -1
  5. package/src/combinator/control/constraint/contract.ts +3 -13
  6. package/src/combinator/control/manipulation/context.ts +12 -7
  7. package/src/combinator/control/manipulation/resource.ts +37 -3
  8. package/src/combinator/control/manipulation/surround.ts +6 -6
  9. package/src/combinator/data/parser/inits.ts +1 -1
  10. package/src/combinator/data/parser/sequence.ts +1 -1
  11. package/src/combinator/data/parser/some.ts +16 -38
  12. package/src/combinator/data/parser.ts +34 -18
  13. package/src/debug.test.ts +2 -2
  14. package/src/parser/api/bind.ts +9 -11
  15. package/src/parser/api/parse.test.ts +51 -9
  16. package/src/parser/api/parse.ts +6 -5
  17. package/src/parser/block/extension/aside.ts +3 -3
  18. package/src/parser/block/extension/example.ts +3 -3
  19. package/src/parser/block.ts +1 -1
  20. package/src/parser/inline/annotation.test.ts +6 -5
  21. package/src/parser/inline/annotation.ts +9 -6
  22. package/src/parser/inline/autolink/url.ts +6 -6
  23. package/src/parser/inline/bracket.test.ts +11 -7
  24. package/src/parser/inline/bracket.ts +11 -11
  25. package/src/parser/inline/comment.test.ts +4 -3
  26. package/src/parser/inline/comment.ts +4 -4
  27. package/src/parser/inline/deletion.ts +3 -3
  28. package/src/parser/inline/emphasis.ts +3 -3
  29. package/src/parser/inline/emstrong.ts +4 -5
  30. package/src/parser/inline/extension/index.test.ts +1 -0
  31. package/src/parser/inline/extension/index.ts +8 -7
  32. package/src/parser/inline/extension/indexer.ts +3 -5
  33. package/src/parser/inline/extension/label.ts +1 -1
  34. package/src/parser/inline/extension/placeholder.test.ts +8 -7
  35. package/src/parser/inline/extension/placeholder.ts +4 -4
  36. package/src/parser/inline/html.test.ts +2 -0
  37. package/src/parser/inline/html.ts +5 -5
  38. package/src/parser/inline/insertion.ts +3 -3
  39. package/src/parser/inline/link.test.ts +1 -0
  40. package/src/parser/inline/link.ts +8 -7
  41. package/src/parser/inline/mark.ts +3 -3
  42. package/src/parser/inline/math.test.ts +21 -14
  43. package/src/parser/inline/math.ts +4 -15
  44. package/src/parser/inline/media.test.ts +0 -2
  45. package/src/parser/inline/media.ts +6 -6
  46. package/src/parser/inline/reference.test.ts +9 -9
  47. package/src/parser/inline/reference.ts +19 -7
  48. package/src/parser/inline/ruby.ts +29 -27
  49. package/src/parser/inline/strong.ts +3 -3
  50. package/src/parser/inline/template.ts +4 -4
  51. package/src/parser/inline.test.ts +13 -10
  52. package/src/parser/util.ts +18 -2
@@ -1,12 +1,12 @@
1
1
  import { MarkParser } from '../inline';
2
- import { union, some, creator, surround, open, lazy } from '../../combinator';
2
+ import { union, some, precedence, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
5
  import { startTight, blankWith } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
- export const mark: MarkParser = lazy(() => creator(surround(
9
+ export const mark: MarkParser = lazy(() => creator(precedence(2, surround(
10
10
  str('=='),
11
11
  startTight(some(union([
12
12
  some(inline, blankWith('==')),
@@ -14,4 +14,4 @@ export const mark: MarkParser = lazy(() => creator(surround(
14
14
  ]))),
15
15
  str('=='), false,
16
16
  ([, bs], rest) => [[html('mark', defrag(bs))], rest],
17
- ([as, bs], rest) => [unshift(as, bs), rest])));
17
+ ([as, bs], rest) => [unshift(as, bs), rest]))));
@@ -18,6 +18,12 @@ describe('Unit: parser/inline/math', () => {
18
18
  assert.deepStrictEqual(inspect(parser('$-a $-b')), undefined);
19
19
  assert.deepStrictEqual(inspect(parser('$-a\\ $-b')), undefined);
20
20
  assert.deepStrictEqual(inspect(parser('$a $')), undefined);
21
+ assert.deepStrictEqual(inspect(parser('$-a `$`')), undefined);
22
+ assert.deepStrictEqual(inspect(parser('$-a ``$``')), undefined);
23
+ // (``" + ``" + $-b") or (``"`` + "$-b")
24
+ assert.deepStrictEqual(inspect(parser('$-a ``"``"$-b"')), undefined);
25
+ // (``b`` + "c" + $-d) or (``b``"c" + $-d)
26
+ assert.deepStrictEqual(inspect(parser('$-a ``b``"c"$-d')), undefined);
21
27
  assert.deepStrictEqual(inspect(parser('$-a"$-b"')), undefined);
22
28
  assert.deepStrictEqual(inspect(parser('$-a\\"$-b\\"')), undefined);
23
29
  assert.deepStrictEqual(inspect(parser('$a"$')), undefined);
@@ -28,7 +34,6 @@ describe('Unit: parser/inline/math', () => {
28
34
  assert.deepStrictEqual(inspect(parser('$-a\\[$-b\\]')), undefined);
29
35
  assert.deepStrictEqual(inspect(parser('$a[$')), undefined);
30
36
  assert.deepStrictEqual(inspect(parser('$-a{$-b}')), undefined);
31
- assert.deepStrictEqual(inspect(parser('$-a\\{$-b\\}')), undefined);
32
37
  assert.deepStrictEqual(inspect(parser('$a{$')), undefined);
33
38
  assert.deepStrictEqual(inspect(parser('$a$b')), undefined);
34
39
  assert.deepStrictEqual(inspect(parser('$a$b$')), undefined);
@@ -38,13 +43,25 @@ describe('Unit: parser/inline/math', () => {
38
43
  assert.deepStrictEqual(inspect(parser('$\n$')), undefined);
39
44
  assert.deepStrictEqual(inspect(parser('$a\\$\nb$')), undefined);
40
45
  assert.deepStrictEqual(inspect(parser('$a\\$\\\nb$')), undefined);
41
- assert.deepStrictEqual(inspect(parser('$`"$')), undefined);
46
+ assert.deepStrictEqual(inspect(parser('$`$')), undefined);
47
+ assert.deepStrictEqual(inspect(parser('$`a"$')), undefined);
48
+ assert.deepStrictEqual(inspect(parser('$`a"b$')), undefined);
49
+ assert.deepStrictEqual(inspect(parser('$``$')), undefined);
50
+ assert.deepStrictEqual(inspect(parser('$``$"$')), undefined);
51
+ assert.deepStrictEqual(inspect(parser('$``a$')), undefined);
42
52
  assert.deepStrictEqual(inspect(parser('$``a"$0')), undefined);
53
+ assert.deepStrictEqual(inspect(parser('$``a""b$')), undefined);
43
54
  assert.deepStrictEqual(inspect(parser('$``a""$')), undefined);
55
+ assert.deepStrictEqual(inspect(parser('$```a"$')), undefined);
44
56
  assert.deepStrictEqual(inspect(parser('$```a""$')), undefined);
45
57
  assert.deepStrictEqual(inspect(parser('$``\n"$')), undefined);
46
58
  assert.deepStrictEqual(inspect(parser('$``a\\$\nb"$')), undefined);
47
59
  assert.deepStrictEqual(inspect(parser('$``a\\$\\\nb"$')), undefined);
60
+ assert.deepStrictEqual(inspect(parser('$``"$')), undefined);
61
+ assert.deepStrictEqual(inspect(parser('$``a"$')), undefined);
62
+ assert.deepStrictEqual(inspect(parser('$``\\"$')), undefined);
63
+ assert.deepStrictEqual(inspect(parser('$``a``b"c"$')), undefined);
64
+ assert.deepStrictEqual(inspect(parser('$````""$')), undefined);
48
65
  assert.deepStrictEqual(inspect(parser('$"$')), undefined);
49
66
  assert.deepStrictEqual(inspect(parser('$}$')), undefined);
50
67
  assert.deepStrictEqual(inspect(parser('${')), undefined);
@@ -104,18 +121,8 @@ describe('Unit: parser/inline/math', () => {
104
121
  assert.deepStrictEqual(inspect(parser('$f(x)$')), [['<span class="math" translate="no" data-src="$f(x)$">$f(x)$</span>'], '']);
105
122
  assert.deepStrictEqual(inspect(parser('$f: x \\to y$')), [['<span class="math" translate="no" data-src="$f: x \\to y$">$f: x \\to y$</span>'], '']);
106
123
  assert.deepStrictEqual(inspect(parser('$k$-space')), [['<span class="math" translate="no" data-src="$k$">$k$</span>'], '-space']);
107
- assert.deepStrictEqual(inspect(parser('$`$')), [['<span class="math" translate="no" data-src="$`$">$`$</span>'], '']);
108
- assert.deepStrictEqual(inspect(parser('$`"a$')), [['<span class="math" translate="no" data-src="$`&quot;a$">$`"a$</span>'], '']);
109
- assert.deepStrictEqual(inspect(parser('$``$')), [['<span class="math" translate="no" data-src="$``$">$``$</span>'], '']);
110
- assert.deepStrictEqual(inspect(parser('$``"$')), [['<span class="math" translate="no" data-src="$``&quot;$">$``"$</span>'], '']);
111
- assert.deepStrictEqual(inspect(parser('$``""a$')), [['<span class="math" translate="no" data-src="$``&quot;&quot;a$">$``""a$</span>'], '']);
112
- assert.deepStrictEqual(inspect(parser('$``a"$')), [['<span class="math" translate="no" data-src="$``a&quot;$">$``a"$</span>'], '']);
113
- assert.deepStrictEqual(inspect(parser('$``$"$')), [['<span class="math" translate="no" data-src="$``$">$``$</span>'], '"$']);
114
- assert.deepStrictEqual(inspect(parser('$``\\"$')), [['<span class="math" translate="no" data-src="$``\\&quot;$">$``\\"$</span>'], '']);
115
- assert.deepStrictEqual(inspect(parser('$``a``b"c"$')), [['<span class="math" translate="no" data-src="$``a``b&quot;c&quot;$">$``a``b"c"$</span>'], '']);
116
- assert.deepStrictEqual(inspect(parser('$```"$')), [['<span class="math" translate="no" data-src="$```&quot;$">$```"$</span>'], '']);
117
- assert.deepStrictEqual(inspect(parser('$````""$')), [['<span class="math" translate="no" data-src="$````&quot;&quot;$">$````""$</span>'], '']);
118
124
  assert.deepStrictEqual(inspect(parser('$a{b}$')), [['<span class="math" translate="no" data-src="$a{b}$">$a{b}$</span>'], '']);
125
+ assert.deepStrictEqual(inspect(parser('$a\\{$\\}')), [['<span class="math" translate="no" data-src="$a\\{$">$a\\{$</span>'], '\\}']);
119
126
  assert.deepStrictEqual(inspect(parser('${}$')), [['<span class="math" translate="no" data-src="${}$">${}$</span>'], '']);
120
127
  assert.deepStrictEqual(inspect(parser('${ }$')), [['<span class="math" translate="no" data-src="${ }$">${ }$</span>'], '']);
121
128
  assert.deepStrictEqual(inspect(parser('${a}$')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], '']);
@@ -127,7 +134,7 @@ describe('Unit: parser/inline/math', () => {
127
134
  assert.deepStrictEqual(inspect(parser('${a }$')), [['<span class="math" translate="no" data-src="${a }$">${a }$</span>'], '']);
128
135
  assert.deepStrictEqual(inspect(parser('${ a}$')), [['<span class="math" translate="no" data-src="${ a}$">${ a}$</span>'], '']);
129
136
  assert.deepStrictEqual(inspect(parser('${ a }$')), [['<span class="math" translate="no" data-src="${ a }$">${ a }$</span>'], '']);
130
- assert.deepStrictEqual(inspect(parser('${``a"}$')), [['<span class="math" translate="no" data-src="${``a&quot;}$">${``a"}$</span>'], '']);
137
+ assert.deepStrictEqual(inspect(parser('${``"}$')), [['<span class="math" translate="no" data-src="${``&quot;}$">${``"}$</span>'], '']);
131
138
  assert.deepStrictEqual(inspect(parser('${\\a}$')), [['<span class="math" translate="no" data-src="${\\a}$">${\\a}$</span>'], '']);
132
139
  assert.deepStrictEqual(inspect(parser('${\\$}$')), [['<span class="math" translate="no" data-src="${\\$}$">${\\$}$</span>'], '']);
133
140
  assert.deepStrictEqual(inspect(parser('${\\\\}$')), [['<span class="math" translate="no" data-src="${\\\\}$">${\\\\}$</span>'], '']);
@@ -1,19 +1,18 @@
1
1
  import { MathParser } from '../inline';
2
- import { union, some, validate, focus, rewrite, creator, surround, lazy } from '../../combinator';
2
+ import { union, some, validate, rewrite, precedence, creator, surround, lazy } from '../../combinator';
3
3
  import { escsource, str } from '../source';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
- const syntax = /^(?:[ "([](?!\$)|\\{(?!\$)|\\[\\}$]?|^`|`(?!`)|[!#%&')\x2A-\x5A\]^_\x61-\x7A|~])+/;
6
+ const syntax = /^(?:[ ([](?!\$)|\\[\\{}$]?|[!#%&')\x2A-\x5A\]^_\x61-\x7A|~])+/;
7
7
  const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])/i;
8
8
 
9
- export const math: MathParser = lazy(() => creator(validate('$', rewrite(
9
+ export const math: MathParser = lazy(() => validate('$', creator(precedence(7, rewrite(
10
10
  union([
11
11
  surround('$', bracket, '$'),
12
12
  surround(
13
13
  /^\$(?![\s{}])/,
14
14
  some(union([
15
15
  bracket,
16
- quote,
17
16
  str(syntax),
18
17
  ])),
19
18
  /^\$(?![0-9A-Za-z])/),
@@ -31,7 +30,7 @@ export const math: MathParser = lazy(() => creator(validate('$', rewrite(
31
30
  'data-invalid-message': `"${source.match(forbiddenCommand)![0]}" command is forbidden`,
32
31
  },
33
32
  source)
34
- ], '']))));
33
+ ], ''])))));
35
34
 
36
35
  const bracket: MathParser.BracketParser = lazy(() => creator(surround(
37
36
  '{',
@@ -41,13 +40,3 @@ const bracket: MathParser.BracketParser = lazy(() => creator(surround(
41
40
  ])),
42
41
  '}',
43
42
  true)));
44
-
45
- const quote: MathParser.QuoteParser = lazy(() => creator(surround(
46
- '``',
47
- some(union([
48
- quote,
49
- bracket,
50
- focus(/^(?:\\[\\{}$]|`(?!`)|[^`{}"$\n\P{ASCII}])*/u, str(syntax)),
51
- ])),
52
- /^"?/,
53
- true)));
@@ -45,8 +45,6 @@ describe('Unit: parser/inline/media', () => {
45
45
  assert.deepStrictEqual(inspect(parser('![[]{b}')), undefined);
46
46
  assert.deepStrictEqual(inspect(parser('![]]{b}')), undefined);
47
47
  assert.deepStrictEqual(inspect(parser('![a]{}')), undefined);
48
- assert.deepStrictEqual(inspect(parser('![\\ a ]{b}')), undefined);
49
- assert.deepStrictEqual(inspect(parser('![ \\ a ]{b}')), undefined);
50
48
  assert.deepStrictEqual(inspect(parser('![a\nb]{b}')), undefined);
51
49
  assert.deepStrictEqual(inspect(parser('![a\\\nb]{b}')), undefined);
52
50
  assert.deepStrictEqual(inspect(parser('![]{ttp://host}')), [['<img class="media invalid" data-src="ttp://host" alt="">'], '']);
@@ -1,6 +1,6 @@
1
1
  import { undefined, location } from 'spica/global';
2
2
  import { MediaParser } from '../inline';
3
- import { union, inits, tails, some, validate, verify, guard, creator, surround, open, dup, lazy, fmap, bind } from '../../combinator';
3
+ import { union, inits, tails, some, validate, verify, guard, precedence, creator, surround, open, dup, lazy, fmap, bind } from '../../combinator';
4
4
  import { link, uri, option as linkoption, resolve } from './link';
5
5
  import { attributes } from './html';
6
6
  import { unsafehtmlentity } from './htmlentity';
@@ -17,13 +17,13 @@ const optspec = {
17
17
  } as const;
18
18
  Object.setPrototypeOf(optspec, null);
19
19
 
20
- export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'], '}', '\n', bind(verify(fmap(open(
20
+ export const media: MediaParser = lazy(() => validate(['![', '!{'], creator(10, precedence(3, bind(verify(fmap(open(
21
21
  '!',
22
22
  guard(context => context.syntax?.inline?.media ?? true,
23
23
  tails([
24
24
  dup(surround(
25
- /^\[(?!\s*\\\s)/,
26
- some(union([unsafehtmlentity, bracket, txt]), ']', /^\\?\n/),
25
+ '[',
26
+ some(union([unsafehtmlentity, bracket, txt]), ']', [[/^\\?\n/, 9]]),
27
27
  ']',
28
28
  true)),
29
29
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
@@ -57,13 +57,13 @@ export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'],
57
57
  link as MediaParser,
58
58
  ([link]) => [define(link, { target: '_blank' }, [el])])
59
59
  (`{ ${INSECURE_URI}${params.join('')} }${rest}`, context);
60
- }))));
60
+ })))));
61
61
 
62
62
  const bracket: MediaParser.TextParser.BracketParser = lazy(() => union([
63
63
  surround(str('('), some(union([unsafehtmlentity, bracket, txt]), ')'), str(')'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
64
64
  surround(str('['), some(union([unsafehtmlentity, bracket, txt]), ']'), str(']'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
65
65
  surround(str('{'), some(union([unsafehtmlentity, bracket, txt]), '}'), str('}'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
66
- surround(str('"'), some(union([unsafehtmlentity, txt]), '"'), str('"'), true),
66
+ surround(str('"'), precedence(8, some(union([unsafehtmlentity, txt]), '"')), str('"'), true),
67
67
  ]));
68
68
 
69
69
  const option: MediaParser.ParameterParser.OptionParser = union([
@@ -17,11 +17,11 @@ describe('Unit: parser/inline/reference', () => {
17
17
  assert.deepStrictEqual(inspect(parser('[[\n]]')), undefined);
18
18
  assert.deepStrictEqual(inspect(parser('[[\na]]')), undefined);
19
19
  assert.deepStrictEqual(inspect(parser('[[\\\na]]')), undefined);
20
- assert.deepStrictEqual(inspect(parser('[[a\n]]')), undefined);
21
- assert.deepStrictEqual(inspect(parser('[[a\\\n]]')), undefined);
22
- assert.deepStrictEqual(inspect(parser('[[a\nb]]')), undefined);
23
- assert.deepStrictEqual(inspect(parser('[[a\\\nb]]')), undefined);
24
- assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), undefined);
20
+ assert.deepStrictEqual(inspect(parser('[[a\n]]')), [['[['], 'a\n]]']);
21
+ assert.deepStrictEqual(inspect(parser('[[a\\\n]]')), [['[['], 'a\\\n]]']);
22
+ assert.deepStrictEqual(inspect(parser('[[a\nb]]')), [['[['], 'a\nb]]']);
23
+ assert.deepStrictEqual(inspect(parser('[[a\\\nb]]')), [['[['], 'a\\\nb]]']);
24
+ assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[['], '*a\nb*]]']);
25
25
  assert.deepStrictEqual(inspect(parser('[[\\]]')), undefined);
26
26
  assert.deepStrictEqual(inspect(parser('[[a]b]]')), undefined);
27
27
  assert.deepStrictEqual(inspect(parser('[[[a]]')), undefined);
@@ -57,7 +57,7 @@ describe('Unit: parser/inline/reference', () => {
57
57
  assert.deepStrictEqual(inspect(parser('[[^a,]]')), [['<sup class="reference" data-abbr="a,"><span></span></sup>'], '']);
58
58
  assert.deepStrictEqual(inspect(parser('[[^a, ]]')), [['<sup class="reference" data-abbr="a,"><span></span></sup>'], '']);
59
59
  assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="reference" data-abbr="a"><span></span></sup>'], '']);
60
- assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="invalid"><span>^a </span></sup>'], '']);
60
+ assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="invalid"><span>^a</span></sup>'], '']);
61
61
  assert.deepStrictEqual(inspect(parser('[[^a b]]')), [['<sup class="reference" data-abbr="a b"><span></span></sup>'], '']);
62
62
  assert.deepStrictEqual(inspect(parser('[[^a b]]')), [['<sup class="invalid"><span>^a b</span></sup>'], '']);
63
63
  assert.deepStrictEqual(inspect(parser('[[^a|]]')), [['<sup class="reference" data-abbr="a"><span></span></sup>'], '']);
@@ -75,7 +75,7 @@ describe('Unit: parser/inline/reference', () => {
75
75
  assert.deepStrictEqual(inspect(parser('[[^a| ]]')), [['<sup class="reference" data-abbr="a"><span></span></sup>'], '']);
76
76
  assert.deepStrictEqual(inspect(parser('[[^1]]')), [['<sup class="invalid"><span>^1</span></sup>'], '']);
77
77
  assert.deepStrictEqual(inspect(parser('[[^1a]]')), [['<sup class="reference" data-abbr="1a"><span></span></sup>'], '']);
78
- assert.deepStrictEqual(inspect(parser('[[^1 ]]')), [['<sup class="invalid"><span>^1 </span></sup>'], '']);
78
+ assert.deepStrictEqual(inspect(parser('[[^1 ]]')), [['<sup class="invalid"><span>^1</span></sup>'], '']);
79
79
  assert.deepStrictEqual(inspect(parser('[[^1 a]]')), [['<sup class="reference" data-abbr="1 a"><span></span></sup>'], '']);
80
80
  assert.deepStrictEqual(inspect(parser('[[^1|]]')), [['<sup class="invalid"><span>^1|</span></sup>'], '']);
81
81
  assert.deepStrictEqual(inspect(parser('[[^1 |]]')), [['<sup class="invalid"><span>^1 |</span></sup>'], '']);
@@ -86,11 +86,11 @@ describe('Unit: parser/inline/reference', () => {
86
86
  assert.deepStrictEqual(inspect(parser(`[[^A's, Aces']]`)), [[`<sup class="reference" data-abbr="A's, Aces'"><span></span></sup>`], '']);
87
87
  assert.deepStrictEqual(inspect(parser('[[^^]]')), [['<sup class="invalid"><span>^^</span></sup>'], '']);
88
88
  assert.deepStrictEqual(inspect(parser('[[\\^]]')), [['<sup class="reference"><span>^</span></sup>'], '']);
89
- assert.deepStrictEqual(inspect(parser('[[^ ]]')), [['<sup class="invalid"><span>^ </span></sup>'], '']);
89
+ assert.deepStrictEqual(inspect(parser('[[^ ]]')), [['<sup class="invalid"><span>^</span></sup>'], '']);
90
90
  assert.deepStrictEqual(inspect(parser('[[^ a]]')), [['<sup class="invalid"><span>^ a</span></sup>'], '']);
91
91
  assert.deepStrictEqual(inspect(parser('[[^ |]]')), [['<sup class="invalid"><span>^ |</span></sup>'], '']);
92
92
  assert.deepStrictEqual(inspect(parser('[[^ |b]]')), [['<sup class="invalid"><span>^ |b</span></sup>'], '']);
93
- assert.deepStrictEqual(inspect(parser('[[^ | ]]')), [['<sup class="invalid"><span>^ | </span></sup>'], '']);
93
+ assert.deepStrictEqual(inspect(parser('[[^ | ]]')), [['<sup class="invalid"><span>^ |</span></sup>'], '']);
94
94
  assert.deepStrictEqual(inspect(parser('[[^ | b]]')), [['<sup class="invalid"><span>^ | b</span></sup>'], '']);
95
95
  });
96
96
 
@@ -1,12 +1,13 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ReferenceParser } from '../inline';
3
- import { union, subsequence, some, validate, guard, context, creator, surround, open, lazy, fmap, bind } from '../../combinator';
3
+ import { Result } from '../../combinator/data/parser';
4
+ import { union, subsequence, some, validate, guard, context, precedence, creator, recursion, surround, open, lazy, bind } from '../../combinator';
4
5
  import { inline } from '../inline';
5
6
  import { str, stropt } from '../source';
6
- import { regBlankStart, trimBlank, stringify } from '../util';
7
+ import { regBlankStart, trimBlankStart, trimNodeEnd, stringify } from '../util';
7
8
  import { html, defrag } from 'typed-dom/dom';
8
9
 
9
- export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]', '\n', fmap(surround(
10
+ export const reference: ReferenceParser = lazy(() => validate('[[', creator(recursion(precedence(6, surround(
10
11
  '[[',
11
12
  guard(context => context.syntax?.inline?.reference ?? true,
12
13
  context({ syntax: { inline: {
@@ -21,11 +22,13 @@ export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]'
21
22
  }}, delimiters: undefined },
22
23
  subsequence([
23
24
  abbr,
24
- open(stropt(/^(?=\^)/), some(inline, ']', /^\\?\n/)),
25
- trimBlank(some(inline, ']', /^\\?\n/)),
25
+ open(stropt(/^(?=\^)/), some(inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]])),
26
+ trimBlankStart(some(inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]])),
26
27
  ]))),
27
- ']]'),
28
- ns => [html('sup', attributes(ns), [html('span', defrag(ns))])]))));
28
+ ']]',
29
+ false,
30
+ ([, ns], rest) => [[html('sup', attributes(ns), [html('span', trimNodeEnd(defrag(ns)))])], rest],
31
+ ([, ns, rest], next) => next[0] === ']' ? undefined : optimize('[[', ns, rest)))))));
29
32
 
30
33
  const abbr: ReferenceParser.AbbrParser = creator(bind(surround(
31
34
  '^',
@@ -48,3 +51,12 @@ function attributes(ns: (string | HTMLElement)[]): Record<string, string | undef
48
51
  }
49
52
  : { class: 'reference' };
50
53
  }
54
+
55
+ export function optimize(opener: string, ns: readonly (string | HTMLElement)[], rest: string): Result<string> {
56
+ let count = 0;
57
+ for (let i = 0; i < ns.length - 1; i += 2) {
58
+ if (ns[i] !== '' || ns[i + 1] !== opener[0]) break;
59
+ ++count;
60
+ }
61
+ return [[opener[0].repeat(opener.length + count)], rest.slice(count)];
62
+ }
@@ -1,47 +1,49 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { RubyParser } from '../inline';
3
3
  import { eval, exec } from '../../combinator/data/parser';
4
- import { sequence, validate, verify, focus, creator, surround, lazy, bind } from '../../combinator';
4
+ import { sequence, validate, verify, focus, creator, surround, lazy, fmap } from '../../combinator';
5
5
  import { unsafehtmlentity } from './htmlentity';
6
6
  import { text as txt } from '../source';
7
7
  import { isStartTightNodes } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
  import { unshift, push } from 'spica/array';
10
10
 
11
- export const ruby: RubyParser = lazy(() => creator(validate('[', ')', '\n', bind(verify(
11
+ export const ruby: RubyParser = lazy(() => validate('[', creator(fmap(verify(
12
12
  sequence([
13
- surround('[', focus(/^(?:\\[^\n]|[^\\\[\]\n])+(?=]\()/, text), ']'),
14
- surround('(', focus(/^(?:\\[^\n]|[^\\\(\)\n])+(?=\))/, text), ')'),
13
+ surround('[', focus(/^(?:\\[^\n]|[^\\[\](){}"\n])+(?=]\()/, text), ']'),
14
+ surround('(', focus(/^(?:\\[^\n]|[^\\[\](){}"\n])+(?=\))/, text), ')'),
15
15
  ]),
16
16
  ([texts]) => isStartTightNodes(texts)),
17
- ([texts, rubies], rest) => {
18
- const tail = typeof texts[texts.length - 1] === 'object'
19
- ? [texts.pop()!]
20
- : [];
21
- tail.length === 0 && texts[texts.length - 1] === '' && texts.pop();
17
+ ([texts, rubies]) => {
18
+ texts[texts.length - 1] === '' && texts.pop();
22
19
  switch (true) {
23
20
  case rubies.length <= texts.length:
24
- return [[html('ruby', attributes(texts, rubies), defrag(push(texts
25
- .reduce((acc, _, i) =>
26
- push(acc, unshift([texts[i]],
27
- i < rubies.length && rubies[i]
28
- ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
29
- : [html('rt')]))
30
- , []), tail)))], rest];
21
+ return [
22
+ html('ruby', attributes(texts, rubies), defrag(texts
23
+ .reduce((acc, _, i) =>
24
+ push(acc, unshift([texts[i]],
25
+ i < rubies.length && rubies[i]
26
+ ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
27
+ : [html('rt')]))
28
+ , []))),
29
+ ];
31
30
  case texts.length === 1 && [...texts[0]].length >= rubies.length:
32
- return [[html('ruby', attributes(texts, rubies), defrag(push([...texts[0]]
33
- .reduce((acc, _, i, texts) =>
34
- push(acc, unshift([texts[i]],
35
- i < rubies.length && rubies[i]
36
- ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
37
- : [html('rt')]))
38
- , []), tail)))], rest];
31
+ return [
32
+ html('ruby', attributes(texts, rubies), defrag([...texts[0]]
33
+ .reduce((acc, _, i, texts) =>
34
+ push(acc, unshift([texts[i]],
35
+ i < rubies.length && rubies[i]
36
+ ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
37
+ : [html('rt')]))
38
+ , []))),
39
+ ];
39
40
  default:
40
41
  assert(rubies.length > 0);
41
- return [[html('ruby', attributes(texts, rubies), defrag(push(unshift(
42
- [texts.join(' ')],
43
- [html('rp', '('), html('rt', rubies.join(' ').trim()), html('rp', ')')]), tail)))
44
- ], rest];
42
+ return [
43
+ html('ruby', attributes(texts, rubies), defrag(unshift(
44
+ [texts.join(' ')],
45
+ [html('rp', '('), html('rt', rubies.join(' ').trim()), html('rp', ')')]))),
46
+ ];
45
47
  }
46
48
  }))));
47
49
 
@@ -1,5 +1,5 @@
1
1
  import { StrongParser } from '../inline';
2
- import { union, some, creator, surround, open, lazy } from '../../combinator';
2
+ import { union, some, precedence, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { emstrong } from './emstrong';
5
5
  import { str } from '../source';
@@ -7,7 +7,7 @@ import { startTight, blankWith } from '../util';
7
7
  import { html, defrag } from 'typed-dom/dom';
8
8
  import { unshift } from 'spica/array';
9
9
 
10
- export const strong: StrongParser = lazy(() => creator(surround(
10
+ export const strong: StrongParser = lazy(() => creator(precedence(1, surround(
11
11
  str('**'),
12
12
  startTight(some(union([
13
13
  some(inline, blankWith('**')),
@@ -18,4 +18,4 @@ export const strong: StrongParser = lazy(() => creator(surround(
18
18
  ])), '*'),
19
19
  str('**'), false,
20
20
  ([, bs], rest) => [[html('strong', defrag(bs))], rest],
21
- ([as, bs], rest) => [unshift(as, bs), rest])));
21
+ ([as, bs], rest) => [unshift(as, bs), rest]))));
@@ -1,17 +1,17 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { TemplateParser } from '../inline';
3
- import { union, some, rewrite, creator, surround, lazy } from '../../combinator';
3
+ import { union, some, rewrite, precedence, creator, surround, lazy } from '../../combinator';
4
4
  import { escsource, str } from '../source';
5
5
  import { html } from 'typed-dom/dom';
6
6
  import { unshift } from 'spica/array';
7
7
 
8
- export const template: TemplateParser = lazy(() => creator(rewrite(
8
+ export const template: TemplateParser = lazy(() => creator(precedence(3, rewrite(
9
9
  surround('{{', some(union([bracket, escsource]), '}'), '}}', true),
10
- source => [[html('span', { class: 'template' }, source.replace(/\x1B/g, ''))], ''])));
10
+ source => [[html('span', { class: 'template' }, source.replace(/\x1B/g, ''))], '']))));
11
11
 
12
12
  const bracket: TemplateParser.BracketParser = lazy(() => union([
13
13
  surround(str('('), some(union([bracket, escsource]), ')'), str(')'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
14
14
  surround(str('['), some(union([bracket, escsource]), ']'), str(']'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
15
15
  surround(str('{'), some(union([bracket, escsource]), '}'), str('}'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
16
- surround(str('"'), some(escsource, /^"|^\\?\n/), str('"'), true),
16
+ surround(str('"'), precedence(8, some(escsource, /^"|^\\?\n/)), str('"'), true),
17
17
  ]));
@@ -142,27 +142,30 @@ describe('Unit: parser/inline', () => {
142
142
  assert.deepStrictEqual(inspect(parser('${{{a}}}')), [['$', '<span class="template">{{{a}}}</span>'], '']);
143
143
  assert.deepStrictEqual(inspect(parser('Di$ney Micro$oft')), [['Di', '$', 'ney', ' ', 'Micro', '$', 'oft'], '']);
144
144
  assert.deepStrictEqual(inspect(parser('Di$ney, Micro$oft')), [['Di', '$', 'ney', ',', ' ', 'Micro', '$', 'oft'], '']);
145
- assert.deepStrictEqual(inspect(parser('(((a))')), [['(', '<sup class="annotation"><span>a</span></sup>'], '']);
146
- assert.deepStrictEqual(inspect(parser('((((a))')), [['(', '(', '<sup class="annotation"><span>a</span></sup>'], '']);
145
+ assert.deepStrictEqual(inspect(parser('(((a))')), [['', '(', '<sup class="annotation"><span>a</span></sup>'], '']);
146
+ assert.deepStrictEqual(inspect(parser('((((a))')), [['((', '<sup class="annotation"><span>a</span></sup>'], '']);
147
147
  assert.deepStrictEqual(inspect(parser('((((a))))')), [['<sup class="annotation"><span><span class="paren">((a))</span></span></sup>'], '']);
148
+ assert.deepStrictEqual(inspect(parser('((<bdi>))')), [['<sup class="annotation"><span><span class="invalid">&lt;bdi&gt;</span></span></sup>'], '']);
148
149
  assert.deepStrictEqual(inspect(parser('"((""))')), [['"', '<sup class="annotation"><span>""</span></sup>'], '']);
149
- assert.deepStrictEqual(inspect(parser('[[[a]]')), [['[', '<sup class="reference"><span>a</span></sup>'], '']);
150
- assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['[', '[', '<sup class="reference"><span>a</span></sup>'], '']);
150
+ assert.deepStrictEqual(inspect(parser('[[[a]]')), [['', '[', '<sup class="reference"><span>a</span></sup>'], '']);
151
+ assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['[[', '<sup class="reference"><span>a</span></sup>'], '']);
151
152
  assert.deepStrictEqual(inspect(parser('[[[[a]]]]')), [['<sup class="reference"><span>[[a]]</span></sup>'], '']);
152
153
  assert.deepStrictEqual(inspect(parser('[[[$-1]]]')), [['<sup class="reference"><span><a class="label" data-label="$-1">$-1</a></span></sup>'], '']);
153
154
  assert.deepStrictEqual(inspect(parser('[[[]{a}]]')), [['<sup class="reference"><span><a href="a">a</a></span></sup>'], '']);
154
155
  assert.deepStrictEqual(inspect(parser('[[[a]{b}]]')), [['<sup class="reference"><span><a href="b">a</a></span></sup>'], '']);
155
156
  assert.deepStrictEqual(inspect(parser('[(([a]{#}))]{#}')), [['<a href="#"><span class="paren">(<span class="paren">([a]{#})</span>)</span></a>'], '']);
157
+ assert.deepStrictEqual(inspect(parser('[[<bdi>]]')), [['<sup class="reference"><span><span class="invalid">&lt;bdi&gt;</span></span></sup>'], '']);
156
158
  assert.deepStrictEqual(inspect(parser('"[[""]]')), [['"', '<sup class="reference"><span>""</span></sup>'], '']);
159
+ assert.deepStrictEqual(inspect(parser('[[a](b)]{c}')), [['<a href="c"><ruby>a<rp>(</rp><rt>b</rt><rp>)</rp></ruby></a>'], '']);
157
160
  assert.deepStrictEqual(inspect(parser('<http://host>')), [['<', '<a href="http://host" target="_blank">http://host</a>', '>'], '']);
158
- assert.deepStrictEqual(inspect(parser('[~http://host')), [['[', '~', '<a href="http://host" target="_blank">http://host</a>'], '']);
159
- assert.deepStrictEqual(inspect(parser('[~a@b')), [['[', '~', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
161
+ assert.deepStrictEqual(inspect(parser('[~http://host')), [['', '[', '~', '<a href="http://host" target="_blank">http://host</a>'], '']);
162
+ assert.deepStrictEqual(inspect(parser('[~a@b')), [['', '[', '~', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
160
163
  assert.deepStrictEqual(inspect(parser('[~~a~~]')), [['[', '<del>a</del>', ']'], '']);
161
- assert.deepStrictEqual(inspect(parser('[^http://host')), [['[', '^', '<a href="http://host" target="_blank">http://host</a>'], '']);
162
- assert.deepStrictEqual(inspect(parser('[^a@b')), [['[', '^', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
164
+ assert.deepStrictEqual(inspect(parser('[^http://host')), [['[^', '<a href="http://host" target="_blank">http://host</a>'], '']);
165
+ assert.deepStrictEqual(inspect(parser('[^a@b')), [['[^', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
163
166
  assert.deepStrictEqual(inspect(parser('[#a*b\nc*]')), [['[', '<a href="/hashtags/a" class="hashtag">#a</a>', '<em>b<br>c</em>', ']'], '']);
164
167
  assert.deepStrictEqual(inspect(parser('[*a\nb*]{/}')), [['[', '<em>a<br>b</em>', ']', '<a href="/">/</a>'], '']);
165
- assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[', '[', '<em>a<br>b</em>', ']', ']'], '']);
168
+ assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[[', '<em>a<br>b</em>', ']', ']'], '']);
166
169
  assert.deepStrictEqual(inspect(parser('"[% *"*"*')), [['"', '[%', ' ', '*', '"', '*', '"', '*'], '']);
167
170
  assert.deepStrictEqual(inspect(parser('"[% "*"* %]')), [['"', '[%', ' ', '"', '*', '"', '*', ' ', '%', ']'], '']);
168
171
  });
@@ -228,7 +231,7 @@ describe('Unit: parser/inline', () => {
228
231
  assert.deepStrictEqual(inspect(parser('*a*#b')), [['<em>a</em>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
229
232
  assert.deepStrictEqual(inspect(parser('((a))#b')), [['<sup class="annotation"><span>a</span></sup>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
230
233
  assert.deepStrictEqual(inspect(parser('[[a]]#b')), [['<sup class="reference"><span>a</span></sup>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
231
- assert.deepStrictEqual(inspect(parser('[#a')), [['[', '<a href="/hashtags/a" class="hashtag">#a</a>'], '']);
234
+ assert.deepStrictEqual(inspect(parser('[#a')), [['', '[', '<a href="/hashtags/a" class="hashtag">#a</a>'], '']);
232
235
  assert.deepStrictEqual(inspect(parser('|#a')), [['|', '<a href="/hashtags/a" class="hashtag">#a</a>'], '']);
233
236
  assert.deepStrictEqual(inspect(parser(' #a')), [[' ', '<a href="/hashtags/a" class="hashtag">#a</a>'], '']);
234
237
  });
@@ -5,9 +5,25 @@ import { union, some, verify, convert, fmap } from '../combinator';
5
5
  import { unsafehtmlentity } from './inline/htmlentity';
6
6
  import { linebreak, unescsource } from './source';
7
7
  import { invisibleHTMLEntityNames } from './api/normalize';
8
- import { reduce } from 'spica/memoize';
8
+ import { memoize, reduce } from 'spica/memoize';
9
9
  import { push } from 'spica/array';
10
10
 
11
+ export function clean<P extends Parser<unknown>>(parser: P): P;
12
+ export function clean<T>(parser: Parser<T, MarkdownParser.Context>): Parser<T, MarkdownParser.Context> {
13
+ const clean = memoize<MarkdownParser.Context, MarkdownParser.Context>(context => ({
14
+ resources: context.resources,
15
+ precedence: context.precedence,
16
+ delimiters: context.delimiters,
17
+ host: context.host,
18
+ url: context.url,
19
+ id: context.id,
20
+ header: context.header,
21
+ cache: context.caches,
22
+ }), new WeakMap());
23
+ return (source, context) =>
24
+ parser(source, context.syntax ? clean(context) : context);
25
+ }
26
+
11
27
  export const regBlankStart = new RegExp(
12
28
  /^(?:\\?[^\S\n]|&IHN;|<wbr>)+/.source.replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`));
13
29
 
@@ -183,7 +199,7 @@ export function trimBlankEnd<T extends HTMLElement | string>(parser: Parser<T>):
183
199
  // }
184
200
  // return nodes;
185
201
  //}
186
- function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
202
+ export function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
187
203
  const skip = nodes.length > 0 &&
188
204
  typeof nodes[nodes.length - 1] === 'object' &&
189
205
  nodes[nodes.length - 1]['className'] === 'indexer'