securemark 0.250.0 → 0.253.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 (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/design.md +17 -11
  3. package/dist/index.js +120 -139
  4. package/index.d.ts +1 -1
  5. package/markdown.d.ts +8 -18
  6. package/package.json +1 -1
  7. package/src/combinator/control/manipulation/indent.ts +15 -12
  8. package/src/combinator/control/manipulation/match.ts +2 -3
  9. package/src/combinator/data/parser.ts +1 -1
  10. package/src/parser/api/bind.test.ts +6 -6
  11. package/src/parser/api/parse.test.ts +8 -1
  12. package/src/parser/api/parse.ts +1 -1
  13. package/src/parser/block/blockquote.test.ts +31 -31
  14. package/src/parser/block/blockquote.ts +1 -3
  15. package/src/parser/block/extension/aside.test.ts +3 -3
  16. package/src/parser/block/extension/aside.ts +0 -3
  17. package/src/parser/block/extension/example.test.ts +11 -11
  18. package/src/parser/block/extension/example.ts +1 -3
  19. package/src/parser/block/extension/fig.test.ts +5 -5
  20. package/src/parser/block/extension/figure.test.ts +2 -2
  21. package/src/parser/block/extension/figure.ts +2 -4
  22. package/src/parser/block/extension/message.ts +0 -2
  23. package/src/parser/block/ilist.ts +4 -5
  24. package/src/parser/block/olist.ts +26 -22
  25. package/src/parser/block/paragraph.test.ts +3 -3
  26. package/src/parser/block/ulist.ts +3 -13
  27. package/src/parser/block.ts +0 -3
  28. package/src/parser/inline/annotation.test.ts +18 -18
  29. package/src/parser/inline/annotation.ts +1 -1
  30. package/src/parser/inline/autolink/hashnum.ts +1 -1
  31. package/src/parser/inline/autolink/hashtag.ts +5 -5
  32. package/src/parser/inline/autolink.ts +2 -2
  33. package/src/parser/inline/code.ts +1 -1
  34. package/src/parser/inline/comment.ts +1 -1
  35. package/src/parser/inline/extension/index.ts +4 -5
  36. package/src/parser/inline/html.ts +3 -3
  37. package/src/parser/inline/reference.test.ts +58 -58
  38. package/src/parser/inline/reference.ts +4 -4
  39. package/src/parser/inline.test.ts +20 -20
  40. package/src/parser/locale.test.ts +1 -1
  41. package/src/parser/processor/figure.test.ts +3 -3
  42. package/src/parser/processor/figure.ts +2 -2
  43. package/src/parser/processor/footnote.test.ts +60 -2
  44. package/src/parser/processor/footnote.ts +53 -24
  45. package/src/parser/source/str.ts +2 -2
  46. package/src/parser/util.ts +14 -10
  47. package/src/util/info.ts +4 -4
  48. package/src/util/toc.ts +12 -16
  49. package/src/parser/block/indentblock.test.ts +0 -30
  50. package/src/parser/block/indentblock.ts +0 -13
@@ -101,11 +101,11 @@ describe('Unit: parser/inline', () => {
101
101
  assert.deepStrictEqual(inspect(parser('*++ a ++*')), [['<em><ins> a </ins></em>'], '']);
102
102
  assert.deepStrictEqual(inspect(parser('*<small>`a`</small>*')), [['<em><small><code data-src="`a`">a</code></small></em>'], '']);
103
103
  assert.deepStrictEqual(inspect(parser('<small>*<bdi>a</bdi>*</small>')), [['<small><em><bdi>a</bdi></em></small>'], '']);
104
- assert.deepStrictEqual(inspect(parser('<bdi>((<bdi>((a))</bdi>))</bdi>')), [['<bdi><sup class="annotation"><bdi><span class="paren">((a))</span></bdi></sup></bdi>'], '']);
105
- assert.deepStrictEqual(inspect(parser('<bdi>[[<bdi>[[a]]</bdi>]]</bdi>')), [['<bdi><sup class="reference"><bdi>[[a]]</bdi></sup></bdi>'], '']);
104
+ assert.deepStrictEqual(inspect(parser('<bdi>((<bdi>((a))</bdi>))</bdi>')), [['<bdi><sup class="annotation"><span><bdi><span class="paren">((a))</span></bdi></span></sup></bdi>'], '']);
105
+ assert.deepStrictEqual(inspect(parser('<bdi>[[<bdi>[[a]]</bdi>]]</bdi>')), [['<bdi><sup class="reference"><span><bdi>[[a]]</bdi></span></sup></bdi>'], '']);
106
106
  assert.deepStrictEqual(inspect(parser('*[*]')), [['*', '[', '*', ']'], '']);
107
107
  assert.deepStrictEqual(inspect(parser('*<*>')), [['<em>&lt;</em>', '>'], '']);
108
- assert.deepStrictEqual(inspect(parser('*a((b))*')), [['<em>a<sup class="annotation">b</sup></em>'], '']);
108
+ assert.deepStrictEqual(inspect(parser('*a((b))*')), [['<em>a<sup class="annotation"><span>b</span></sup></em>'], '']);
109
109
  assert.deepStrictEqual(inspect(parser('``a`')), [['``', 'a', '`'], '']);
110
110
  assert.deepStrictEqual(inspect(parser('[@a]')), [['[', '<a href="/@a" class="account">@a</a>', ']'], '']);
111
111
  assert.deepStrictEqual(inspect(parser('[#1][#2]')), [['<a class="index" href="#index:1">1</a>', '<a class="index" href="#index:2">2</a>'], '']);
@@ -115,10 +115,10 @@ describe('Unit: parser/inline', () => {
115
115
  assert.deepStrictEqual(inspect(parser('$-1, $-2')), [['<a class="label" data-label="$-1">$-1</a>', ',', ' ', '<a class="label" data-label="$-2">$-2</a>'], '']);
116
116
  assert.deepStrictEqual(inspect(parser('$-1 and $-2')), [['<a class="label" data-label="$-1">$-1</a>', ' ', 'and', ' ', '<a class="label" data-label="$-2">$-2</a>'], '']);
117
117
  assert.deepStrictEqual(inspect(parser('$$-1')), [['$', '<a class="label" data-label="$-1">$-1</a>'], '']);
118
- assert.deepStrictEqual(inspect(parser('[[#a]]')), [['<sup class="reference"><a href="/hashtags/a" class="hashtag">#a</a></sup>'], '']);
119
- assert.deepStrictEqual(inspect(parser('[[$-1]]')), [['<sup class="reference"><a class="label" data-label="$-1">$-1</a></sup>'], '']);
120
- assert.deepStrictEqual(inspect(parser('[[#-1]]{b}')), [['<sup class="reference">#-1</sup>', '<a href="b">b</a>'], '']);
121
- assert.deepStrictEqual(inspect(parser('[[#-1]](b)')), [['<sup class="reference">#-1</sup>', '(', 'b', ')'], '']);
118
+ assert.deepStrictEqual(inspect(parser('[[#a]]')), [['<sup class="reference"><span><a href="/hashtags/a" class="hashtag">#a</a></span></sup>'], '']);
119
+ assert.deepStrictEqual(inspect(parser('[[$-1]]')), [['<sup class="reference"><span><a class="label" data-label="$-1">$-1</a></span></sup>'], '']);
120
+ assert.deepStrictEqual(inspect(parser('[[#-1]]{b}')), [['<sup class="reference"><span>#-1</span></sup>', '<a href="b">b</a>'], '']);
121
+ assert.deepStrictEqual(inspect(parser('[[#-1]](b)')), [['<sup class="reference"><span>#-1</span></sup>', '(', 'b', ')'], '']);
122
122
  assert.deepStrictEqual(inspect(parser('[[#-1]a]{b}')), [['<a href="b">[#-1]a</a>'], '']);
123
123
  assert.deepStrictEqual(inspect(parser('[[#-1]a](b)')), [['[', '<a class="index" href="#index:-1">-1</a>', 'a', ']', '(', 'b', ')'], '']);
124
124
  assert.deepStrictEqual(inspect(parser('[#a]{b}')), [['<a class="index" href="#index:a">a</a>', '<a href="b">b</a>'], '']);
@@ -142,18 +142,18 @@ 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">a</sup>'], '']);
146
- assert.deepStrictEqual(inspect(parser('((((a))')), [['(', '(', '<sup class="annotation">a</sup>'], '']);
147
- assert.deepStrictEqual(inspect(parser('((((a))))')), [['<sup class="annotation"><span class="paren">((a))</span></sup>'], '']);
148
- assert.deepStrictEqual(inspect(parser('"((""))')), [['"', '<sup class="annotation">""</sup>'], '']);
149
- assert.deepStrictEqual(inspect(parser('[[[a]]')), [['[', '<sup class="reference">a</sup>'], '']);
150
- assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['[', '[', '<sup class="reference">a</sup>'], '']);
151
- assert.deepStrictEqual(inspect(parser('[[[[a]]]]')), [['<sup class="reference">[[a]]</sup>'], '']);
152
- assert.deepStrictEqual(inspect(parser('[[[$-1]]]')), [['<sup class="reference"><a class="label" data-label="$-1">$-1</a></sup>'], '']);
153
- assert.deepStrictEqual(inspect(parser('[[[]{a}]]')), [['<sup class="reference"><a href="a">a</a></sup>'], '']);
154
- assert.deepStrictEqual(inspect(parser('[[[a]{b}]]')), [['<sup class="reference"><a href="b">a</a></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
+ assert.deepStrictEqual(inspect(parser('((((a))))')), [['<sup class="annotation"><span><span class="paren">((a))</span></span></sup>'], '']);
148
+ 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>'], '']);
151
+ assert.deepStrictEqual(inspect(parser('[[[[a]]]]')), [['<sup class="reference"><span>[[a]]</span></sup>'], '']);
152
+ assert.deepStrictEqual(inspect(parser('[[[$-1]]]')), [['<sup class="reference"><span><a class="label" data-label="$-1">$-1</a></span></sup>'], '']);
153
+ assert.deepStrictEqual(inspect(parser('[[[]{a}]]')), [['<sup class="reference"><span><a href="a">a</a></span></sup>'], '']);
154
+ assert.deepStrictEqual(inspect(parser('[[[a]{b}]]')), [['<sup class="reference"><span><a href="b">a</a></span></sup>'], '']);
155
155
  assert.deepStrictEqual(inspect(parser('[(([a]{#}))]{#}')), [['<a href="#"><span class="paren">(<span class="paren">([a]{#})</span>)</span></a>'], '']);
156
- assert.deepStrictEqual(inspect(parser('"[[""]]')), [['"', '<sup class="reference">""</sup>'], '']);
156
+ assert.deepStrictEqual(inspect(parser('"[[""]]')), [['"', '<sup class="reference"><span>""</span></sup>'], '']);
157
157
  assert.deepStrictEqual(inspect(parser('<http://host>')), [['<', '<a href="http://host" target="_blank">http://host</a>', '>'], '']);
158
158
  assert.deepStrictEqual(inspect(parser('<<small>a<</small>')), [['<', '<small>a&lt;</small>'], '']);
159
159
  assert.deepStrictEqual(inspect(parser('<sup><sub>a</sub>')), [['<', 'sup', '>', '<sub>a</sub>'], '']);
@@ -229,8 +229,8 @@ describe('Unit: parser/inline', () => {
229
229
  assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
230
230
  assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '<span class="linebreak"> </span>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
231
231
  assert.deepStrictEqual(inspect(parser('*a*#b')), [['<em>a</em>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
232
- assert.deepStrictEqual(inspect(parser('((a))#b')), [['<sup class="annotation">a</sup>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
233
- assert.deepStrictEqual(inspect(parser('[[a]]#b')), [['<sup class="reference">a</sup>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
232
+ assert.deepStrictEqual(inspect(parser('((a))#b')), [['<sup class="annotation"><span>a</span></sup>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
233
+ assert.deepStrictEqual(inspect(parser('[[a]]#b')), [['<sup class="reference"><span>a</span></sup>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
234
234
  assert.deepStrictEqual(inspect(parser('[#a')), [['[', '<a href="/hashtags/a" class="hashtag">#a</a>'], '']);
235
235
  assert.deepStrictEqual(inspect(parser('|#a')), [['|', '<a href="/hashtags/a" class="hashtag">#a</a>'], '']);
236
236
  assert.deepStrictEqual(inspect(parser(' #a')), [[' ', '<a href="/hashtags/a" class="hashtag">#a</a>'], '']);
@@ -10,7 +10,7 @@ describe('Unit: parser/locale', () => {
10
10
  assert.deepStrictEqual(inspect(parser('。\\\n0')), [['<p>。<span class="linebreak"></span>0</p>'], '']);
11
11
  assert.deepStrictEqual(inspect(parser('。 \\\n0')), [['<p>。<span class="linebreak"></span>0</p>'], '']);
12
12
  assert.deepStrictEqual(inspect(parser('*。*\\\n0')), [['<p><em>。</em><span class="linebreak"></span>0</p>'], '']);
13
- assert.deepStrictEqual(inspect(parser('!> 。\\\n0')), [['<blockquote><section><p>。<span class="linebreak"></span>0</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
13
+ assert.deepStrictEqual(inspect(parser('!> 。\\\n0')), [['<blockquote><section><p>。<span class="linebreak"></span>0</p><ol class="references"></ol></section></blockquote>'], '']);
14
14
  assert.deepStrictEqual(inspect(parser('[。](a)\\\n0')), [['<p><ruby>。<rp>(</rp><rt>a</rt><rp>)</rp></ruby><span class="linebreak"></span>0</p>'], '']);
15
15
  assert.deepStrictEqual(inspect(parser('[。 ](a )\\\n0')), [['<p><ruby>。<rp>(</rp><rt>a</rt><rp>)</rp></ruby><span class="linebreak"></span>0</p>'], '']);
16
16
  assert.deepStrictEqual(inspect(parser('。<wbr>\\\n0')), [['<p>。<wbr><span class="linebreak"></span>0</p>'], '']);
@@ -117,8 +117,8 @@ describe('Unit: parser/processor/figure', () => {
117
117
  assert.deepStrictEqual(
118
118
  [...target.children].map(el => el.outerHTML),
119
119
  [
120
- '<blockquote><blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure><ol class="annotations"></ol><ol class="references"></ol></section></blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>',
121
- '<aside class="example" data-type="markdown"><pre translate="no">~~~figure $test-a\n&gt; \n\n~~~\n\n$test-a</pre><hr><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure><p><a class="label disabled" data-label="test-a">Test 1</a></p><ol class="annotations"></ol><ol class="references"></ol></section></aside>',
120
+ '<blockquote><blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure><ol class="references"></ol></section></blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure><ol class="references"></ol></section></blockquote>',
121
+ '<aside class="example" data-type="markdown"><pre translate="no">~~~figure $test-a\n&gt; \n\n~~~\n\n$test-a</pre><hr><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure><p><a class="label disabled" data-label="test-a">Test 1</a></p><ol class="references"></ol></section></aside>',
122
122
  '<figure data-type="quote" data-label="test-b" data-group="test" data-number="1" id="label:test-b"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure>',
123
123
  '<figure data-type="quote" data-label="test-a" data-group="test" data-number="2" id="label:test-a"><figcaption><span class="figindex">Test 2. </span></figcaption><div><blockquote></blockquote></div></figure>',
124
124
  ]);
@@ -173,7 +173,7 @@ describe('Unit: parser/processor/figure', () => {
173
173
  '<h2 id="index:0">0</h2>',
174
174
  '<figure data-type="quote" data-label="test-1" data-group="test" data-number="1" id="label:test-1"><figcaption><span class="figindex">Test 1. </span></figcaption><div><blockquote></blockquote></div></figure>',
175
175
  '<h2 id="index:0">0</h2>',
176
- '<blockquote><section><h2>0</h2><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>',
176
+ '<blockquote><section><h2>0</h2><ol class="references"></ol></section></blockquote>',
177
177
  '<figure data-type="quote" data-label="test-b" data-group="test" data-number="2.1" id="label:test-b"><figcaption><span class="figindex">Test 2.1. </span></figcaption><div><blockquote></blockquote></div></figure>',
178
178
  '<h2 id="index:0">0</h2>',
179
179
  '<figure data-label="$-0.0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
@@ -6,14 +6,14 @@ import { push } from 'spica/array';
6
6
 
7
7
  export function* figure(
8
8
  target: ParentNode & Node,
9
- footnotes?: Readonly<{ annotations: HTMLOListElement; references: HTMLOListElement; }>,
9
+ footnotes?: Readonly<{ annotations?: HTMLOListElement; references: HTMLOListElement; }>,
10
10
  opts: Readonly<{
11
11
  id?: string;
12
12
  }> = {},
13
13
  ): Generator<HTMLAnchorElement | undefined, undefined, undefined> {
14
14
  const refs = new MultiMap<string, HTMLAnchorElement>(push(push(push([],
15
15
  target.querySelectorAll('a.label:not(.disabled)[data-label]')),
16
- footnotes?.annotations.querySelectorAll('a.label:not(.disabled)') ?? []),
16
+ footnotes?.annotations?.querySelectorAll('a.label:not(.disabled)') ?? []),
17
17
  footnotes?.references.querySelectorAll('a.label:not(.disabled)') ?? [])
18
18
  .map(el => [el.getAttribute('data-label')!, el]));
19
19
  const labels = new Set<string>();
@@ -151,8 +151,8 @@ describe('Unit: parser/processor/footnote', () => {
151
151
  assert.deepStrictEqual(
152
152
  [...target.children].map(el => el.outerHTML),
153
153
  [
154
- '<blockquote><blockquote><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup></p><ol class="annotations"><li>a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></blockquote><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup><br>~~~</p><ol class="annotations"><li>a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></blockquote>',
155
- '<aside class="example" data-type="markdown"><pre translate="no">((a))</pre><hr><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup></p><ol class="annotations"><li>a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></aside>',
154
+ '<blockquote><blockquote><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup></p><ol class="annotations"><li data-marker="*1">a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></blockquote><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup><br>~~~</p><ol class="annotations"><li data-marker="*1">a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></blockquote>',
155
+ '<aside class="example" data-type="markdown"><pre translate="no">((a))</pre><hr><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup></p><ol class="annotations"><li data-marker="*1">a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></aside>',
156
156
  '<p><sup class="annotation" id="annotation:ref:1" title="a"><span hidden="">a</span><a href="#annotation:def:1">*1</a></sup></p>',
157
157
  ]);
158
158
  assert.deepStrictEqual(
@@ -187,6 +187,64 @@ describe('Unit: parser/processor/footnote', () => {
187
187
  }
188
188
  });
189
189
 
190
+ it('split', () => {
191
+ const target = parse('((1))\n\n## a\n\n## b\n\n((2))((3))\n\n## c\n\n((4))');
192
+ for (let i = 0; i < 3; ++i) {
193
+ [...annotation(target)];
194
+ assert.deepStrictEqual(
195
+ [...target.children].map(el => el.outerHTML),
196
+ [
197
+ html('p', [
198
+ html('sup', { class: "annotation", id: "annotation:ref:1", title: "1" }, [
199
+ html('span', { hidden: '' }, '1'),
200
+ html('a', { href: "#annotation:def:1" }, '*1')
201
+ ]),
202
+ ]).outerHTML,
203
+ html('ol', { class: 'annotations' }, [
204
+ html('li', { id: 'annotation:def:1', 'data-marker': '*1' }, [
205
+ '1',
206
+ html('sup', [html('a', { href: '#annotation:ref:1' }, '^1')])
207
+ ]),
208
+ ]).outerHTML,
209
+ html('h2', { id: 'index:a' }, 'a').outerHTML,
210
+ html('h2', { id: 'index:b' }, 'b').outerHTML,
211
+ html('p', [
212
+ html('sup', { class: "annotation", id: "annotation:ref:2", title: "2" }, [
213
+ html('span', { hidden: '' }, '2'),
214
+ html('a', { href: "#annotation:def:2" }, '*2')
215
+ ]),
216
+ html('sup', { class: "annotation", id: "annotation:ref:3", title: "3" }, [
217
+ html('span', { hidden: '' }, '3'),
218
+ html('a', { href: "#annotation:def:3" }, '*3')
219
+ ]),
220
+ ]).outerHTML,
221
+ html('ol', { class: 'annotations' }, [
222
+ html('li', { id: 'annotation:def:2', 'data-marker': '*2' }, [
223
+ '2',
224
+ html('sup', [html('a', { href: '#annotation:ref:2' }, '^2')])
225
+ ]),
226
+ html('li', { id: 'annotation:def:3', 'data-marker': '*3' }, [
227
+ '3',
228
+ html('sup', [html('a', { href: '#annotation:ref:3' }, '^3')])
229
+ ]),
230
+ ]).outerHTML,
231
+ html('h2', { id: 'index:c' }, 'c').outerHTML,
232
+ html('p', [
233
+ html('sup', { class: "annotation", id: "annotation:ref:4", title: "4" }, [
234
+ html('span', { hidden: '' }, '4'),
235
+ html('a', { href: "#annotation:def:4" }, '*4')
236
+ ]),
237
+ ]).outerHTML,
238
+ html('ol', { class: 'annotations' }, [
239
+ html('li', { id: 'annotation:def:4', 'data-marker': '*4' }, [
240
+ '4',
241
+ html('sup', [html('a', { href: '#annotation:ref:4' }, '^4')])
242
+ ]),
243
+ ]).outerHTML,
244
+ ]);
245
+ }
246
+ });
247
+
190
248
  });
191
249
 
192
250
  describe('reference', () => {
@@ -1,55 +1,64 @@
1
- import { undefined, Infinity, Map, WeakMap } from 'spica/global';
1
+ import { undefined, Infinity, Map, Node } from 'spica/global';
2
2
  import { text } from '../inline/extension/indexee';
3
3
  import { frag, html, define } from 'typed-dom/dom';
4
4
  import { MultiMap } from 'spica/multimap';
5
- import { memoize } from 'spica/memoize';
5
+ import { push } from 'spica/array';
6
6
 
7
7
  export function* footnote(
8
8
  target: ParentNode & Node,
9
- footnotes?: Readonly<{ annotations: HTMLOListElement; references: HTMLOListElement; }>,
9
+ footnotes?: Readonly<{ annotations?: HTMLOListElement; references: HTMLOListElement; }>,
10
10
  opts: Readonly<{ id?: string; }> = {},
11
11
  ): Generator<HTMLAnchorElement | HTMLLIElement | undefined, undefined, undefined> {
12
- yield* reference(target, footnotes?.references, opts, footnotes?.annotations && [footnotes.annotations]);
13
- yield* annotation(target, footnotes?.annotations, opts, []);
12
+ yield* reference(target, footnotes?.references, opts);
13
+ yield* annotation(target, footnotes?.annotations, opts);
14
14
  return;
15
15
  }
16
16
 
17
- export const annotation = build('annotation', n => `*${n}`);
17
+ export const annotation = build('annotation', n => `*${n}`, 'h1, h2, h3, h4, h5, h6, aside.aside, hr');
18
18
  export const reference = build('reference', (n, abbr) => `[${abbr || n}]`);
19
19
 
20
20
  function build(
21
- syntax: string,
21
+ syntax: 'annotation' | 'reference',
22
22
  marker: (index: number, abbr: string | undefined) => string,
23
+ splitter?: string,
23
24
  ) {
24
25
  assert(syntax.match(/^[a-z]+$/));
25
26
  // Referenceを含むAnnotationの重複排除は両構文が互いに処理済みであることを必要とするため
26
27
  // 構文ごとに各1回の処理では不可能
27
- const identify = memoize<HTMLElement, string>(
28
- ref => `${+!ref.querySelector('.label')}:${ref.getAttribute('data-abbr') || '_' + ref.innerHTML}`,
29
- new WeakMap());
30
- const contentify = memoize<HTMLElement, DocumentFragment>(
31
- ref => frag(ref.cloneNode(true).childNodes),
32
- new WeakMap());
33
28
  return function* (
34
29
  target: ParentNode & Node,
35
30
  footnote?: HTMLOListElement,
36
31
  opts: Readonly<{ id?: string }> = {},
37
- footnotes: readonly HTMLOListElement[] = [],
38
32
  ): Generator<HTMLAnchorElement | HTMLLIElement | undefined, undefined, undefined> {
33
+ //assert(syntax !== 'annotation' || !footnote);
39
34
  const defs = new Map<string, HTMLLIElement>();
40
35
  const buffer = new MultiMap<string, HTMLElement>();
41
36
  const titles = new Map<string, string>();
42
- const check = footnotes.some(el => target.contains(el));
37
+ // Bug: Firefox
38
+ //const splitters = push([], target.querySelectorAll(`:scope > :is(${splitter ?? '_'})`));
39
+ const splitters = push([], target.querySelectorAll(splitter ?? '_'))
40
+ .filter(el => el.parentNode === target);
41
+ // Bug: Firefox
42
+ //target.querySelectorAll(`:scope > .${syntax}s`).forEach(el => el.remove());
43
+ target.querySelectorAll(`.${syntax}s`).forEach(el => el.parentNode === target && el.remove());
44
+ let offset = 0;
43
45
  let style: 'count' | 'abbr';
44
46
  for (
45
47
  let refs = target.querySelectorAll(`sup.${syntax}:not(.disabled)`),
46
48
  i = 0, len = refs.length; i < len; ++i) {
47
49
  yield;
48
50
  const ref = refs[i];
49
- if (check && footnotes.some(el => el.contains(ref))) continue;
50
- const identifier = identify(ref);
51
+ while (+splitters[0]?.compareDocumentPosition(ref) & Node.DOCUMENT_POSITION_FOLLOWING) {
52
+ if (defs.size > 0) {
53
+ offset += defs.size;
54
+ yield* proc(defs, target.insertBefore(html('ol', { class: `${syntax}s` }), splitters[0] ?? null));
55
+ }
56
+ splitters.shift();
57
+ }
58
+ if (syntax === 'annotation' && ref.closest('#annotations, .annotations, #references, .references')) continue;
59
+ const identifier = `${+!ref.querySelector('.label')}:${ref.getAttribute('data-abbr') || '_' + ref.firstElementChild!.innerHTML}`;
51
60
  const abbr = ref.getAttribute('data-abbr') || undefined;
52
- const content = contentify(ref);
61
+ const content = frag(ref.firstElementChild!.cloneNode(true).childNodes);
53
62
  style ??= abbr ? 'abbr' : 'count';
54
63
  if (style === 'count' ? abbr : !abbr) {
55
64
  define(ref, {
@@ -59,8 +68,16 @@ function build(
59
68
  'data-invalid-message': `${syntax[0].toUpperCase() + syntax.slice(1)} style must be consistent`,
60
69
  });
61
70
  }
62
- if (ref.firstElementChild?.getAttribute('hidden') !== '') {
63
- ref.replaceChildren(html('span', { hidden: '' }, ref.childNodes));
71
+ else if (ref.getAttribute('data-invalid-type') === 'style') {
72
+ define(ref, {
73
+ class: void ref.classList.remove('invalid'),
74
+ 'data-invalid-syntax': null,
75
+ 'data-invalid-type': null,
76
+ 'data-invalid-message': null,
77
+ });
78
+ }
79
+ if (!ref.firstElementChild!.hasAttribute('hidden')) {
80
+ ref.firstElementChild!.setAttribute('hidden', '');
64
81
  }
65
82
  else {
66
83
  ref.lastChild?.remove();
@@ -71,9 +88,11 @@ function build(
71
88
  || text(content).trim()
72
89
  || content.textContent!.trim()
73
90
  || undefined;
91
+ assert(syntax !== 'annotation' || title);
74
92
  title
75
93
  ? !titles.has(identifier) && titles.set(identifier, title)
76
94
  : buffer.set(identifier, ref);
95
+ assert(syntax !== 'annotation' || !buffer.has(identifier));
77
96
  const blank = !!abbr && !content.firstChild;
78
97
  const refIndex = i + 1;
79
98
  const refId = opts.id !== ''
@@ -82,7 +101,10 @@ function build(
82
101
  const def = undefined
83
102
  || defs.get(identifier)
84
103
  || defs.set(identifier, html('li',
85
- { id: opts.id !== '' ? `${syntax}:${opts.id ? `${opts.id}:` : ''}def:${defs.size + 1}` : undefined },
104
+ {
105
+ id: opts.id !== '' ? `${syntax}:${opts.id ? `${opts.id}:` : ''}def:${defs.size + offset + 1}` : undefined,
106
+ 'data-marker': !footnote ? marker(defs.size + offset + 1, abbr) : undefined,
107
+ },
86
108
  [content.cloneNode(true), html('sup')]))
87
109
  .get(identifier)!;
88
110
  assert(def.lastChild);
@@ -100,7 +122,7 @@ function build(
100
122
  });
101
123
  }
102
124
  }
103
- const defIndex = +def.id.slice(def.id.lastIndexOf(':') + 1) || defs.size;
125
+ const defIndex = +def.id.slice(def.id.lastIndexOf(':') + 1) || defs.size + offset;
104
126
  const defId = def.id || undefined;
105
127
  define(ref, {
106
128
  id: refId,
@@ -125,13 +147,20 @@ function build(
125
147
  },
126
148
  `^${refIndex}`));
127
149
  }
128
- if (!footnote) return;
150
+ if (defs.size > 0 || footnote) {
151
+ yield* proc(defs, footnote ?? target.insertBefore(html('ol', { class: `${syntax}s` }), splitters[0] ?? target.querySelector(':scope > :is(#annotations, #references)')));
152
+ }
153
+ return;
154
+ }
155
+
156
+ function* proc(defs: Map<string, HTMLLIElement>, footnote: HTMLOListElement): Generator<HTMLLIElement | undefined, undefined, undefined> {
129
157
  const { children } = footnote;
130
158
  const size = defs.size;
131
159
  let count = 0;
132
160
  let length = children.length;
133
161
  I:
134
- for (const def of defs.values()) {
162
+ for (const [key, def] of defs) {
163
+ defs.delete(key);
135
164
  ++count;
136
165
  while (length > size) {
137
166
  const node = children[count - 1] as HTMLLIElement;
@@ -30,13 +30,13 @@ export function stropt(pattern: string | RegExp): Parser<string, Context<StrPars
30
30
  if (source === '') return;
31
31
  return source.slice(0, pattern.length) === pattern
32
32
  ? [[pattern], source.slice(pattern.length)]
33
- : [[], source];
33
+ : undefined;
34
34
  })
35
35
  : creator(source => {
36
36
  if (source === '') return;
37
37
  const m = source.match(pattern);
38
38
  return m
39
39
  ? [[m[0]], source.slice(m[0].length)]
40
- : [[], source];
40
+ : undefined;
41
41
  });
42
42
  }
@@ -8,8 +8,8 @@ import { invisibleHTMLEntityNames } from './api/normalize';
8
8
  import { reduce } from 'spica/memoize';
9
9
  import { push } from 'spica/array';
10
10
 
11
- export const regBlankStart = new RegExp(String.raw
12
- `^(?:\\?[^\S\n]|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr>)+`);
11
+ export const regBlankStart = new RegExp(
12
+ /^(?:\\?[^\S\n]|&IHN;|<wbr>)+/.source.replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`));
13
13
 
14
14
  export function blankWith(delimiter: string | RegExp): RegExp;
15
15
  export function blankWith(starting: '' | '\n', delimiter: string | RegExp): RegExp;
@@ -25,8 +25,8 @@ export function blankWith(starting: '' | '\n', delimiter?: string | RegExp): Reg
25
25
 
26
26
  export function visualize<P extends Parser<HTMLElement | string>>(parser: P): P;
27
27
  export function visualize<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
28
- const blankline = new RegExp(String.raw
29
- `^(?:\\$|\\?[^\S\n]|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr>)+$`,
28
+ const blankline = new RegExp(
29
+ /^(?:\\$|\\?[^\S\n]|&IHN;|<wbr>)+$/.source.replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`),
30
30
  'gm');
31
31
  return union([
32
32
  convert(
@@ -139,16 +139,20 @@ function isVisible(node: HTMLElement | string, strpos?: number): boolean {
139
139
 
140
140
  export function trimBlank<P extends Parser<HTMLElement | string>>(parser: P): P;
141
141
  export function trimBlank<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
142
- return fmap(
143
- trimBlankStart(parser),
144
- trimNodeEnd);
142
+ return trimBlankStart(trimBlankEnd(parser));
145
143
  }
146
- function trimBlankStart<P extends Parser<unknown>>(parser: P): P;
147
- function trimBlankStart<T>(parser: Parser<T>): Parser<T> {
144
+ export function trimBlankStart<P extends Parser<unknown>>(parser: P): P;
145
+ export function trimBlankStart<T>(parser: Parser<T>): Parser<T> {
148
146
  return convert(
149
147
  reduce(source => source.replace(regBlankStart, '')),
150
148
  parser);
151
149
  }
150
+ export function trimBlankEnd<P extends Parser<HTMLElement | string>>(parser: P): P;
151
+ export function trimBlankEnd<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
152
+ return fmap(
153
+ parser,
154
+ trimNodeEnd);
155
+ }
152
156
  //export function trimNode(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
153
157
  // return trimNodeStart(trimNodeEnd(nodes));
154
158
  //}
@@ -166,7 +170,7 @@ function trimBlankStart<T>(parser: Parser<T>): Parser<T> {
166
170
  // }
167
171
  // return nodes;
168
172
  //}
169
- export function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
173
+ function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
170
174
  const skip = nodes.length > 0 &&
171
175
  typeof nodes[nodes.length - 1] === 'object' &&
172
176
  nodes[nodes.length - 1]['className'] === 'indexer'
package/src/util/info.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { Info } from '../..';
2
2
  import { scope } from './scope';
3
- import { querySelectorAll } from 'typed-dom/query';
3
+ import { push } from 'spica/array';
4
4
 
5
5
  export function info(source: DocumentFragment | HTMLElement | ShadowRoot): Info {
6
6
  const match = scope(source, '.invalid');
7
7
  return {
8
- url: find<HTMLAnchorElement>('a:not(.email):not(.account):not(.channel):not(.hashtag):not(.hashnum):not(.anchor)')
8
+ url: find<HTMLAnchorElement>('a:not(:is(.email, .account, .channel, .hashtag, .hashnum, .anchor))')
9
9
  .filter(el => ['http:', 'https:'].includes(el.protocol)),
10
- tel: find<HTMLAnchorElement>('a:not(.email):not(.account):not(.channel):not(.hashtag):not(.hashnum):not(.anchor)')
10
+ tel: find<HTMLAnchorElement>('a:not(:is(.email, .account, .channel, .hashtag, .hashnum, .anchor))')
11
11
  .filter(el => ['tel:'].includes(el.protocol)),
12
12
  email: find('a.email'),
13
13
  account: find('a.account'),
@@ -20,7 +20,7 @@ export function info(source: DocumentFragment | HTMLElement | ShadowRoot): Info
20
20
  };
21
21
 
22
22
  function find<T extends HTMLElement>(selector: string): T[] {
23
- return querySelectorAll<T>(source, selector)
23
+ return push([], source.querySelectorAll<T>(selector))
24
24
  .filter(match);
25
25
  }
26
26
  }
package/src/util/toc.ts CHANGED
@@ -1,26 +1,22 @@
1
- import { undefined, Array } from 'spica/global';
1
+ import { undefined } from 'spica/global';
2
2
  import { html } from 'typed-dom/dom';
3
3
  import { push } from 'spica/array';
4
4
 
5
5
  // Bug: Firefox
6
6
  //const selector = 'h1 h2 h3 h4 h5 h6 aside.aside'.split(' ').map(s => `:scope > ${s}[id]`).join();
7
- const selector = 'h1 h2 h3 h4 h5 h6 aside.aside'.split(' ').map(s => `${s}[id]`).join();
7
+ const selector = ':is(h1, h2, h3, h4, h5, h6, aside.aside)[id]';
8
8
 
9
9
  export function toc(source: DocumentFragment | HTMLElement | ShadowRoot): HTMLUListElement {
10
- const es = source.querySelectorAll(selector);
11
- const hs: HTMLHeadingElement[] = Array(es.length);
12
- for (let i = 0; i < hs.length; ++i) {
13
- const el = es[i];
14
- assert(el.parentNode === source);
15
- switch (el.tagName) {
16
- case 'ASIDE':
17
- hs[i] = html(el.firstElementChild!.tagName.toLowerCase() as 'h1', { id: el.id, class: 'aside' }, el.firstElementChild!.cloneNode(true).childNodes);
18
- continue;
19
- default:
20
- hs[i] = el as HTMLHeadingElement;
21
- continue;
22
- }
23
- }
10
+ const hs = push([], source.querySelectorAll(selector))
11
+ .map(el => {
12
+ assert(el.parentNode === source);
13
+ switch (el.tagName) {
14
+ case 'ASIDE':
15
+ return html(el.firstElementChild!.tagName.toLowerCase() as 'h1', { id: el.id, class: 'aside' }, el.firstElementChild!.cloneNode(true).childNodes);
16
+ default:
17
+ return el as HTMLHeadingElement;
18
+ }
19
+ });
24
20
  return parse(cons(hs));
25
21
  }
26
22
 
@@ -1,30 +0,0 @@
1
- import { indentblock } from './indentblock';
2
- import { some } from '../../combinator';
3
- import { inspect } from '../../debug.test';
4
-
5
- describe('Unit: parser/block/indentblock', () => {
6
- describe('indentblock', () => {
7
- const parser = (source: string) => some(indentblock)(source, {});
8
-
9
- it('invalid', () => {
10
- assert.deepStrictEqual(inspect(parser('')), undefined);
11
- assert.deepStrictEqual(inspect(parser('\na')), undefined);
12
- assert.deepStrictEqual(inspect(parser(' a')), undefined);
13
- assert.deepStrictEqual(inspect(parser(' \ta')), undefined);
14
- assert.deepStrictEqual(inspect(parser(' a\nb')), undefined);
15
- assert.deepStrictEqual(inspect(parser(' a\n b')), undefined);
16
- });
17
-
18
- it('valid', () => {
19
- assert.deepStrictEqual(inspect(parser(' a')), [['<pre class="text">a</pre>'], '']);
20
- assert.deepStrictEqual(inspect(parser(' a ')), [['<pre class="text">a </pre>'], '']);
21
- assert.deepStrictEqual(inspect(parser(' a \n')), [['<pre class="text">a </pre>'], '']);
22
- assert.deepStrictEqual(inspect(parser(' a \n b')), [['<pre class="text">a <br> b</pre>'], '']);
23
- assert.deepStrictEqual(inspect(parser(' a\\\n b')), [['<pre class="text">a\\<br>b</pre>'], '']);
24
- assert.deepStrictEqual(inspect(parser('\ta')), [['<pre class="text">a</pre>'], '']);
25
- assert.deepStrictEqual(inspect(parser('\t a')), [['<pre class="text"> a</pre>'], '']);
26
- });
27
-
28
- });
29
-
30
- });
@@ -1,13 +0,0 @@
1
- import { IndentBlockParser } from '../block';
2
- import { union, block, validate, indent, convert } from '../../combinator';
3
- import { codeblock } from './codeblock';
4
-
5
- // 空行を含むインデントブロックはインデントの違いによるセグメント分割の境界が視認不能となるため採用しない
6
-
7
- export const indentblock: IndentBlockParser = block(validate(/^(?: |\t)/, indent(convert(
8
- source => {
9
- const fence = (source.match(/^`{3,}(?=[^\S\n]*$)/mg) ?? [])
10
- .reduce((max, fence) => fence > max ? fence : max, '``') + '`';
11
- return `${fence}\n${source}\n${fence}`;
12
- },
13
- union([codeblock])), true)));