securemark 0.296.3 → 0.296.5

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.
@@ -28,7 +28,7 @@ function build(
28
28
  splitter &&= `${splitter}, .${syntax}s`;
29
29
  // Referenceを含むAnnotationの重複排除は両構文が互いに処理済みであることを必要とするため
30
30
  // 構文ごとに各1回の処理では不可能
31
- const memory = memoize((ref: HTMLElement): {
31
+ const refInfo = memoize((ref: HTMLElement): {
32
32
  readonly content: Element;
33
33
  readonly identifier: string;
34
34
  readonly abbr: string;
@@ -37,6 +37,8 @@ function build(
37
37
  const content = ref.firstElementChild!;
38
38
  content.replaceWith(content.cloneNode());
39
39
  const abbr = ref.getAttribute('data-abbr') ?? '';
40
+ const clone = content.cloneNode(true);
41
+ const txt = text(clone).trim();
40
42
  const identifier = abbr
41
43
  ? identity(
42
44
  '',
@@ -46,12 +48,12 @@ function build(
46
48
  abbr.match(/^[^,\s]+(?:,? [^,\s]+)*?(?: \d{1,4}(?:-\d{0,4})?[a-z]?(?=,|$)|(?=,(?: [a-z]+\.?)? [0-9]))/)?.[0] ??
47
49
  abbr
48
50
  ))?.slice(2) || ''
49
- : identity('mark', undefined, signature(content))?.slice(6) || '';
51
+ : identity('mark', undefined, signature(clone))?.slice(6) || '';
50
52
  return {
51
53
  content,
52
54
  identifier,
53
55
  abbr,
54
- text: text(content).trim(),
56
+ text: txt,
55
57
  };
56
58
  }, new WeakMap());
57
59
  return function* (
@@ -62,10 +64,13 @@ function build(
62
64
  ): Generator<HTMLAnchorElement | HTMLLIElement | undefined, undefined, undefined> {
63
65
  const defs = new Map<string, HTMLLIElement>();
64
66
  const refs = target.querySelectorAll(`sup.${syntax}:not(.disabled)`);
65
- const titles = new Map<string, string>();
66
- const defIndexes = new Map<HTMLLIElement, number>();
67
- const refSubindexes = new Map<string, number>();
68
- const defSubindexes = new Map<string, number>();
67
+ const identifierInfo = memoize((identifier: string) => ({
68
+ defIndex: 0,
69
+ defSubindex: 0,
70
+ refSubindex: 0,
71
+ title: '' && identifier,
72
+ queue: [] as HTMLElement[],
73
+ }));
69
74
  const scope = target instanceof Element ? ':scope > ' : '';
70
75
  const splitters = splitter ? target.querySelectorAll(`${scope}:is(${splitter})`) : [];
71
76
  let iSplitters = 0;
@@ -100,17 +105,16 @@ function build(
100
105
  assert(defs.size === 0);
101
106
  }
102
107
  }
103
- const { content, identifier, abbr, text } = memory(ref);
104
- const refSubindex = refSubindexes.get(identifier)! + 1 || 1;
105
- refSubindexes.set(identifier, refSubindex);
108
+ const { content, identifier, abbr, text } = refInfo(ref);
109
+ const info = identifierInfo(identifier);
110
+ const refSubindex = ++info.refSubindex;
106
111
  const refId = opts.id !== ''
107
112
  ? `${syntax}:${opts.id ?? ''}:ref:${identifier}:${refSubindex}`
108
113
  : undefined;
109
114
  const initial = splitter
110
115
  ? !defs.has(identifier)
111
116
  : refSubindex === 1;
112
- const defSubindex = defSubindexes?.get(identifier)! + +initial || 1;
113
- initial && defSubindexes?.set(identifier, defSubindex);
117
+ const defSubindex = initial ? ++info.defSubindex : info.defSubindex;
114
118
  const defId = opts.id !== ''
115
119
  ? `${syntax}:${opts.id ?? ''}:def:${identifier}${splitter && `:${defSubindex}`}`
116
120
  : undefined;
@@ -125,11 +129,9 @@ function build(
125
129
  initial && defs.set(identifier, def);
126
130
  assert(def.lastElementChild?.matches('sup'));
127
131
  const defIndex = initial
128
- ? total + defs.size
129
- : defIndexes.get(def)!;
130
- initial && defIndexes.set(def, defIndex);
131
- const title = initial ? text : titles.get(identifier)!;
132
- initial && titles.set(identifier, title);
132
+ ? info.defIndex = total + defs.size
133
+ : info.defIndex;
134
+ const title = info.title ||= text;
133
135
  assert(syntax !== 'annotation' || title);
134
136
  ref.childElementCount > 1 && ref.lastElementChild!.remove();
135
137
  define(ref, {
@@ -137,6 +139,14 @@ function build(
137
139
  class: opts.id !== '' ? undefined : void ref.classList.add('disabled'),
138
140
  title,
139
141
  });
142
+ if (title && info.queue.length > 0) {
143
+ for (const ref of info.queue) {
144
+ define(ref, { title });
145
+ unmarkInvalid(ref);
146
+ }
147
+ info.queue = [];
148
+ def.firstElementChild!.replaceWith(content.cloneNode(true));
149
+ }
140
150
  switch (ref.getAttribute('data-invalid-syntax')) {
141
151
  case 'format':
142
152
  case 'content':
@@ -144,11 +154,12 @@ function build(
144
154
  }
145
155
  format ??= abbr ? 'abbr' : 'number';
146
156
  if (!ref.classList.contains('invalid')) switch (true) {
147
- case format === 'number' ? !!abbr : !abbr:
157
+ case format === 'number' ? abbr !== '' : abbr === '':
148
158
  markInvalid(ref, syntax, 'format', 'Notation format must be consistent with numbers or abbreviations');
149
159
  break;
150
- case !title:
160
+ case title === '':
151
161
  markInvalid(ref, syntax, 'content', 'Missing the content');
162
+ info.queue.push(ref);
152
163
  break;
153
164
  }
154
165
  yield ref.appendChild(html('a', { href: refId && defId && `#${defId}` }, marker(defIndex, abbr)));
@@ -167,11 +178,9 @@ function build(
167
178
  ? (++iSplitters, el as HTMLOListElement)
168
179
  : target.insertBefore(html('ol', { class: `${syntax}s` }), splitters[iSplitters] ?? bottom);
169
180
  yield* proc(defs, note);
181
+ assert(defs.size === 0);
170
182
  }
171
- if (splitter) for (
172
- let el: Element;
173
- el = splitters[iSplitters];
174
- ++iSplitters) {
183
+ if (splitter) for (let el: Element; el = splitters[iSplitters]; ++iSplitters) {
175
184
  if (~iSplitters << 32 - 8 === 0) yield;
176
185
  if (!scope && el.parentNode !== target) continue;
177
186
  if (el.tagName === 'OL') {
@@ -4,108 +4,108 @@ import { Command } from './context';
4
4
  describe('Unit: parser/segment', () => {
5
5
  describe('segment', () => {
6
6
  it('huge input', () => {
7
- const result = segment(`${'\n'.repeat(1e6 + 1)}`).next().value?.split('\n', 1)[0];
7
+ const result = segment(`${'\n'.repeat(1e6 + 1)}`).next().value?.[0].split('\n', 1)[0];
8
8
  assert(result?.startsWith(`${Command.Error}Too large input`));
9
9
  });
10
10
 
11
11
  it('huge segment', () => {
12
- const result = segment(`${'\n'.repeat(1e5 + 1)}`).next().value?.split('\n', 1)[0];
12
+ const result = segment(`${'\n'.repeat(1e5 + 1)}`).next().value?.[0].split('\n', 1)[0];
13
13
  assert(result?.startsWith(`${Command.Error}Too large segment`));
14
14
  });
15
15
 
16
16
  it('basic', () => {
17
- assert.deepStrictEqual([...segment('')], []);
18
- assert.deepStrictEqual([...segment('a')], ['a']);
19
- assert.deepStrictEqual([...segment('a\n')], ['a\n']);
20
- assert.deepStrictEqual([...segment('a\n\n')], ['a\n', '\n']);
21
- assert.deepStrictEqual([...segment('a\n\n\n')], ['a\n', '\n\n']);
22
- assert.deepStrictEqual([...segment('a\nb')], ['a\nb']);
23
- assert.deepStrictEqual([...segment('a\nb\n')], ['a\nb\n']);
24
- assert.deepStrictEqual([...segment('a\nb\n\n')], ['a\nb\n', '\n']);
25
- assert.deepStrictEqual([...segment('a\nb\n\n\n')], ['a\nb\n', '\n\n']);
26
- assert.deepStrictEqual([...segment('a\nb\n\nc\n\nd')], ['a\nb\n', '\n', 'c\n', '\n', 'd']);
27
- assert.deepStrictEqual([...segment('a\n\\\nb')], ['a\n\\\nb']);
28
- assert.deepStrictEqual([...segment('a ')], ['a ']);
29
- assert.deepStrictEqual([...segment(' a')], [' a']);
30
- assert.deepStrictEqual([...segment(' a ')], [' a ']);
17
+ assert.deepStrictEqual([...segment('')].map(t => t[0]), []);
18
+ assert.deepStrictEqual([...segment('a')].map(t => t[0]), ['a']);
19
+ assert.deepStrictEqual([...segment('a\n')].map(t => t[0]), ['a\n']);
20
+ assert.deepStrictEqual([...segment('a\n\n')].map(t => t[0]), ['a\n', '\n']);
21
+ assert.deepStrictEqual([...segment('a\n\n\n')].map(t => t[0]), ['a\n', '\n\n']);
22
+ assert.deepStrictEqual([...segment('a\nb')].map(t => t[0]), ['a\nb']);
23
+ assert.deepStrictEqual([...segment('a\nb\n')].map(t => t[0]), ['a\nb\n']);
24
+ assert.deepStrictEqual([...segment('a\nb\n\n')].map(t => t[0]), ['a\nb\n', '\n']);
25
+ assert.deepStrictEqual([...segment('a\nb\n\n\n')].map(t => t[0]), ['a\nb\n', '\n\n']);
26
+ assert.deepStrictEqual([...segment('a\nb\n\nc\n\nd')].map(t => t[0]), ['a\nb\n', '\n', 'c\n', '\n', 'd']);
27
+ assert.deepStrictEqual([...segment('a\n\\\nb')].map(t => t[0]), ['a\n\\\nb']);
28
+ assert.deepStrictEqual([...segment('a ')].map(t => t[0]), ['a ']);
29
+ assert.deepStrictEqual([...segment(' a')].map(t => t[0]), [' a']);
30
+ assert.deepStrictEqual([...segment(' a ')].map(t => t[0]), [' a ']);
31
31
  });
32
32
 
33
33
  it('linebreak', () => {
34
- assert.deepStrictEqual([...segment('\n')], ['\n']);
35
- assert.deepStrictEqual([...segment('\n\n')], ['\n\n']);
36
- assert.deepStrictEqual([...segment('\n\n\n')], ['\n\n\n']);
37
- assert.deepStrictEqual([...segment('\n\n\n\n')], ['\n\n\n\n']);
38
- assert.deepStrictEqual([...segment(' ')], [' ']);
39
- assert.deepStrictEqual([...segment(' \n')], [' \n']);
40
- assert.deepStrictEqual([...segment(' \n \n \n ')], [' \n \n \n ']);
41
- assert.deepStrictEqual([...segment('a\n')], ['a\n']);
42
- assert.deepStrictEqual([...segment('a\n ')], ['a\n', ' ']);
43
- assert.deepStrictEqual([...segment('a\n\n ')], ['a\n', '\n ']);
44
- assert.deepStrictEqual([...segment('a\n\n\n ')], ['a\n', '\n\n ']);
45
- assert.deepStrictEqual([...segment('a\n\n\n\n ')], ['a\n', '\n\n\n ']);
46
- assert.deepStrictEqual([...segment('a\n\n\n\n\n ')], ['a\n', '\n\n\n\n ']);
47
- assert.deepStrictEqual([...segment('a\n\n\n\n\n\n ')], ['a\n', '\n\n\n\n\n ']);
34
+ assert.deepStrictEqual([...segment('\n')].map(t => t[0]), ['\n']);
35
+ assert.deepStrictEqual([...segment('\n\n')].map(t => t[0]), ['\n\n']);
36
+ assert.deepStrictEqual([...segment('\n\n\n')].map(t => t[0]), ['\n\n\n']);
37
+ assert.deepStrictEqual([...segment('\n\n\n\n')].map(t => t[0]), ['\n\n\n\n']);
38
+ assert.deepStrictEqual([...segment(' ')].map(t => t[0]), [' ']);
39
+ assert.deepStrictEqual([...segment(' \n')].map(t => t[0]), [' \n']);
40
+ assert.deepStrictEqual([...segment(' \n \n \n ')].map(t => t[0]), [' \n \n \n ']);
41
+ assert.deepStrictEqual([...segment('a\n')].map(t => t[0]), ['a\n']);
42
+ assert.deepStrictEqual([...segment('a\n ')].map(t => t[0]), ['a\n', ' ']);
43
+ assert.deepStrictEqual([...segment('a\n\n ')].map(t => t[0]), ['a\n', '\n ']);
44
+ assert.deepStrictEqual([...segment('a\n\n\n ')].map(t => t[0]), ['a\n', '\n\n ']);
45
+ assert.deepStrictEqual([...segment('a\n\n\n\n ')].map(t => t[0]), ['a\n', '\n\n\n ']);
46
+ assert.deepStrictEqual([...segment('a\n\n\n\n\n ')].map(t => t[0]), ['a\n', '\n\n\n\n ']);
47
+ assert.deepStrictEqual([...segment('a\n\n\n\n\n\n ')].map(t => t[0]), ['a\n', '\n\n\n\n\n ']);
48
48
  });
49
49
 
50
50
  it('codeblock', () => {
51
- assert.deepStrictEqual([...segment('```')], ['```']);
52
- assert.deepStrictEqual([...segment('```\n```')], ['```\n```']);
53
- assert.deepStrictEqual([...segment('```\n\n```')], ['```\n\n```']);
54
- assert.deepStrictEqual([...segment('```\n\n\n```')], ['```\n', '\n\n', '```']);
55
- assert.deepStrictEqual([...segment('```\n\n0\n```')], ['```\n', '\n', '0\n```']);
56
- assert.deepStrictEqual([...segment('```\n0\n\n```')], ['```\n0\n\n```']);
57
- assert.deepStrictEqual([...segment('```\n````\n```')], ['```\n````\n```']);
58
- assert.deepStrictEqual([...segment('````\n```\n````')], ['````\n```\n````']);
59
- assert.deepStrictEqual([...segment('```\n\n```\n\n')], ['```\n\n```\n', '\n']);
51
+ assert.deepStrictEqual([...segment('```')].map(t => t[0]), ['```']);
52
+ assert.deepStrictEqual([...segment('```\n```')].map(t => t[0]), ['```\n```']);
53
+ assert.deepStrictEqual([...segment('```\n\n```')].map(t => t[0]), ['```\n\n```']);
54
+ assert.deepStrictEqual([...segment('```\n\n\n```')].map(t => t[0]), ['```\n', '\n\n', '```']);
55
+ assert.deepStrictEqual([...segment('```\n\n0\n```')].map(t => t[0]), ['```\n', '\n', '0\n```']);
56
+ assert.deepStrictEqual([...segment('```\n0\n\n```')].map(t => t[0]), ['```\n0\n\n```']);
57
+ assert.deepStrictEqual([...segment('```\n````\n```')].map(t => t[0]), ['```\n````\n```']);
58
+ assert.deepStrictEqual([...segment('````\n```\n````')].map(t => t[0]), ['````\n```\n````']);
59
+ assert.deepStrictEqual([...segment('```\n\n```\n\n')].map(t => t[0]), ['```\n\n```\n', '\n']);
60
60
  });
61
61
 
62
62
  it('mathblock', () => {
63
- assert.deepStrictEqual([...segment('$$')], ['$$']);
64
- assert.deepStrictEqual([...segment('$$\n$$')], ['$$\n$$']);
65
- assert.deepStrictEqual([...segment('$$\n\n$$')], ['$$\n\n$$']);
66
- assert.deepStrictEqual([...segment('$$\n\n\n$$')], ['$$\n', '\n\n', '$$']);
67
- assert.deepStrictEqual([...segment('$$\n\n0\n$$')], ['$$\n', '\n', '0\n$$']);
68
- assert.deepStrictEqual([...segment('$$\n0\n\n$$')], ['$$\n0\n\n$$']);
69
- assert.deepStrictEqual([...segment('$$\n\n$$\n\n')], ['$$\n\n$$\n', '\n']);
63
+ assert.deepStrictEqual([...segment('$$')].map(t => t[0]), ['$$']);
64
+ assert.deepStrictEqual([...segment('$$\n$$')].map(t => t[0]), ['$$\n$$']);
65
+ assert.deepStrictEqual([...segment('$$\n\n$$')].map(t => t[0]), ['$$\n\n$$']);
66
+ assert.deepStrictEqual([...segment('$$\n\n\n$$')].map(t => t[0]), ['$$\n', '\n\n', '$$']);
67
+ assert.deepStrictEqual([...segment('$$\n\n0\n$$')].map(t => t[0]), ['$$\n', '\n', '0\n$$']);
68
+ assert.deepStrictEqual([...segment('$$\n0\n\n$$')].map(t => t[0]), ['$$\n0\n\n$$']);
69
+ assert.deepStrictEqual([...segment('$$\n\n$$\n\n')].map(t => t[0]), ['$$\n\n$$\n', '\n']);
70
70
  });
71
71
 
72
72
  it('extension', () => {
73
- assert.deepStrictEqual([...segment('~~~')], ['~~~']);
74
- assert.deepStrictEqual([...segment('~~~\n~~~')], ['~~~\n~~~']);
75
- assert.deepStrictEqual([...segment('~~~\n\n~~~')], ['~~~\n\n~~~']);
76
- assert.deepStrictEqual([...segment('~~~\n\n\n~~~')], ['~~~\n', '\n\n', '~~~']);
77
- assert.deepStrictEqual([...segment('~~~\n\n0\n~~~')], ['~~~\n', '\n', '0\n~~~']);
78
- assert.deepStrictEqual([...segment('~~~\n0\n\n~~~')], ['~~~\n0\n\n~~~']);
79
- assert.deepStrictEqual([...segment('~~~\n~~~~\n~~~')], ['~~~\n~~~~\n~~~']);
80
- assert.deepStrictEqual([...segment('~~~~\n~~~\n~~~~')], ['~~~~\n~~~\n~~~~']);
81
- assert.deepStrictEqual([...segment('~~~\n\n~~~\n\n')], ['~~~\n\n~~~\n', '\n']);
82
- assert.deepStrictEqual([...segment('~~~\n```\n~~~\n```\n~~~')], ['~~~\n```\n~~~\n```\n~~~']);
83
- assert.deepStrictEqual([...segment('~~~\ninvalid\n~~~')], ['~~~\ninvalid\n~~~']);
84
- assert.deepStrictEqual([...segment('~~~\ninvalid\n\ncaption\n~~~')], ['~~~\ninvalid\n\ncaption\n~~~']);
85
- assert.deepStrictEqual([...segment('~~~figure [$-name]\n$$\n$$\n\n```\n~~~')], ['~~~figure [$-name]\n$$\n$$\n\n```\n~~~']);
73
+ assert.deepStrictEqual([...segment('~~~')].map(t => t[0]), ['~~~']);
74
+ assert.deepStrictEqual([...segment('~~~\n~~~')].map(t => t[0]), ['~~~\n~~~']);
75
+ assert.deepStrictEqual([...segment('~~~\n\n~~~')].map(t => t[0]), ['~~~\n\n~~~']);
76
+ assert.deepStrictEqual([...segment('~~~\n\n\n~~~')].map(t => t[0]), ['~~~\n', '\n\n', '~~~']);
77
+ assert.deepStrictEqual([...segment('~~~\n\n0\n~~~')].map(t => t[0]), ['~~~\n', '\n', '0\n~~~']);
78
+ assert.deepStrictEqual([...segment('~~~\n0\n\n~~~')].map(t => t[0]), ['~~~\n0\n\n~~~']);
79
+ assert.deepStrictEqual([...segment('~~~\n~~~~\n~~~')].map(t => t[0]), ['~~~\n~~~~\n~~~']);
80
+ assert.deepStrictEqual([...segment('~~~~\n~~~\n~~~~')].map(t => t[0]), ['~~~~\n~~~\n~~~~']);
81
+ assert.deepStrictEqual([...segment('~~~\n\n~~~\n\n')].map(t => t[0]), ['~~~\n\n~~~\n', '\n']);
82
+ assert.deepStrictEqual([...segment('~~~\n```\n~~~\n```\n~~~')].map(t => t[0]), ['~~~\n```\n~~~\n```\n~~~']);
83
+ assert.deepStrictEqual([...segment('~~~\ninvalid\n~~~')].map(t => t[0]), ['~~~\ninvalid\n~~~']);
84
+ assert.deepStrictEqual([...segment('~~~\ninvalid\n\ncaption\n~~~')].map(t => t[0]), ['~~~\ninvalid\n\ncaption\n~~~']);
85
+ assert.deepStrictEqual([...segment('~~~figure [$-name]\n$$\n$$\n\n```\n~~~')].map(t => t[0]), ['~~~figure [$-name]\n$$\n$$\n\n```\n~~~']);
86
86
  });
87
87
 
88
88
  it('mixed', () => {
89
- assert.deepStrictEqual([...segment('```\n\n\n')], ['```\n', '\n\n']);
90
- assert.deepStrictEqual([...segment('~~~\n\n\n')], ['~~~\n', '\n\n']);
91
- assert.deepStrictEqual([...segment('```\n~~~\n```')], ['```\n~~~\n```']);
92
- assert.deepStrictEqual([...segment('~~~\n```\n~~~')], ['~~~\n```\n~~~']);
93
- assert.deepStrictEqual([...segment('```\n```\n\n~~~\n~~~')], ['```\n```\n', '\n', '~~~\n~~~']);
94
- assert.deepStrictEqual([...segment('~~~\n~~~\n\n```\n```')], ['~~~\n~~~\n', '\n', '```\n```']);
95
- assert.deepStrictEqual([...segment(' ```\n\n```')], [' ```\n', '\n', '```']);
96
- assert.deepStrictEqual([...segment(' ~~~\n\n~~~')], [' ~~~\n', '\n', '~~~']);
89
+ assert.deepStrictEqual([...segment('```\n\n\n')].map(t => t[0]), ['```\n', '\n\n']);
90
+ assert.deepStrictEqual([...segment('~~~\n\n\n')].map(t => t[0]), ['~~~\n', '\n\n']);
91
+ assert.deepStrictEqual([...segment('```\n~~~\n```')].map(t => t[0]), ['```\n~~~\n```']);
92
+ assert.deepStrictEqual([...segment('~~~\n```\n~~~')].map(t => t[0]), ['~~~\n```\n~~~']);
93
+ assert.deepStrictEqual([...segment('```\n```\n\n~~~\n~~~')].map(t => t[0]), ['```\n```\n', '\n', '~~~\n~~~']);
94
+ assert.deepStrictEqual([...segment('~~~\n~~~\n\n```\n```')].map(t => t[0]), ['~~~\n~~~\n', '\n', '```\n```']);
95
+ assert.deepStrictEqual([...segment(' ```\n\n```')].map(t => t[0]), [' ```\n', '\n', '```']);
96
+ assert.deepStrictEqual([...segment(' ~~~\n\n~~~')].map(t => t[0]), [' ~~~\n', '\n', '~~~']);
97
97
  });
98
98
 
99
99
  it('blockquote', () => {
100
- assert.deepStrictEqual([...segment('> ```\n\n```')], ['> ```\n', '\n', '```']);
101
- assert.deepStrictEqual([...segment('!> ```\n\n```')], ['!> ```\n', '\n', '```']);
100
+ assert.deepStrictEqual([...segment('> ```\n\n```')].map(t => t[0]), ['> ```\n', '\n', '```']);
101
+ assert.deepStrictEqual([...segment('!> ```\n\n```')].map(t => t[0]), ['!> ```\n', '\n', '```']);
102
102
  });
103
103
 
104
104
  it('heading', () => {
105
- assert.deepStrictEqual([...segment('# a\n\n# b\n')], ['# a\n', '\n', '# b\n']);
106
- assert.deepStrictEqual([...segment('# a\n# b\n')], ['# a\n', '# b\n']);
107
- assert.deepStrictEqual([...segment('# a\n# b')], ['# a\n', '# b']);
108
- assert.deepStrictEqual([...segment('# a\n # b')], ['# a\n # b']);
105
+ assert.deepStrictEqual([...segment('# a\n\n# b\n')].map(t => t[0]), ['# a\n', '\n', '# b\n']);
106
+ assert.deepStrictEqual([...segment('# a\n# b\n')].map(t => t[0]), ['# a\n', '# b\n']);
107
+ assert.deepStrictEqual([...segment('# a\n# b')].map(t => t[0]), ['# a\n', '# b']);
108
+ assert.deepStrictEqual([...segment('# a\n # b')].map(t => t[0]), ['# a\n # b']);
109
109
  });
110
110
 
111
111
  });
@@ -1,11 +1,11 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
- import { Context, Command } from './context';
2
+ import { Context, Segment, Command } from './context';
3
3
  import { union, some } from '../combinator';
4
4
  import { segment as heading } from './block/heading';
5
5
  import { segment as codeblock } from './block/codeblock';
6
6
  import { segment as mathblock } from './block/mathblock';
7
7
  import { segment as extension } from './block/extension';
8
- import { contentline, emptyline } from './source';
8
+ import { contentline, emptysegment } from './source';
9
9
 
10
10
  import SegmentParser = MarkdownParser.SegmentParser;
11
11
 
@@ -13,7 +13,7 @@ export const MAX_SEGMENT_SIZE = 100_000; // 100,000 bytes (Max value size of FDB
13
13
  export const MAX_INPUT_SIZE = MAX_SEGMENT_SIZE * 10;
14
14
 
15
15
  const parser: SegmentParser = union([
16
- some(emptyline, MAX_SEGMENT_SIZE + 1),
16
+ some(emptysegment, MAX_SEGMENT_SIZE + 1),
17
17
  input => {
18
18
  const { context: { source, position } } = input;
19
19
  if (position === source.length) return;
@@ -26,21 +26,19 @@ const parser: SegmentParser = union([
26
26
  break;
27
27
  case '$':
28
28
  if (source[position + 1] === '$') return mathblock(input);
29
- break;
29
+ return extension(input);
30
30
  case '[':
31
31
  if (source[position + 1] === '$') return extension(input);
32
32
  break;
33
33
  case '#':
34
34
  return heading(input);
35
- case '$':
36
- return extension(input);
37
35
  }
38
36
  },
39
37
  some(contentline, MAX_SEGMENT_SIZE + 1),
40
38
  ]) as any;
41
39
 
42
- export function* segment(source: string): Generator<string, undefined, undefined> {
43
- if (!validate(source, MAX_INPUT_SIZE)) return yield `${Command.Error}Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`;
40
+ export function* segment(source: string): Generator<readonly [string, Segment], undefined, undefined> {
41
+ if (!validate(source, MAX_INPUT_SIZE)) return yield [`${Command.Error}Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`, Segment.unknown];
44
42
  assert(source.length < Number.MAX_SAFE_INTEGER);
45
43
  for (let position = 0; position < source.length;) {
46
44
  const context = new Context({ source, position });
@@ -51,13 +49,13 @@ export function* segment(source: string): Generator<string, undefined, undefined
51
49
  ? result.foldl<string[]>((acc, { value }) => void acc.push(value) || acc, [])
52
50
  : [source.slice(position, context.position)];
53
51
  assert(segs.join('') === source.slice(position, context.position));
52
+ position = context.position;
54
53
  for (let i = 0; i < segs.length; ++i) {
55
54
  const seg = segs[i];
56
55
  validate(seg, MAX_SEGMENT_SIZE)
57
- ? yield seg
58
- : yield `${Command.Error}Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`
56
+ ? yield [seg, context.segment]
57
+ : yield [`${Command.Error}Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`, Segment.unknown];
59
58
  }
60
- position = context.position;
61
59
  }
62
60
  }
63
61
 
@@ -1,4 +1,5 @@
1
- import { AnyLineParser, EmptyLineParser, ContentLineParser } from '../source';
1
+ import { AnyLineParser, EmptyLineParser, EmptySegmentParser, ContentLineParser } from '../source';
2
+ import { Segment } from '../context';
2
3
  import { List } from '../../combinator/data/parser';
3
4
 
4
5
  export const anyline: AnyLineParser = input => {
@@ -14,14 +15,32 @@ export const emptyline: EmptyLineParser = input => {
14
15
  const { context } = input;
15
16
  const { source, position } = context;
16
17
  if (position === source.length) return;
17
- if (source[position] === '\n') return ++context.position, new List();
18
- regEmptyline.lastIndex = position;
19
- regEmptyline.test(source);
20
- const i = regEmptyline.lastIndex;
21
- if (i === 0) return;
18
+ const i = eoel(source, position);
19
+ if (i === position) return;
20
+ context.position = i;
21
+ return new List();
22
+ };
23
+ export const emptysegment: EmptySegmentParser = input => {
24
+ const { context } = input;
25
+ const { source, position, segment } = context;
26
+ if (position === source.length) return;
27
+ if (segment & Segment.write) {
28
+ if (segment !== (Segment.empty | Segment.write)) return;
29
+ context.position = source.length;
30
+ return new List();
31
+ }
32
+ const i = eoel(source, position);
33
+ if (i === position) return;
22
34
  context.position = i;
35
+ context.segment = Segment.empty;
23
36
  return new List();
24
37
  };
38
+ function eoel(source: string, position: number): number {
39
+ if (source[position] === '\n') return position + 1;
40
+ regEmptyline.lastIndex = position;
41
+ regEmptyline.test(source);
42
+ return regEmptyline.lastIndex || position;
43
+ }
25
44
 
26
45
  const regContentline = /[^\S\n]*\S[^\n]*(?:$|\n)/y;
27
46
  export const contentline: ContentLineParser = input => {
@@ -7,18 +7,17 @@ export function str(pattern: string | RegExp, after?: string | RegExp): Parser<s
7
7
  return matcher(pattern, true, after ? tester(after, false) : undefined);
8
8
  }
9
9
 
10
- export function strs(pattern: string, limit?: number): StrParser;
11
- export function strs(pattern: string, limit: number = -1): Parser<string> {
12
- assert(pattern);
10
+ export function strs(char: string, min?: number, max?: number): StrParser;
11
+ export function strs(char: string, min: number = 1, max: number = -1): Parser<string> {
12
+ assert(char.length === 1);
13
13
  return ({ context }) => {
14
- const { source } = context;
15
- let acc = '';
16
- for (let i = 0; i !== limit && context.position < source.length && source.startsWith(pattern, context.position); ++i) {
17
- acc += pattern;
18
- context.position += pattern.length;
14
+ const { source, position } = context;
15
+ let cnt = 0;
16
+ for (; cnt !== max && context.position < source.length && source[context.position] === char; ++cnt) {
17
+ context.position += char.length;
19
18
  }
20
- return acc
21
- ? new List([new Node(acc)])
19
+ return cnt >= min
20
+ ? new List([new Node(source.slice(position, context.position))])
22
21
  : undefined;
23
22
  };
24
23
  }
@@ -8,10 +8,11 @@ export import UnescapableSourceParser = SourceParser.UnescapableSourceParser;
8
8
  export import StrParser = SourceParser.StrParser;
9
9
  export import ContentLineParser = SourceParser.ContentLineParser;
10
10
  export import EmptyLineParser = SourceParser.EmptyLineParser;
11
+ export import EmptySegmentParser = SourceParser.EmptySegmentParser;
11
12
  export import AnyLineParser = SourceParser.AnyLineParser;
12
13
 
13
14
  export { text, txt } from './source/text';
14
15
  export { escsource } from './source/escapable';
15
16
  export { unescsource } from './source/unescapable';
16
17
  export { str, strs } from './source/str';
17
- export { contentline, emptyline, anyline } from './source/line';
18
+ export { contentline, emptyline, emptysegment, anyline } from './source/line';