securemark 0.293.5 → 0.294.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 (105) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/index.js +845 -534
  3. package/markdown.d.ts +13 -13
  4. package/package.json +3 -3
  5. package/src/combinator/control/constraint/block.test.ts +6 -6
  6. package/src/combinator/control/constraint/contract.ts +3 -3
  7. package/src/combinator/control/constraint/line.test.ts +7 -7
  8. package/src/combinator/control/constraint/line.ts +1 -1
  9. package/src/combinator/control/manipulation/clear.ts +2 -3
  10. package/src/combinator/control/manipulation/convert.ts +2 -2
  11. package/src/combinator/control/manipulation/duplicate.ts +4 -5
  12. package/src/combinator/control/manipulation/fence.ts +2 -2
  13. package/src/combinator/control/manipulation/indent.test.ts +2 -2
  14. package/src/combinator/control/manipulation/indent.ts +4 -4
  15. package/src/combinator/control/manipulation/reverse.ts +2 -2
  16. package/src/combinator/control/manipulation/scope.ts +3 -4
  17. package/src/combinator/control/manipulation/surround.ts +12 -13
  18. package/src/combinator/control/monad/bind.ts +6 -6
  19. package/src/combinator/control/monad/fmap.ts +7 -7
  20. package/src/combinator/data/data.ts +135 -0
  21. package/src/combinator/data/parser/context.test.ts +8 -8
  22. package/src/combinator/data/parser/context.ts +3 -3
  23. package/src/combinator/data/parser/inits.ts +6 -7
  24. package/src/combinator/data/parser/sequence.test.ts +3 -3
  25. package/src/combinator/data/parser/sequence.ts +6 -7
  26. package/src/combinator/data/parser/some.test.ts +3 -3
  27. package/src/combinator/data/parser/some.ts +4 -4
  28. package/src/combinator/data/parser/subsequence.test.ts +4 -4
  29. package/src/combinator/data/parser/subsequence.ts +3 -3
  30. package/src/combinator/data/parser/tails.ts +3 -3
  31. package/src/combinator/data/parser/union.test.ts +3 -3
  32. package/src/combinator/data/parser.ts +16 -7
  33. package/src/debug.test.ts +6 -5
  34. package/src/parser/api/bind.ts +4 -6
  35. package/src/parser/api/header.ts +1 -1
  36. package/src/parser/api/normalize.ts +1 -1
  37. package/src/parser/api/parse.ts +3 -1
  38. package/src/parser/block/blockquote.ts +6 -4
  39. package/src/parser/block/codeblock.ts +8 -7
  40. package/src/parser/block/dlist.ts +9 -8
  41. package/src/parser/block/extension/aside.ts +27 -21
  42. package/src/parser/block/extension/example.ts +29 -26
  43. package/src/parser/block/extension/fig.ts +1 -1
  44. package/src/parser/block/extension/figbase.ts +6 -5
  45. package/src/parser/block/extension/figure.ts +23 -19
  46. package/src/parser/block/extension/message.ts +35 -24
  47. package/src/parser/block/extension/placeholder.ts +17 -13
  48. package/src/parser/block/extension/table.ts +47 -40
  49. package/src/parser/block/heading.test.ts +3 -12
  50. package/src/parser/block/heading.ts +12 -8
  51. package/src/parser/block/ilist.ts +13 -12
  52. package/src/parser/block/mathblock.ts +21 -17
  53. package/src/parser/block/mediablock.ts +7 -5
  54. package/src/parser/block/olist.ts +15 -5
  55. package/src/parser/block/pagebreak.ts +2 -1
  56. package/src/parser/block/paragraph.ts +3 -1
  57. package/src/parser/block/reply/cite.ts +20 -15
  58. package/src/parser/block/reply/quote.ts +6 -4
  59. package/src/parser/block/reply.ts +6 -3
  60. package/src/parser/block/sidefence.ts +8 -7
  61. package/src/parser/block/table.ts +23 -22
  62. package/src/parser/block/ulist.ts +16 -12
  63. package/src/parser/block.ts +7 -6
  64. package/src/parser/header.ts +18 -18
  65. package/src/parser/inline/annotation.ts +3 -1
  66. package/src/parser/inline/autolink/account.ts +3 -2
  67. package/src/parser/inline/autolink/anchor.ts +3 -2
  68. package/src/parser/inline/autolink/channel.ts +5 -4
  69. package/src/parser/inline/autolink/email.ts +4 -3
  70. package/src/parser/inline/autolink/hashnum.ts +3 -2
  71. package/src/parser/inline/autolink/hashtag.ts +4 -3
  72. package/src/parser/inline/autolink/url.ts +7 -6
  73. package/src/parser/inline/bracket.ts +16 -15
  74. package/src/parser/inline/code.ts +5 -4
  75. package/src/parser/inline/deletion.ts +5 -5
  76. package/src/parser/inline/emphasis.ts +4 -3
  77. package/src/parser/inline/emstrong.test.ts +18 -18
  78. package/src/parser/inline/emstrong.ts +39 -30
  79. package/src/parser/inline/extension/index.ts +22 -19
  80. package/src/parser/inline/extension/indexee.ts +2 -2
  81. package/src/parser/inline/extension/indexer.ts +2 -1
  82. package/src/parser/inline/extension/label.ts +7 -3
  83. package/src/parser/inline/extension/placeholder.ts +6 -6
  84. package/src/parser/inline/html.ts +27 -28
  85. package/src/parser/inline/htmlentity.ts +9 -8
  86. package/src/parser/inline/insertion.ts +5 -5
  87. package/src/parser/inline/italic.ts +5 -5
  88. package/src/parser/inline/link.ts +36 -38
  89. package/src/parser/inline/mark.ts +7 -7
  90. package/src/parser/inline/math.ts +5 -4
  91. package/src/parser/inline/media.ts +33 -32
  92. package/src/parser/inline/reference.ts +19 -20
  93. package/src/parser/inline/remark.ts +11 -11
  94. package/src/parser/inline/ruby.ts +50 -53
  95. package/src/parser/inline/strong.ts +4 -3
  96. package/src/parser/inline/template.ts +16 -15
  97. package/src/parser/inline.test.ts +3 -3
  98. package/src/parser/segment.ts +3 -1
  99. package/src/parser/source/escapable.ts +9 -8
  100. package/src/parser/source/line.ts +4 -3
  101. package/src/parser/source/str.ts +2 -2
  102. package/src/parser/source/text.ts +9 -8
  103. package/src/parser/source/unescapable.ts +6 -5
  104. package/src/parser/util.ts +18 -13
  105. package/src/parser/visibility.ts +19 -20
@@ -1,6 +1,7 @@
1
1
  import { MathBlockParser } from '../block';
2
+ import { List, Data } from '../../combinator/data/parser';
2
3
  import { block, fence, clear, fmap } from '../../combinator';
3
- import { invalid } from '../util';
4
+ import { unwrap, invalid } from '../util';
4
5
  import { html } from 'typed-dom/dom';
5
6
 
6
7
  const opener = /(\${2,})(?!\$)([^\n]*)(?:$|\n)/y;
@@ -14,19 +15,22 @@ export const segment_: MathBlockParser.SegmentParser = block(
14
15
  export const mathblock: MathBlockParser = block(fmap(
15
16
  fence(opener, 300),
16
17
  // Bug: Type mismatch between outer and inner.
17
- ([body, overflow, closer, opener, delim, param]: string[], { caches: { math: cache = undefined } = {} }) => [
18
- delim.length === 2 && closer && !overflow && param.trimStart() === ''
19
- ? cache?.get(`${delim}\n${body}${delim}`)?.cloneNode(true) as HTMLDivElement ||
20
- html('div', { class: 'math', translate: 'no' }, `${delim}\n${body}${delim}`)
21
- : html('pre', {
22
- class: 'invalid',
23
- translate: 'no',
24
- ...invalid(
25
- 'mathblock',
26
- delim.length > 2 ? 'syntax' : !closer || overflow ? 'fence' : 'argument',
27
- delim.length > 2 ? 'Invalid syntax' :
28
- !closer ? `Missing the closing delimiter "${delim}"` :
29
- overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
30
- 'Invalid argument'),
31
- }, `${opener}${body}${overflow || closer}`),
32
- ]));
18
+ (nodes, { caches: { math: cache = undefined } = {} }) => {
19
+ const [body, overflow, closer, opener, delim, param] = unwrap<string>(nodes);
20
+ return new List([
21
+ delim.length === 2 && closer && !overflow && param.trimStart() === ''
22
+ ? new Data(cache?.get(`${delim}\n${body}${delim}`)?.cloneNode(true) as HTMLDivElement ||
23
+ html('div', { class: 'math', translate: 'no' }, `${delim}\n${body}${delim}`))
24
+ : new Data(html('pre', {
25
+ class: 'invalid',
26
+ translate: 'no',
27
+ ...invalid(
28
+ 'mathblock',
29
+ delim.length > 2 ? 'syntax' : !closer || overflow ? 'fence' : 'argument',
30
+ delim.length > 2 ? 'Invalid syntax' :
31
+ !closer ? `Missing the closing delimiter "${delim}"` :
32
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
33
+ 'Invalid argument'),
34
+ }, `${opener}${body}${overflow || closer}`)),
35
+ ]);
36
+ }));
@@ -1,7 +1,8 @@
1
1
  import { MediaBlockParser } from '../block';
2
+ import { List, Data } from '../../combinator/data/parser';
2
3
  import { union, inits, some, block, line, fallback, fmap } from '../../combinator';
3
4
  import { medialink, media, lineshortmedia } from '../inline';
4
- import { invalid } from '../util';
5
+ import { unwrap, invalid } from '../util';
5
6
  import { html } from 'typed-dom/dom';
6
7
 
7
8
  export const mediablock: MediaBlockParser = block(fmap(
@@ -15,9 +16,10 @@ export const mediablock: MediaBlockParser = block(fmap(
15
16
  medialink,
16
17
  media,
17
18
  lineshortmedia,
18
- ]), ({ context: { source } }) => [[html('span', {
19
+ ]), ({ context: { source } }) => new List([
20
+ new Data(html('span', {
19
21
  class: 'invalid',
20
22
  ...invalid('mediablock', 'syntax', 'Not media syntax'),
21
- }, source.replace('\n', ''))]]))),
22
- ]),
23
- ns => [html('div', ns)]));
23
+ }, source.replace('\n', '')))
24
+ ]))))]),
25
+ ns => new List([new Data(html('div', unwrap(ns)))])));
@@ -1,10 +1,11 @@
1
1
  import { OListParser } from '../block';
2
2
  import { Recursion } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, inits, subsequence, some, recursion, block, line, validate, indent, focus, open, match, fallback, lazy, fmap } from '../../combinator';
4
5
  import { ulist_, checkbox, fillFirstLine } from './ulist';
5
6
  import { ilist_, ilistitem } from './ilist';
6
7
  import { inline, indexee, indexer, dataindex } from '../inline';
7
- import { invalid } from '../util';
8
+ import { invalid, unwrap } from '../util';
8
9
  import { visualize, trimBlank } from '../visibility';
9
10
  import { memoize } from 'spica/memoize';
10
11
  import { html, define, defrag } from 'typed-dom/dom';
@@ -40,17 +41,26 @@ const list = (type: string, form: string): OListParser.ListParser => fmap(
40
41
  indent(union([ulist_, olist_, ilist_])),
41
42
  ]),
42
43
  ilistitem),
43
- ns => [html('li', { 'data-index': dataindex(ns), 'data-marker': ns.shift() as string || undefined }, defrag(fillFirstLine(ns)))])),
44
+ ns => new List([
45
+ new Data(html('li', {
46
+ 'data-index': dataindex(ns),
47
+ 'data-marker': ns.shift()?.value as string || undefined,
48
+ }, defrag(unwrap(fillFirstLine(ns)))))
49
+ ]))),
44
50
  ]))),
45
- es => [format(html('ol', es), type, form)]);
51
+ ns => new List([new Data(format(html('ol', unwrap(ns)), type, form))]));
46
52
 
47
53
  const heads = {
48
54
  '.': focus(
49
55
  openers['.'],
50
- ({ context: { source } }) => [[source.trimEnd().split('.', 1)[0] + '.']]),
56
+ ({ context: { source } }) => new List([
57
+ new Data(source.trimEnd().split('.', 1)[0] + '.')
58
+ ])),
51
59
  '(': focus(
52
60
  openers['('],
53
- ({ context: { source } }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')]]),
61
+ ({ context: { source } }) => new List([
62
+ new Data(source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)'))
63
+ ])),
54
64
  } as const;
55
65
 
56
66
  function idx(value: string): number {
@@ -1,7 +1,8 @@
1
1
  import { PagebreakParser } from '../block';
2
+ import { List, Data } from '../../combinator/data/parser';
2
3
  import { block, line, focus } from '../../combinator';
3
4
  import { html } from 'typed-dom/dom';
4
5
 
5
6
  export const pagebreak: PagebreakParser = block(line(focus(
6
7
  /={3,}[^\S\n]*(?:$|\n)/y,
7
- () => [[html('hr')]])));
8
+ () => new List([new Data(html('hr'))]))));
@@ -1,9 +1,11 @@
1
1
  import { ParagraphParser } from '../block';
2
+ import { List, Data } from '../../combinator/data/parser';
2
3
  import { union, some, block, fmap } from '../../combinator';
3
4
  import { inline } from '../inline';
4
5
  import { visualize, trimBlankEnd } from '../visibility';
6
+ import { unwrap } from '../util';
5
7
  import { html, defrag } from 'typed-dom/dom';
6
8
 
7
9
  export const paragraph: ParagraphParser = block(fmap(
8
10
  visualize(trimBlankEnd(some(union([inline])))),
9
- ns => [html('p', defrag(ns))]));
11
+ ns => new List([new Data(html('p', defrag(unwrap(ns))))])));
@@ -1,4 +1,5 @@
1
1
  import { ReplyParser } from '../../block';
2
+ import { List, Data } from '../../../combinator/data/parser';
2
3
  import { union, line, focus, open, fmap } from '../../../combinator';
3
4
  import { anchor } from '../../inline/autolink/anchor';
4
5
  import { str } from '../../source';
@@ -14,20 +15,24 @@ export const cite: ReplyParser.CiteParser = line(fmap(
14
15
  anchor,
15
16
  // Subject page representation.
16
17
  // リンクの実装は後で検討
17
- focus(/>>#\S*(?=\s*$)/y, ({ context: { source } }) => [[html('a', { class: 'anchor' }, source)]]),
18
- focus(/>>https?:\/\/\S+(?=\s*$)/y, ({ context: { source } }) => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)]]),
19
- focus(/>>.+(?=\s*$)/y, ({ context: { source } }) => [[source]]),
18
+ focus(/>>#\S*(?=\s*$)/y, ({ context: { source } }) => new List([new Data(html('a', { class: 'anchor' }, source))])),
19
+ focus(/>>https?:\/\/\S+(?=\s*$)/y, ({ context: { source } }) => new List([new Data(html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source))])),
20
+ focus(/>>.+(?=\s*$)/y, ({ context: { source } }) => new List([new Data(source)])),
20
21
  ])),
21
- ([quotes, node]: [string, HTMLElement | string]) => [
22
- html('span',
23
- typeof node === 'object'
24
- ? { class: 'cite' }
25
- : { class: 'cite invalid', ...invalid('cite', 'syntax', 'Invalid syntax') },
26
- defrag([
27
- `${quotes}>`,
22
+ nodes => {
23
+ const quotes = nodes.head!.value as string;
24
+ const node = nodes.last!.value;
25
+ return new List([
26
+ new Data(html('span',
28
27
  typeof node === 'object'
29
- ? define(node, { 'data-depth': `${quotes.length + 1}` }, node.innerText.slice(1))
30
- : node.slice(1),
31
- ])),
32
- html('br'),
33
- ]));
28
+ ? { class: 'cite' }
29
+ : { class: 'cite invalid', ...invalid('cite', 'syntax', 'Invalid syntax') },
30
+ defrag([
31
+ `${quotes}>`,
32
+ typeof node === 'object'
33
+ ? define(node, { 'data-depth': `${quotes.length + 1}` }, node.innerText.slice(1))
34
+ : node.slice(1),
35
+ ]))),
36
+ new Data(html('br')),
37
+ ]);
38
+ }));
@@ -1,8 +1,10 @@
1
1
  import { ReplyParser } from '../../block';
2
+ import { List, Data } from '../../../combinator/data/parser';
2
3
  import { union, some, block, validate, rewrite, convert, lazy, fmap } from '../../../combinator';
3
4
  import { math } from '../../inline/math';
4
5
  import { autolink } from '../../inline/autolink';
5
6
  import { linebreak, unescsource, anyline } from '../../source';
7
+ import { unwrap } from '../../util';
6
8
  import { html, defrag } from 'typed-dom/dom';
7
9
 
8
10
  export const syntax = />+[^\S\n]/y;
@@ -21,8 +23,8 @@ export const quote: ReplyParser.QuoteParser = lazy(() => block(fmap(
21
23
  unescsource,
22
24
  ])),
23
25
  false)),
24
- (ns: [string, ...(string | HTMLElement)[]], { source, position }) => [
25
- source[position - 1] === '\n' ? ns.pop() as HTMLBRElement : html('br'),
26
- html('span', { class: 'quote' }, defrag(ns)),
27
- ].reverse()),
26
+ (ns, { source, position }) => new List([
27
+ new Data(source[position - 1] === '\n' ? ns.pop()!.value as HTMLBRElement : html('br')),
28
+ new Data(html('span', { class: 'quote' }, defrag(unwrap(ns)))),
29
+ ].reverse())),
28
30
  false));
@@ -1,11 +1,12 @@
1
1
  import { ReplyParser } from '../block';
2
+ import { List, Data } from '../../combinator/data/parser';
2
3
  import { union, some, block, validate, rewrite, fmap } from '../../combinator';
3
4
  import { cite, syntax as csyntax } from './reply/cite';
4
5
  import { quote, syntax as qsyntax } from './reply/quote';
5
6
  import { inline } from '../inline';
6
7
  import { anyline } from '../source';
7
8
  import { visualize, trimBlankNodeEnd } from '../visibility';
8
- import { push } from 'spica/array';
9
+ import { unwrap } from '../util';
9
10
  import { html, defrag } from 'typed-dom/dom';
10
11
 
11
12
  const delimiter = new RegExp(`${csyntax.source}|${qsyntax.source}`, 'y');
@@ -17,6 +18,8 @@ export const reply: ReplyParser = block(validate(csyntax, fmap(
17
18
  rewrite(
18
19
  some(anyline, delimiter),
19
20
  visualize(fmap(some(inline), (ns, { source, position }) =>
20
- source[position - 1] === '\n' ? ns : push(ns, [html('br')])))),
21
+ source[position - 1] === '\n'
22
+ ? ns
23
+ : ns.push(new Data(html('br'))) && ns)))
21
24
  ])),
22
- ns => [html('p', trimBlankNodeEnd(defrag(ns)))])));
25
+ ns => new List([new Data(html('p', defrag(unwrap(trimBlankNodeEnd(ns)))))]))));
@@ -1,20 +1,21 @@
1
1
  import { SidefenceParser } from '../block';
2
2
  import { Recursion } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, some, recursion, block, focus, rewrite, convert, lazy, fmap } from '../../combinator';
4
5
  import { autolink } from '../autolink';
5
6
  import { contentline } from '../source';
6
- import { invalid } from '../util';
7
+ import { unwrap, invalid } from '../util';
7
8
  import { html, define, defrag } from 'typed-dom/dom';
8
9
 
9
10
  export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
10
11
  /(?=\|+(?:[^\S\n]|\n\|))(?:\|+(?:[^\S\n][^\n]*)?(?:$|\n))+$/y,
11
12
  union([source])),
12
- ([el]) => [
13
- define(el, {
13
+ ([{ value }]) => new List([
14
+ new Data(define(value, {
14
15
  class: 'invalid',
15
16
  ...invalid('sidefence', 'syntax', 'Reserved syntax'),
16
- }),
17
- ])));
17
+ })),
18
+ ]))));
18
19
 
19
20
  const opener = /(?=\|\|+(?:$|\s))/y;
20
21
  const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?:[^\S\n]|(?=\|*(?:$|\s)))|\n$/g, '');
@@ -26,6 +27,6 @@ const source: SidefenceParser.SourceParser = lazy(() => fmap(
26
27
  convert(unindent, source, false, true)),
27
28
  rewrite(
28
29
  some(contentline, opener),
29
- convert(unindent, fmap(autolink, ns => [html('pre', defrag(ns))]), false, true)),
30
+ convert(unindent, fmap(autolink, ns => new List([new Data(html('pre', defrag(unwrap(ns))))])), false, true)),
30
31
  ]))),
31
- ns => [html('blockquote', ns)]));
32
+ ns => new List([new Data(html('blockquote', unwrap(ns)))])));
@@ -1,9 +1,10 @@
1
1
  import { TableParser } from '../block';
2
+ import { List, Data } from '../../combinator/data/parser';
2
3
  import { union, sequence, some, block, line, validate, focus, rewrite, surround, open, close, fallback, lazy, fmap } from '../../combinator';
3
4
  import { inline, media, medialink, shortmedia } from '../inline';
4
5
  import { contentline } from '../source';
5
6
  import { trimBlank } from '../visibility';
6
- import { invalid } from '../util';
7
+ import { unwrap, invalid } from '../util';
7
8
  import { duffReduce } from 'spica/duff';
8
9
  import { push } from 'spica/array';
9
10
  import { html, defrag } from 'typed-dom/dom';
@@ -19,32 +20,32 @@ export const table: TableParser = lazy(() => block(fmap(validate(
19
20
  row(some(align), false),
20
21
  some(row(some(data), true)),
21
22
  ])),
22
- rows => [
23
- html('table', [
24
- html('thead', [rows.shift()!]),
25
- html('tbody', format(rows)),
26
- ]),
27
- ])));
23
+ rows => new List([
24
+ new Data(html('table', [
25
+ html('thead', [rows.shift()!.value]),
26
+ html('tbody', unwrap(format(rows))),
27
+ ])),
28
+ ]))));
28
29
 
29
30
  const row = <P extends CellParser | AlignParser>(parser: P, optional: boolean): RowParser<P> => fallback(fmap(
30
31
  line(surround(/(?=\|)/y, some(union([parser])), /[|\\]?\s*$/y, optional)),
31
- es => [html('tr', es)]),
32
- rewrite(contentline, ({ context: { source } }) => [[
33
- html('tr', {
32
+ ns => new List([new Data(html('tr', unwrap(ns)))])),
33
+ rewrite(contentline, ({ context: { source } }) => new List([
34
+ new Data(html('tr', {
34
35
  class: 'invalid',
35
36
  ...invalid('table-row', 'syntax', 'Missing the start symbol of the table row'),
36
- }, [html('td', source.replace('\n', ''))])
37
- ]]));
37
+ }, [html('td', source.replace('\n', ''))]))
38
+ ])));
38
39
 
39
40
  const align: AlignParser = fmap(open(
40
41
  '|',
41
42
  union([
42
43
  focus(/:-+:?/y, ({ context: { source } }) =>
43
- [[source.at(-1) === ':' ? 'center' : 'start']]),
44
+ new List([new Data(source.at(-1) === ':' ? 'center' : 'start')])),
44
45
  focus(/-+:?/y, ({ context: { source } }) =>
45
- [[source.at(-1) === ':' ? 'end' : '']]),
46
+ new List([new Data(source.at(-1) === ':' ? 'end' : '')])),
46
47
  ])),
47
- ns => [html('td', defrag(ns))]);
48
+ ns => new List([new Data(html('td', defrag(unwrap(ns))))]));
48
49
 
49
50
  const cell: CellParser = surround(
50
51
  /\|\s*(?=\S)/y,
@@ -58,18 +59,18 @@ const cell: CellParser = surround(
58
59
 
59
60
  const head: CellParser.HeadParser = fmap(
60
61
  cell,
61
- ns => [html('th', defrag(ns))]);
62
+ ns => new List([new Data(html('th', defrag(unwrap(ns))))]));
62
63
 
63
64
  const data: CellParser.DataParser = fmap(
64
65
  cell,
65
- ns => [html('td', defrag(ns))]);
66
+ ns => new List([new Data(html('td', defrag(unwrap(ns))))]));
66
67
 
67
- function format(rows: HTMLTableRowElement[]): HTMLTableRowElement[] {
68
- const aligns = rows[0].className === 'invalid'
68
+ function format(rows: List<Data<HTMLTableRowElement>>): List<Data<HTMLTableRowElement>> {
69
+ const aligns = rows.head!.value.className === 'invalid'
69
70
  ? []
70
- : duffReduce(rows.shift()!.children, (acc, el) => push(acc, [el.textContent!]), [] as string[]);
71
- for (let i = 0; i < rows.length; ++i) {
72
- for (let cols = rows[i].children, len = cols.length, j = 0; j < len; ++j) {
71
+ : duffReduce(rows.shift()!.value.children, (acc, el) => push(acc, [el.textContent!]), [] as string[]);
72
+ for (const { value: row } of rows) {
73
+ for (let cols = row.children, len = cols.length, j = 0; j < len; ++j) {
73
74
  if (j > 0 && !aligns[j]) {
74
75
  aligns[j] = aligns[j - 1];
75
76
  }
@@ -1,11 +1,12 @@
1
1
  import { UListParser } from '../block';
2
2
  import { Recursion } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, inits, subsequence, some, recursion, block, line, validate, indent, focus, open, fallback, lazy, fmap } from '../../combinator';
4
5
  import { olist_ } from './olist';
5
6
  import { ilist_, ilistitem } from './ilist';
6
7
  import { inline, indexer, indexee, dataindex } from '../inline';
7
8
  import { visualize, trimBlank } from '../visibility';
8
- import { unshift } from 'spica/array';
9
+ import { unwrap } from '../util';
9
10
  import { html, defrag } from 'typed-dom/dom';
10
11
 
11
12
  export const ulist: UListParser = lazy(() => block(validate(
@@ -24,22 +25,25 @@ export const ulist_: UListParser = lazy(() => block(fmap(validate(
24
25
  indent(union([ulist_, olist_, ilist_])),
25
26
  ]),
26
27
  ilistitem),
27
- ns => [html('li', { 'data-index': dataindex(ns) }, defrag(fillFirstLine(ns)))])),
28
+ ns => new List([new Data(html('li', { 'data-index': dataindex(ns) }, defrag(unwrap(fillFirstLine(ns)))))]))),
28
29
  ])))),
29
- es => [format(html('ul', es))])));
30
+ ns => new List([new Data(format(html('ul', unwrap(ns))))]))));
30
31
 
31
32
  export const checkbox = focus(
32
33
  /\[[xX ]\](?=$|[ \n])/y,
33
- ({ context: { source } }) => [[
34
- html('span', { class: 'checkbox' }, source[1].trimStart() ? '☑' : '☐'),
35
- ]]);
34
+ ({ context: { source } }) => new List([
35
+ new Data(html('span', { class: 'checkbox' }, source[1].trimStart() ? '☑' : '☐')),
36
+ ]));
36
37
 
37
- export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | string)[] {
38
- return ns.length === 1
39
- && typeof ns[0] === 'object'
40
- && ['UL', 'OL'].includes(ns[0].tagName)
41
- ? unshift([html('br')], ns)
42
- : ns;
38
+ export function fillFirstLine(nodes: List<Data<string | HTMLElement>>): List<Data<string | HTMLElement>> {
39
+ const node = nodes.head?.value;
40
+ if (typeof node !== 'object') return nodes;
41
+ switch (node.tagName) {
42
+ case 'UL':
43
+ case 'OL':
44
+ nodes.unshift(new Data(html('br')));
45
+ }
46
+ return nodes;
43
47
  }
44
48
 
45
49
  function format(list: HTMLUListElement): HTMLUListElement {
@@ -1,5 +1,6 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
2
  import { Recursion, Command } from './context';
3
+ import { List, Data } from '../combinator/data/parser';
3
4
  import { union, reset, open, fallback, recover } from '../combinator';
4
5
  import { MAX_SEGMENT_SIZE } from './segment';
5
6
  import { emptyline } from './source';
@@ -113,16 +114,16 @@ function error(parser: BlockParser): BlockParser {
113
114
  return recover<BlockParser>(fallback(
114
115
  open(Command.Error, ({ context: { source, position } }) => { throw new Error(source.slice(position).split('\n', 1)[0]); }),
115
116
  parser),
116
- ({ context: { source, position, id } }, reason) => [[
117
- html('h1',
117
+ ({ context: { source, position, id } }, reason) => new List([
118
+ new Data(html('h1',
118
119
  {
119
120
  id: id !== '' ? `error:${rnd0Z(8)}` : undefined,
120
121
  class: 'error',
121
122
  },
122
123
  reason instanceof Error
123
124
  ? `${reason.name}: ${reason.message}`
124
- : `UnknownError: ${reason}`),
125
- html('pre',
125
+ : `UnknownError: ${reason}`)),
126
+ new Data(html('pre',
126
127
  {
127
128
  class: 'error',
128
129
  translate: 'no',
@@ -130,6 +131,6 @@ function error(parser: BlockParser): BlockParser {
130
131
  source.slice(position)
131
132
  .replace(reg, '')
132
133
  .slice(0, 1001)
133
- .replace(/^(.{997}).{4}$/s, '$1...') || undefined),
134
- ]]);
134
+ .replace(/^(.{997}).{4}$/s, '$1...') || undefined)),
135
+ ]));
135
136
  }
@@ -1,8 +1,9 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
+ import { List, Data } from '../combinator/data/parser';
2
3
  import { union, inits, some, block, line, validate, focus, rewrite, clear, convert, lazy, fmap } from '../combinator';
3
4
  import { segment } from './segment';
4
5
  import { str } from './source';
5
- import { invalid } from './util';
6
+ import { unwrap, invalid } from './util';
6
7
  import { normalize } from './api/normalize';
7
8
  import { html, defrag } from 'typed-dom/dom';
8
9
 
@@ -18,7 +19,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
18
19
  else {
19
20
  context.position = source.length;
20
21
  }
21
- return [[]];
22
+ return new List();
22
23
  },
23
24
  block(
24
25
  union([
@@ -28,24 +29,23 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
28
29
  normalize(source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n'))).replace(/(\S)\s+$/mg, '$1'),
29
30
  fmap(
30
31
  some(union([field])),
31
- es => [
32
- html('aside', { class: 'header' }, [
33
- html('details', { open: '' }, defrag([
34
- html('summary', 'Header'),
35
- ...es,
36
- ])),
37
- ]),
38
- ]), false))),
32
+ ns => new List([
33
+ new Data(html('aside', { class: 'header' }, [
34
+ html('details',
35
+ { open: '' },
36
+ defrag(unwrap(ns.unshift(new Data(html('summary', 'Header'))) && ns))),
37
+ ])),
38
+ ])), false))),
39
39
  ({ context }) => {
40
40
  const { source, position } = context;
41
41
  context.position += source.length;
42
- return [[
43
- html('pre', {
42
+ return new List([
43
+ new Data(html('pre', {
44
44
  class: 'invalid',
45
45
  translate: 'no',
46
46
  ...invalid('header', 'syntax', 'Invalid syntax'),
47
- }, normalize(source.slice(position))),
48
- ]];
47
+ }, normalize(source.slice(position)))),
48
+ ]);
49
49
  },
50
50
  ]))),
51
51
  clear(str(/[^\S\v\f\r\n]*\r?\n/y)),
@@ -54,12 +54,12 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
54
54
  const field: MarkdownParser.HeaderParser.FieldParser = line(({ context: { source, position } }) => {
55
55
  const name = source.slice(position, source.indexOf(':', position));
56
56
  const value = source.slice(position + name.length + 1).trim();
57
- return [[
58
- html('span', { class: 'field', 'data-name': name.toLowerCase(), 'data-value': value }, [
57
+ return new List([
58
+ new Data(html('span', { class: 'field', 'data-name': name.toLowerCase(), 'data-value': value }, [
59
59
  html('span', { class: 'field-name' }, name),
60
60
  ': ',
61
61
  html('span', { class: 'field-value' }, value),
62
62
  '\n',
63
- ]),
64
- ]];
63
+ ])),
64
+ ]);
65
65
  });
@@ -1,8 +1,10 @@
1
1
  import { AnnotationParser } from '../inline';
2
2
  import { State, Backtrack } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, some, precedence, state, constraint, surround, lazy } from '../../combinator';
4
5
  import { inline } from '../inline';
5
6
  import { trimBlankStart, trimBlankNodeEnd } from '../visibility';
7
+ import { unwrap } from '../util';
6
8
  import { html, defrag } from 'typed-dom/dom';
7
9
 
8
10
  export const annotation: AnnotationParser = lazy(() => constraint(State.annotation, surround(
@@ -13,7 +15,7 @@ export const annotation: AnnotationParser = lazy(() => constraint(State.annotati
13
15
  false,
14
16
  ([, ns], context) =>
15
17
  context.linebreak === 0
16
- ? [[html('sup', { class: 'annotation' }, [html('span', defrag(trimBlankNodeEnd(ns)))])]]
18
+ ? new List([new Data(html('sup', { class: 'annotation' }, [html('span', defrag(unwrap(trimBlankNodeEnd(ns))))]))])
17
19
  : undefined,
18
20
  undefined,
19
21
  [1 | Backtrack.bracket, 3 | Backtrack.doublebracket])));
@@ -1,5 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
2
  import { State, Backtrack } from '../../context';
3
+ import { List, Data } from '../../../combinator/data/parser';
3
4
  import { union, tails, state, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
4
5
  import { unsafelink } from '../link';
5
6
  import { str } from '../../source';
@@ -26,6 +27,6 @@ export const account: AutolinkParser.AccountParser = lazy(() => rewrite(
26
27
  } }`,
27
28
  unsafelink,
28
29
  false),
29
- ([el]) => [define(el, { class: 'account' })]))),
30
- ({ context: { source } }) => [[source]],
30
+ ([{ value }]) => new List([new Data(define(value, { class: 'account' }))])))),
31
+ ({ context: { source } }) => new List([new Data(source)]),
31
32
  ])));
@@ -1,5 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
2
  import { State, Backtrack } from '../../context';
3
+ import { List, Data } from '../../../combinator/data/parser';
3
4
  import { union, state, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
4
5
  import { unsafelink } from '../link';
5
6
  import { str } from '../../source';
@@ -30,6 +31,6 @@ export const anchor: AutolinkParser.AnchorParser = lazy(() => rewrite(
30
31
  } }`,
31
32
  unsafelink,
32
33
  false),
33
- ([el]) => [define(el, { class: 'anchor' })]))),
34
- ({ context: { source } }) => [[source]],
34
+ ([{ value }]) => new List([new Data(define(value, { class: 'anchor' }))])))),
35
+ ({ context: { source } }) => new List([new Data(source)]),
35
36
  ])));
@@ -1,5 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
2
  import { State, Backtrack } from '../../context';
3
+ import { List, Data } from '../../../combinator/data/parser';
3
4
  import { union, tails, sequence, some, state, constraint, verify, rewrite, open, convert, fmap, lazy } from '../../../combinator';
4
5
  import { unsafelink } from '../link';
5
6
  import { emoji } from './hashtag';
@@ -24,7 +25,7 @@ export const channel: AutolinkParser.ChannelParser = lazy(() => rewrite(
24
25
  str(new RegExp([
25
26
  /(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^'\p{C}\p{S}\p{P}\s]|emoji))+(?![0-9a-z@]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
26
27
  ].join('').replace(/emoji/g, emoji), 'yu')),
27
- ([source]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(source)),
28
+ ([{ value }]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(value)),
28
29
  false,
29
30
  [3 | Backtrack.autolink])),
30
31
  ]),
@@ -38,10 +39,10 @@ export const channel: AutolinkParser.ChannelParser = lazy(() => rewrite(
38
39
  } }`,
39
40
  unsafelink,
40
41
  false),
41
- ([el], { source, position, range = 0 }) => {
42
+ ([{ value: el }], { source, position, range = 0 }) => {
42
43
  const src = source.slice(position - range, position);
43
44
  const url = `${el.getAttribute('href')}?ch=${src.slice(src.indexOf('#') + 1).replace(/#/g, '+')}`;
44
- return [define(el, { class: 'channel', href: url }, src)];
45
+ return new List([new Data(define(el, { class: 'channel', href: url }, src))]);
45
46
  }))),
46
- ({ context: { source } }) => [[source]],
47
+ ({ context: { source } }) => new List([new Data(source)]),
47
48
  ])));
@@ -1,5 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
2
  import { State, Backtrack } from '../../context';
3
+ import { List, Data } from '../../../combinator/data/parser';
3
4
  import { union, state, constraint, verify, rewrite, open } from '../../../combinator';
4
5
  import { str } from '../../source';
5
6
  import { html } from 'typed-dom/dom';
@@ -10,11 +11,11 @@ export const email: AutolinkParser.EmailParser = rewrite(
10
11
  open(/(?<![0-9a-z][_.+-]?)(?=[0-9a-z])/yi,
11
12
  verify(
12
13
  str(/[0-9a-z](?:[_.+-](?=[0-9a-z])|[0-9a-z]){0,255}@[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*(?![0-9a-z@#]|>>|:\S)/yi),
13
- ([source]) => source.length <= 255),
14
+ ([{ value }]) => value.length <= 255),
14
15
  false,
15
16
  [3 | Backtrack.autolink]),
16
17
  union([
17
18
  constraint(State.autolink, state(State.autolink,
18
- ({ context: { source } }) => [[html('a', { class: 'email', href: `mailto:${source}` }, source)]])),
19
- ({ context: { source } }) => [[source]],
19
+ ({ context: { source } }) => new List([new Data(html('a', { class: 'email', href: `mailto:${source}` }, source))]))),
20
+ ({ context: { source } }) => new List([new Data(source)]),
20
21
  ]));