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
@@ -17,11 +17,11 @@ describe('Unit: parser/inline/annotation', () => {
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);
@@ -42,6 +42,7 @@ describe('Unit: parser/inline/annotation', () => {
42
42
  });
43
43
 
44
44
  it('nest', () => {
45
+ assert.deepStrictEqual(inspect(parser('((<bdi>))')), [['<sup class="annotation"><span><span class="invalid">&lt;bdi&gt;</span></span></sup>'], '']);
45
46
  assert.deepStrictEqual(inspect(parser('((`a`))')), [['<sup class="annotation"><span><code data-src="`a`">a</code></span></sup>'], '']);
46
47
  assert.deepStrictEqual(inspect(parser('((@a))')), [['<sup class="annotation"><span><a href="/@a" class="account">@a</a></span></sup>'], '']);
47
48
  assert.deepStrictEqual(inspect(parser('((http://host))')), [['<sup class="annotation"><span><a href="http://host" target="_blank">http://host</a></span></sup>'], '']);
@@ -1,11 +1,12 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { AnnotationParser } from '../inline';
3
- import { union, some, validate, guard, context, creator, surround, lazy, fmap } from '../../combinator';
3
+ import { union, some, validate, guard, context, precedence, creator, recursion, surround, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
- import { trimBlank } from '../util';
5
+ import { optimize } from './reference';
6
+ import { trimBlankStart, trimNodeEnd } from '../util';
6
7
  import { html, defrag } from 'typed-dom/dom';
7
8
 
8
- export const annotation: AnnotationParser = lazy(() => creator(validate('((', '))', '\n', fmap(surround(
9
+ export const annotation: AnnotationParser = lazy(() => creator(recursion(precedence(6, validate('((', surround(
9
10
  '((',
10
11
  guard(context => context.syntax?.inline?.annotation ?? true,
11
12
  context({ syntax: { inline: {
@@ -19,6 +20,8 @@ export const annotation: AnnotationParser = lazy(() => creator(validate('((', ')
19
20
  //link: true,
20
21
  //autolink: true,
21
22
  }}, delimiters: undefined },
22
- trimBlank(some(union([inline]), ')', /^\\?\n/)))),
23
- '))'),
24
- ns => [html('sup', { class: 'annotation' }, [html('span', defrag(ns))])]))));
23
+ trimBlankStart(some(union([inline]), ')', [[/^\\?\n/, 9], [')', 3], ['))', 6]])))),
24
+ '))',
25
+ false,
26
+ ([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNodeEnd(defrag(ns)))])], rest],
27
+ ([, ns, rest], next) => next[0] === ')' ? undefined : optimize('((', ns, rest)))))));
@@ -1,7 +1,8 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, some, validate, focus, rewrite, context, creator, convert, surround, open, lazy } from '../../../combinator';
2
+ import { union, some, validate, focus, rewrite, precedence, creator, convert, surround, open, lazy } from '../../../combinator';
3
3
  import { link } from '../link';
4
4
  import { unescsource } from '../../source';
5
+ import { clean } from '../../util';
5
6
 
6
7
  const closer = /^[-+*=~^,.;:!?]*(?=["`|\[\](){}<>]|\\?$)/;
7
8
 
@@ -9,14 +10,13 @@ export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'ht
9
10
  open(
10
11
  /^https?:\/\/(?=[\x21-\x7E])/,
11
12
  focus(/^[\x21-\x7E]+/, some(union([bracket, some(unescsource, closer)])))),
12
- context({ syntax: { inline: { link: true } } },
13
13
  convert(
14
14
  url => `{ ${url} }`,
15
- union([link]))))));
15
+ clean(union([link]))))));
16
16
 
17
- const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creator(union([
17
+ const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creator(precedence(3, union([
18
18
  surround('(', some(union([bracket, unescsource]), ')'), ')', true),
19
19
  surround('[', some(union([bracket, unescsource]), ']'), ']', true),
20
20
  surround('{', some(union([bracket, unescsource]), '}'), '}', true),
21
- surround('"', some(unescsource, '"'), '"', true),
22
- ])));
21
+ surround('"', precedence(8, some(unescsource, '"')), '"', true),
22
+ ]))));
@@ -7,11 +7,9 @@ describe('Unit: parser/inline/bracket', () => {
7
7
  const parser = (source: string) => some(bracket)(source, {});
8
8
 
9
9
  it('(', () => {
10
- assert.deepStrictEqual(inspect(parser('(')), [['('], '']);
10
+ assert.deepStrictEqual(inspect(parser('(')), [['', '('], '']);
11
11
  assert.deepStrictEqual(inspect(parser('()')), [['<span class="paren">()</span>'], '']);
12
- assert.deepStrictEqual(inspect(parser('(a')), [['(', 'a'], '']);
13
- assert.deepStrictEqual(inspect(parser('(\\a)')), [['<span class="paren">(a)</span>'], '']);
14
- assert.deepStrictEqual(inspect(parser(')')), undefined);
12
+ assert.deepStrictEqual(inspect(parser('(a')), [['', '(', 'a'], '']);
15
13
  assert.deepStrictEqual(inspect(parser('(0)')), [['(', '0', ')'], '']);
16
14
  assert.deepStrictEqual(inspect(parser('(1)')), [['(', '1', ')'], '']);
17
15
  assert.deepStrictEqual(inspect(parser('(10)')), [['(', '10', ')'], '']);
@@ -39,6 +37,9 @@ describe('Unit: parser/inline/bracket', () => {
39
37
  assert.deepStrictEqual(inspect(parser('(Name, Name)')), [['(', 'Name, Name', ')'], '']);
40
38
  assert.deepStrictEqual(inspect(parser('(ABBR)')), [['(', 'ABBR', ')'], '']);
41
39
  assert.deepStrictEqual(inspect(parser('(ABBR, ABBR)')), [['(', 'ABBR, ABBR', ')'], '']);
40
+ assert.deepStrictEqual(inspect(parser('(\\a)')), [['<span class="paren">(a)</span>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('(==)')), [['<span class="paren">(==)</span>'], '']);
42
+ assert.deepStrictEqual(inspect(parser(')')), undefined);
42
43
  assert.deepStrictEqual(inspect(parser('(1,2)')), [['(', '1,2', ')'], '']);
43
44
  assert.deepStrictEqual(inspect(parser('(0-1)')), [['(', '0-1', ')'], '']);
44
45
  assert.deepStrictEqual(inspect(parser('(0.1)')), [['(', '0.1', ')'], '']);
@@ -49,10 +50,11 @@ describe('Unit: parser/inline/bracket', () => {
49
50
  });
50
51
 
51
52
  it('[', () => {
52
- assert.deepStrictEqual(inspect(parser('[')), [['['], '']);
53
+ assert.deepStrictEqual(inspect(parser('[')), [['', '['], '']);
53
54
  assert.deepStrictEqual(inspect(parser('[]')), [['[', ']'], '']);
54
- assert.deepStrictEqual(inspect(parser('[a')), [['[', 'a'], '']);
55
+ assert.deepStrictEqual(inspect(parser('[a')), [['', '[', 'a'], '']);
55
56
  assert.deepStrictEqual(inspect(parser('[a]')), [['[', 'a', ']'], '']);
57
+ assert.deepStrictEqual(inspect(parser('[==]')), [['[', '==', ']'], '']);
56
58
  assert.deepStrictEqual(inspect(parser(']')), undefined);
57
59
  });
58
60
 
@@ -61,6 +63,7 @@ describe('Unit: parser/inline/bracket', () => {
61
63
  assert.deepStrictEqual(inspect(parser('{}')), [['{', '}'], '']);
62
64
  assert.deepStrictEqual(inspect(parser('{a')), [['{', 'a'], '']);
63
65
  assert.deepStrictEqual(inspect(parser('{a}')), [['{', 'a', '}'], '']);
66
+ assert.deepStrictEqual(inspect(parser('{==}')), [['{', '==', '}'], '']);
64
67
  assert.deepStrictEqual(inspect(parser('}')), undefined);
65
68
  });
66
69
 
@@ -69,7 +72,8 @@ describe('Unit: parser/inline/bracket', () => {
69
72
  assert.deepStrictEqual(inspect(parser('""')), [['"', '"'], '']);
70
73
  assert.deepStrictEqual(inspect(parser('"a')), [['"', 'a'], '']);
71
74
  assert.deepStrictEqual(inspect(parser('"a"')), [['"', 'a', '"'], '']);
72
- assert.deepStrictEqual(inspect(parser('"(")"')), [['"', '(', '"'], ')"']);
75
+ assert.deepStrictEqual(inspect(parser('"(")"')), [['"', '', '(', '"'], ')"']);
76
+ assert.deepStrictEqual(inspect(parser('"(("')), [['"', '((', '"'], '']);
73
77
  assert.deepStrictEqual(inspect(parser('"(\\")"')), [['"', '<span class="paren">(")</span>', '"'], '']);
74
78
  assert.deepStrictEqual(inspect(parser('"(\n)"')), [['"', '<span class="paren">(<br>)</span>', '"'], '']);
75
79
  assert.deepStrictEqual(inspect(parser('"(\\\n)"')), [['"', '<span class="paren">(<span class="linebreak"> </span>)</span>', '"'], '']);
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { BracketParser } from '../inline';
3
- import { union, some, creator, surround, lazy } from '../../combinator';
3
+ import { union, some, precedence, creator, surround, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { str } from '../source';
6
6
  import { html, defrag } from 'typed-dom/dom';
@@ -8,23 +8,23 @@ import { unshift, push } from 'spica/array';
8
8
 
9
9
  const index = /^[0-9A-Za-z]+(?:(?:[.-]|, )[0-9A-Za-z]+)*/;
10
10
 
11
- export const bracket: BracketParser = lazy(() => creator(union([
12
- surround(str('('), str(index), str(')')),
13
- surround(str('('), some(inline, ')'), str(')'), true,
11
+ export const bracket: BracketParser = lazy(() => creator(0, union([
12
+ surround(str('('), precedence(3, str(index)), str(')')),
13
+ surround(str('('), precedence(3, some(inline, ')', [[')', 3]])), str(')'), true,
14
14
  ([as, bs = [], cs], rest) => [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))], rest],
15
- ([as, bs = []], rest) => [unshift(as, bs), rest]),
16
- surround(str('('), str(new RegExp(index.source.replace(', ', '[,、]').replace(/[09AZaz.]|\-(?!\w)/g, c => c.trimStart() && String.fromCharCode(c.charCodeAt(0) + 0xFEE0)))), str(')')),
17
- surround(str('('), some(inline, ')'), str(')'), true,
15
+ ([as, bs = []], rest) => [unshift([''], unshift(as, bs)), rest]),
16
+ surround(str('('), precedence(3, str(new RegExp(index.source.replace(', ', '[,、]').replace(/[09AZaz.]|\-(?!\w)/g, c => c.trimStart() && String.fromCharCode(c.charCodeAt(0) + 0xFEE0))))), str(')')),
17
+ surround(str('('), precedence(3, some(inline, ')', [[')', 3]])), str(')'), true,
18
18
  ([as, bs = [], cs], rest) => [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))], rest],
19
19
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
20
- surround(str('['), some(inline, ']'), str(']'), true,
20
+ surround(str('['), precedence(3, some(inline, ']', [[']', 3]])), str(']'), true,
21
21
  undefined,
22
- ([as, bs = []], rest) => [unshift(as, bs), rest]),
23
- surround(str('{'), some(inline, '}'), str('}'), true,
22
+ ([as, bs = []], rest) => [unshift([''], unshift(as, bs)), rest]),
23
+ surround(str('{'), precedence(3, some(inline, '}', [['}', 3]])), str('}'), true,
24
24
  undefined,
25
25
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
26
26
  // Control media blinking in editing rather than control confusion of pairs of quote marks.
27
- surround(str('"'), some(inline, '"', '"'), str('"'), true,
27
+ surround(str('"'), precedence(8, some(inline, '"', [['"', 8]])), str('"'), true,
28
28
  undefined,
29
29
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
30
30
  ])));
@@ -16,12 +16,12 @@ describe('Unit: parser/inline/comment', () => {
16
16
  assert.deepStrictEqual(inspect(parser('[% ')), [['[%'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('[% \n a')), [['[%', '<br>', ' ', 'a'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('[%%]')), undefined);
19
- assert.deepStrictEqual(inspect(parser('[% [%')), [['[%', ' ', '[', '%'], '']);
19
+ assert.deepStrictEqual(inspect(parser('[% [%')), [['[%', ' ', '', '[', '%'], '']);
20
20
  assert.deepStrictEqual(inspect(parser('[% [% ')), [['[%', ' ', '[%'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('[% [% a')), [['[%', ' ', '[%', ' ', 'a'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('[% [% a %]')), [['[%', ' ', '<span class="comment"><input type="checkbox"><span>[% a %]</span></span>'], '']);
23
- assert.deepStrictEqual(inspect(parser('[% a[%')), [['[%', ' ', 'a', '[', '%'], '']);
24
- assert.deepStrictEqual(inspect(parser('[% a [%')), [['[%', ' ', 'a', ' ', '[', '%'], '']);
23
+ assert.deepStrictEqual(inspect(parser('[% a[%')), [['[%', ' ', 'a', '', '[', '%'], '']);
24
+ assert.deepStrictEqual(inspect(parser('[% a [%')), [['[%', ' ', 'a', ' ', '', '[', '%'], '']);
25
25
  assert.deepStrictEqual(inspect(parser('[% a [% ')), [['[%', ' ', 'a', ' ', '[%'], '']);
26
26
  assert.deepStrictEqual(inspect(parser('[% a [% b')), [['[%', ' ', 'a', ' ', '[%', ' ', 'b'], '']);
27
27
  assert.deepStrictEqual(inspect(parser('[% a [%% b')), [['[%', ' ', 'a', ' ', '[%%', ' ', 'b'], '']);
@@ -55,6 +55,7 @@ describe('Unit: parser/inline/comment', () => {
55
55
  assert.deepStrictEqual(inspect(parser('[% &a; %]')), [['<span class="comment"><input type="checkbox"><span>[% <span class="invalid">&amp;a;</span> %]</span></span>'], '']);
56
56
  assert.deepStrictEqual(inspect(parser('[% &copy; %]')), [['<span class="comment"><input type="checkbox"><span>[% © %]</span></span>'], '']);
57
57
  assert.deepStrictEqual(inspect(parser('[% &amp;copy; %]')), [['<span class="comment"><input type="checkbox"><span>[% &amp;copy; %]</span></span>'], '']);
58
+ assert.deepStrictEqual(inspect(parser('[% [ %]')), [['<span class="comment"><input type="checkbox"><span>[% [ %]</span></span>'], '']);
58
59
  assert.deepStrictEqual(inspect(parser('[% \\ a %]')), [['<span class="comment"><input type="checkbox"><span>[% a %]</span></span>'], '']);
59
60
  });
60
61
 
@@ -1,18 +1,18 @@
1
1
  import { CommentParser } from '../inline';
2
- import { union, some, validate, creator, surround, open, close, match, lazy } from '../../combinator';
2
+ import { union, some, validate, precedence, creator, surround, open, close, match, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { text, str } from '../source';
5
5
  import { html, defrag } from 'typed-dom/dom';
6
6
  import { memoize } from 'spica/memoize';
7
7
  import { unshift, push } from 'spica/array';
8
8
 
9
- export const comment: CommentParser = lazy(() => creator(validate('[%', match(
9
+ export const comment: CommentParser = lazy(() => validate('[%', creator(precedence(4, match(
10
10
  /^\[(%+)\s/,
11
11
  memoize(
12
12
  ([, fence]) =>
13
13
  surround(
14
14
  open(str(`[${fence}`), some(text, new RegExp(String.raw`^\s+${fence}\]|^\S`)), true),
15
- some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`)),
15
+ some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`), [[new RegExp(String.raw`^\s+${fence}\]`), 4]]),
16
16
  close(some(text, /^\S/), str(`${fence}]`)), true,
17
17
  ([as, bs = [], cs], rest) => [[
18
18
  html('span', { class: 'comment' }, [
@@ -21,4 +21,4 @@ export const comment: CommentParser = lazy(() => creator(validate('[%', match(
21
21
  ]),
22
22
  ], rest],
23
23
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
24
- ([, fence]) => fence.length, [])))));
24
+ ([, fence]) => fence.length, []))))));
@@ -1,12 +1,12 @@
1
1
  import { DeletionParser } 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 { blankWith } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
- export const deletion: DeletionParser = lazy(() => creator(surround(
9
+ export const deletion: DeletionParser = lazy(() => creator(precedence(2, surround(
10
10
  str('~~'),
11
11
  some(union([
12
12
  some(inline, blankWith('\n', '~~')),
@@ -14,4 +14,4 @@ export const deletion: DeletionParser = lazy(() => creator(surround(
14
14
  ])),
15
15
  str('~~'), false,
16
16
  ([, bs], rest) => [[html('del', defrag(bs))], rest],
17
- ([as, bs], rest) => [unshift(as, bs), rest])));
17
+ ([as, bs], rest) => [unshift(as, bs), rest]))));
@@ -1,5 +1,5 @@
1
1
  import { EmphasisParser } 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 { strong } from './strong';
@@ -8,7 +8,7 @@ import { startTight, blankWith } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
  import { unshift } from 'spica/array';
10
10
 
11
- export const emphasis: EmphasisParser = lazy(() => creator(surround(
11
+ export const emphasis: EmphasisParser = lazy(() => creator(precedence(1, surround(
12
12
  str('*'),
13
13
  startTight(some(union([
14
14
  strong,
@@ -21,4 +21,4 @@ export const emphasis: EmphasisParser = lazy(() => creator(surround(
21
21
  ])), '*'),
22
22
  str('*'), false,
23
23
  ([, bs], rest) => [[html('em', defrag(bs))], rest],
24
- ([as, bs], rest) => [unshift(as, bs), rest])));
24
+ ([as, bs], rest) => [unshift(as, bs), rest]))));
@@ -1,7 +1,6 @@
1
- import { MarkdownParser } from '../../../markdown';
2
1
  import { EmStrongParser, EmphasisParser, StrongParser } from '../inline';
3
2
  import { Result, IntermediateParser } from '../../combinator/data/parser';
4
- import { union, some, creator, surround, open, lazy, bind } from '../../combinator';
3
+ import { union, some, precedence, creator, surround, open, lazy, bind } from '../../combinator';
5
4
  import { inline } from '../inline';
6
5
  import { strong } from './strong';
7
6
  import { emphasis } from './emphasis';
@@ -27,14 +26,14 @@ const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
27
26
  ])),
28
27
  ])));
29
28
 
30
- export const emstrong: EmStrongParser = lazy(() => creator(surround(
29
+ export const emstrong: EmStrongParser = lazy(() => creator(precedence(1, surround(
31
30
  str('***'),
32
31
  startTight(some(union([
33
32
  some(inline, blankWith('*')),
34
33
  open(some(inline, '*'), inline),
35
34
  ]))),
36
35
  str(/^\*{1,3}/), false,
37
- ([, bs, cs], rest, context): Result<HTMLElement | string, MarkdownParser.Context> => {
36
+ ([, bs, cs], rest, context): Result<HTMLElement | string, typeof context> => {
38
37
  assert(cs.length === 1);
39
38
  switch (cs[0]) {
40
39
  case '***':
@@ -58,4 +57,4 @@ export const emstrong: EmStrongParser = lazy(() => creator(surround(
58
57
  }
59
58
  assert(false);
60
59
  },
61
- ([as, bs], rest) => [unshift(as, bs), rest])));
60
+ ([as, bs], rest) => [unshift(as, bs), rest]))));
@@ -52,6 +52,7 @@ describe('Unit: parser/inline/extension/index', () => {
52
52
  assert.deepStrictEqual(inspect(parser('[#\\]]')), [['<a class="index" href="#index:]">]</a>'], '']);
53
53
  assert.deepStrictEqual(inspect(parser('[#\\\\]')), [['<a class="index" href="#index:\\">\\</a>'], '']);
54
54
  assert.deepStrictEqual(inspect(parser('[#A]')), [['<a class="index" href="#index:A">A</a>'], '']);
55
+ assert.deepStrictEqual(inspect(parser('[#==]')), [['<a class="index" href="#index:==">==</a>'], '']);
55
56
  assert.deepStrictEqual(inspect(parser('[#*A*]')), [['<a class="index" href="#index:A"><em>A</em></a>'], '']);
56
57
  assert.deepStrictEqual(inspect(parser('[#`A`]')), [['<a class="index" href="#index:`A`"><code data-src="`A`">A</code></a>'], '']);
57
58
  assert.deepStrictEqual(inspect(parser('[#${A}$]')), [['<a class="index" href="#index:${A}$"><span class="math" translate="no" data-src="${A}$">${A}$</span></a>'], '']);
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ExtensionParser } from '../../inline';
3
- import { union, some, validate, guard, context, creator, surround, open, lazy, fmap } from '../../../combinator';
3
+ import { union, some, validate, guard, context, precedence, creator, surround, open, lazy, fmap } from '../../../combinator';
4
4
  import { inline } from '../../inline';
5
5
  import { indexee, identity } from './indexee';
6
6
  import { txt, str, stropt } from '../../source';
@@ -9,7 +9,7 @@ import { html, define, defrag } from 'typed-dom/dom';
9
9
 
10
10
  import IndexParser = ExtensionParser.IndexParser;
11
11
 
12
- export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', fmap(indexee(fmap(surround(
12
+ export const index: IndexParser = lazy(() => validate('[#', creator(precedence(3, fmap(indexee(surround(
13
13
  '[#',
14
14
  guard(context => context.syntax?.inline?.index ?? true,
15
15
  startTight(
@@ -25,9 +25,10 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
25
25
  open(stropt(/^\|?/), trimBlankEnd(some(union([
26
26
  signature,
27
27
  inline,
28
- ]), ']', /^\\?\n/)), true)))),
29
- ']'),
30
- ns => [html('a', defrag(ns))])),
28
+ ]), ']', [[/^\\?\n/, 9], [']', 3]])), true)))),
29
+ ']',
30
+ false,
31
+ ([, ns], rest) => [[html('a', defrag(ns))], rest])),
31
32
  ([el]: [HTMLAnchorElement]) => [
32
33
  define(el,
33
34
  {
@@ -36,7 +37,7 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
36
37
  href: el.id ? `#${el.id}` : undefined,
37
38
  },
38
39
  el.childNodes),
39
- ]))));
40
+ ])))));
40
41
 
41
42
  const signature: IndexParser.SignatureParser = lazy(() => creator(fmap(open(
42
43
  '|#',
@@ -49,5 +50,5 @@ const bracket: IndexParser.SignatureParser.BracketParser = lazy(() => creator(un
49
50
  surround(str('('), some(union([bracket, txt]), ')'), str(')'), true),
50
51
  surround(str('['), some(union([bracket, txt]), ']'), str(']'), true),
51
52
  surround(str('{'), some(union([bracket, txt]), '}'), str('}'), true),
52
- surround(str('"'), some(txt, '"'), str('"'), true),
53
+ surround(str('"'), precedence(8, some(txt, '"')), str('"'), true),
53
54
  ])));
@@ -1,14 +1,12 @@
1
1
  import { ExtensionParser } from '../../inline';
2
- import { union, verify, focus, creator, context, surround, fmap } from '../../../combinator';
2
+ import { union, verify, focus, creator, surround, fmap } from '../../../combinator';
3
3
  import { index } from './index';
4
+ import { clean } from '../../util';
4
5
  import { html } from 'typed-dom/dom';
5
6
 
6
7
  export const indexer: ExtensionParser.IndexerParser = creator(fmap(verify(surround(
7
8
  /^\s+(?=\[#\S)/,
8
- context({ syntax: { inline: {
9
- index: true,
10
- }}},
11
- union([
9
+ clean(union([
12
10
  focus('[#]', () => [[html('a', { href: '#' })], '']),
13
11
  index,
14
12
  ])),
@@ -11,7 +11,7 @@ export const segment: ExtensionParser.LabelParser.SegmentParser = clear(validate
11
11
  body,
12
12
  ])));
13
13
 
14
- export const label: ExtensionParser.LabelParser = creator(validate(['[$', '$'], fmap(
14
+ export const label: ExtensionParser.LabelParser = validate(['[$', '$'], creator(fmap(
15
15
  guard(context => context.syntax?.inline?.label ?? true,
16
16
  union([
17
17
  surround('[', body, ']'),
@@ -21,13 +21,13 @@ describe('Unit: parser/inline/extension/placeholder', () => {
21
21
  assert.deepStrictEqual(inspect(parser('[^\na]')), undefined);
22
22
  assert.deepStrictEqual(inspect(parser('[^\\\na]')), undefined);
23
23
  assert.deepStrictEqual(inspect(parser('[^ !http://host]')), undefined);
24
- assert.deepStrictEqual(inspect(parser('[^a')), undefined);
25
- assert.deepStrictEqual(inspect(parser('[^a\n]')), undefined);
26
- assert.deepStrictEqual(inspect(parser('[^a\n\n]')), undefined);
27
- assert.deepStrictEqual(inspect(parser('[^a\\\n]')), undefined);
28
- assert.deepStrictEqual(inspect(parser('[^a\\\n\\\n]')), undefined);
29
- assert.deepStrictEqual(inspect(parser('[^a\nb]')), undefined);
30
- assert.deepStrictEqual(inspect(parser('[^a\\\nb]')), undefined);
24
+ assert.deepStrictEqual(inspect(parser('[^a')), [['[^', 'a'], '']);
25
+ assert.deepStrictEqual(inspect(parser('[^a\n]')), [['[^', 'a'], '\n]']);
26
+ assert.deepStrictEqual(inspect(parser('[^a\n\n]')), [['[^', 'a'], '\n\n]']);
27
+ assert.deepStrictEqual(inspect(parser('[^a\\\n]')), [['[^', 'a'], '\\\n]']);
28
+ assert.deepStrictEqual(inspect(parser('[^a\\\n\\\n]')), [['[^', 'a'], '\\\n\\\n]']);
29
+ assert.deepStrictEqual(inspect(parser('[^a\nb]')), [['[^', 'a'], '\nb]']);
30
+ assert.deepStrictEqual(inspect(parser('[^a\\\nb]')), [['[^', 'a'], '\\\nb]']);
31
31
  assert.deepStrictEqual(inspect(parser('[[]')), undefined);
32
32
  assert.deepStrictEqual(inspect(parser('[]]')), undefined);
33
33
  assert.deepStrictEqual(inspect(parser('[[]]')), undefined);
@@ -45,6 +45,7 @@ describe('Unit: parser/inline/extension/placeholder', () => {
45
45
  assert.deepStrictEqual(inspect(parser('[^a\\ \\ ]')), [['<span class="invalid">a </span>'], '']);
46
46
  assert.deepStrictEqual(inspect(parser('[^a<wbr>]')), [['<span class="invalid">a<wbr></span>'], '']);
47
47
  assert.deepStrictEqual(inspect(parser('[^a<wbr><wbr>]')), [['<span class="invalid">a<wbr><wbr></span>'], '']);
48
+ assert.deepStrictEqual(inspect(parser('[^==]')), [['<span class="invalid">==</span>'], '']);
48
49
  assert.deepStrictEqual(inspect(parser('[^a[% b %]]')), [['<span class="invalid">a<span class="comment"><input type="checkbox"><span>[% b %]</span></span></span>'], '']);
49
50
  assert.deepStrictEqual(inspect(parser('[^a[% b %][% c %]]')), [['<span class="invalid">a<span class="comment"><input type="checkbox"><span>[% b %]</span></span><span class="comment"><input type="checkbox"><span>[% c %]</span></span></span>'], '']);
50
51
  assert.deepStrictEqual(inspect(parser('[^\\]]')), [['<span class="invalid">]</span>'], '']);
@@ -1,5 +1,5 @@
1
1
  import { ExtensionParser } from '../../inline';
2
- import { union, some, validate, creator, surround, lazy } from '../../../combinator';
2
+ import { union, some, validate, precedence, creator, surround, lazy } from '../../../combinator';
3
3
  import { inline } from '../../inline';
4
4
  import { str } from '../../source';
5
5
  import { startTight } from '../../util';
@@ -10,9 +10,9 @@ import { unshift } from 'spica/array';
10
10
 
11
11
  // All syntax surrounded by square brackets shouldn't contain line breaks.
12
12
 
13
- export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => creator(validate(['[:', '[^'], ']', '\n', surround(
13
+ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => validate(['[:', '[^'], creator(precedence(3, surround(
14
14
  str(/^\[[:^]/),
15
- startTight(some(union([inline]), ']')),
15
+ startTight(some(union([inline]), ']', [[/^\\?\n/, 9], [']', 3]])),
16
16
  str(']'), false,
17
17
  ([as, bs], rest) => [[
18
18
  html('span', {
@@ -22,4 +22,4 @@ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => creator
22
22
  'data-invalid-message': `Reserved start symbol "${as[0][1]}" cannot be used in "[]"`,
23
23
  }, defrag(bs)),
24
24
  ], rest],
25
- ([as, bs], rest) => [unshift(as, bs), rest]))));
25
+ ([as, bs], rest) => [unshift(as, bs), rest])))));
@@ -25,6 +25,7 @@ describe('Unit: parser/inline/html', () => {
25
25
  assert.deepStrictEqual(inspect(parser('<a,b>')), undefined);
26
26
  assert.deepStrictEqual(inspect(parser('<a, b>')), undefined);
27
27
  assert.deepStrictEqual(inspect(parser('<T>')), undefined);
28
+ assert.deepStrictEqual(inspect(parser('<bdi>')), [['<span class="invalid">&lt;bdi&gt;</span>'], '']);
28
29
  assert.deepStrictEqual(inspect(parser('<bdi>z')), [['<span class="invalid">&lt;bdi&gt;z</span>'], '']);
29
30
  assert.deepStrictEqual(inspect(parser('<bdi></bdi>')), [['<span class="invalid">&lt;bdi&gt;&lt;/bdi&gt;</span>'], '']);
30
31
  assert.deepStrictEqual(inspect(parser('<bdi> </bdi>')), [['<span class="invalid">&lt;bdi&gt; &lt;/bdi&gt;</span>'], '']);
@@ -76,6 +77,7 @@ describe('Unit: parser/inline/html', () => {
76
77
  });
77
78
 
78
79
  it('nest', () => {
80
+ assert.deepStrictEqual(inspect(parser('<bdi>[% </bdi>')), [['<bdi>[% </bdi>'], '']);
79
81
  assert.deepStrictEqual(inspect(parser('<bdi><bdi>a</bdi></bdi>')), [['<bdi><bdi>a</bdi></bdi>'], '']);
80
82
  assert.deepStrictEqual(inspect(parser('<bdi>a<bdi>b</bdi>c</bdi>')), [['<bdi>a<bdi>b</bdi>c</bdi>'], '']);
81
83
  assert.deepStrictEqual(inspect(parser('<bdi>`a`</bdi>')), [['<bdi><code data-src="`a`">a</code></bdi>'], '']);
@@ -1,6 +1,6 @@
1
1
  import { undefined, Object } from 'spica/global';
2
2
  import { HTMLParser } from '../inline';
3
- import { union, subsequence, some, validate, focus, creator, surround, open, match, lazy } from '../../combinator';
3
+ import { union, subsequence, some, validate, focus, precedence, creator, surround, open, match, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { str } from '../source';
6
6
  import { isStartLooseNodes, blankWith } from '../util';
@@ -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(() => creator(validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/, union([
21
+ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/, creator(precedence(5, union([
22
22
  focus(
23
23
  '<wbr>',
24
24
  () => [[h('wbr')], '']),
@@ -34,7 +34,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
34
34
  str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
35
35
  subsequence([
36
36
  focus(/^[^\S\n]*\n/, some(inline)),
37
- some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`)), true)),
37
+ some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`), [[blankWith('\n', `</${tag}>`), 5]]), true)),
38
38
  ]),
39
39
  str(`</${tag}>`), true,
40
40
  ([as, bs = [], cs], rest) =>
@@ -50,7 +50,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
50
50
  str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
51
51
  subsequence([
52
52
  focus(/^[^\S\n]*\n/, some(inline)),
53
- some(inline, `</${tag}>`),
53
+ some(inline, `</${tag}>`, [[`</${tag}>`, 5]]),
54
54
  ]),
55
55
  str(`</${tag}>`), true,
56
56
  ([as, bs = [], cs], rest) =>
@@ -59,7 +59,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
59
59
  [[elem(tag, as, bs, [])], rest]),
60
60
  ([, tag]) => tag,
61
61
  new Cache(10000))),
62
- ])))));
62
+ ]))))));
63
63
 
64
64
  export const attribute: HTMLParser.TagParser.AttributeParser = union([
65
65
  str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/),
@@ -1,12 +1,12 @@
1
1
  import { InsertionParser } 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 { blankWith } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
- export const insertion: InsertionParser = lazy(() => creator(surround(
9
+ export const insertion: InsertionParser = lazy(() => creator(precedence(2, surround(
10
10
  str('++'),
11
11
  some(union([
12
12
  some(inline, blankWith('\n', '++')),
@@ -14,4 +14,4 @@ export const insertion: InsertionParser = lazy(() => creator(surround(
14
14
  ])),
15
15
  str('++'), false,
16
16
  ([, bs], rest) => [[html('ins', defrag(bs))], rest],
17
- ([as, bs], rest) => [unshift(as, bs), rest])));
17
+ ([as, bs], rest) => [unshift(as, bs), rest]))));
@@ -151,6 +151,7 @@ describe('Unit: parser/inline/link', () => {
151
151
  });
152
152
 
153
153
  it('nest', () => {
154
+ assert.deepStrictEqual(inspect(parser('[*]{/}')), [['<a href="/">*</a>'], '']);
154
155
  assert.deepStrictEqual(inspect(parser('[\\[]{/}')), [['<a href="/">[</a>'], '']);
155
156
  assert.deepStrictEqual(inspect(parser('[\\]]{/}')), [['<a href="/">]</a>'], '']);
156
157
  assert.deepStrictEqual(inspect(parser('[[]{a}]{a}')), [['<a href="a">[]{a}</a>'], '']);
@@ -1,12 +1,12 @@
1
1
  import { undefined, location, encodeURI, decodeURI, Location } from 'spica/global';
2
2
  import { LinkParser } from '../inline';
3
3
  import { eval } from '../../combinator/data/parser';
4
- import { union, inits, tails, some, validate, guard, context, creator, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
4
+ import { union, inits, tails, some, validate, guard, context, precedence, creator, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
5
5
  import { inline, media, shortmedia } from '../inline';
6
6
  import { attributes } from './html';
7
7
  import { autolink } from '../autolink';
8
8
  import { str } from '../source';
9
- import { trimBlank, stringify } from '../util';
9
+ import { trimBlankStart, trimNodeEnd, stringify } from '../util';
10
10
  import { html, define, defrag } from 'typed-dom/dom';
11
11
  import { ReadonlyURL } from 'spica/url';
12
12
 
@@ -15,7 +15,7 @@ const optspec = {
15
15
  } as const;
16
16
  Object.setPrototypeOf(optspec, null);
17
17
 
18
- export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}', '\n', bind(
18
+ export const link: LinkParser = lazy(() => validate(['[', '{'], creator(10, precedence(3, bind(
19
19
  guard(context => context.syntax?.inline?.link ?? true,
20
20
  reverse(tails([
21
21
  context({ syntax: { inline: {
@@ -36,7 +36,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
36
36
  media: false,
37
37
  autolink: false,
38
38
  }}},
39
- trimBlank(some(inline, ']', /^\\?\n/))),
39
+ trimBlankStart(some(inline, ']', [[/^\\?\n/, 9], [']', 3]]))),
40
40
  ']',
41
41
  true),
42
42
  ]))),
@@ -44,6 +44,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
44
44
  ]))),
45
45
  ([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
46
46
  assert(params.every(p => typeof p === 'string'));
47
+ content = trimNodeEnd(content);
47
48
  if (eval(some(autolink)(stringify(content), context))?.some(node => typeof node === 'object')) return;
48
49
  assert(!html('div', content).querySelector('a, .media, .annotation, .reference') || (content[0] as HTMLElement).matches('.media'));
49
50
  const INSECURE_URI = params.shift()!;
@@ -59,7 +60,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
59
60
  if (el.classList.contains('invalid')) return [[el], rest];
60
61
  assert(el.classList.length === 0);
61
62
  return [[define(el, attributes('link', [], optspec, params))], rest];
62
- }))));
63
+ })))));
63
64
 
64
65
  export const uri: LinkParser.ParameterParser.UriParser = union([
65
66
  open(/^[^\S\n]+/, str(/^\S+/)),
@@ -89,8 +90,8 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
89
90
  default:
90
91
  const target = new ReadonlyURL(uri, source.href);
91
92
  return target.origin === host.origin
92
- ? target.href.slice(target.origin.length)
93
- : target.href;
93
+ ? target.href.slice(target.origin.length)
94
+ : target.href;
94
95
  }
95
96
  }
96
97