securemark 0.237.1 → 0.239.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 (35) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/securemark.js +151 -106
  3. package/markdown.d.ts +3 -1
  4. package/package-lock.json +50 -50
  5. package/package.json +1 -1
  6. package/src/combinator/data/parser/subsequence.ts +5 -9
  7. package/src/combinator/data/parser/tails.ts +1 -1
  8. package/src/combinator/data/parser/union.ts +1 -1
  9. package/src/debug.test.ts +1 -1
  10. package/src/parser/block/codeblock.test.ts +5 -2
  11. package/src/parser/block/codeblock.ts +42 -24
  12. package/src/parser/block/extension/fig.test.ts +1 -1
  13. package/src/parser/block/extension/fig.ts +1 -1
  14. package/src/parser/block/extension/figure.test.ts +2 -0
  15. package/src/parser/block/extension/figure.ts +21 -24
  16. package/src/parser/block/olist.test.ts +2 -0
  17. package/src/parser/block/ulist.test.ts +2 -0
  18. package/src/parser/block.ts +2 -2
  19. package/src/parser/inline/comment.ts +1 -1
  20. package/src/parser/inline/extension/index.test.ts +19 -13
  21. package/src/parser/inline/extension/index.ts +4 -3
  22. package/src/parser/inline/extension/indexee.ts +7 -3
  23. package/src/parser/inline/extension/indexer.test.ts +2 -1
  24. package/src/parser/inline/extension/indexer.ts +6 -3
  25. package/src/parser/inline/htmlentity.ts +2 -2
  26. package/src/parser/inline/media.ts +2 -2
  27. package/src/parser/inline/ruby.ts +2 -2
  28. package/src/parser/processor/figure.test.ts +8 -6
  29. package/src/parser/processor/figure.ts +32 -19
  30. package/src/parser/segment.test.ts +2 -2
  31. package/src/parser/segment.ts +2 -2
  32. package/src/parser/source/str.ts +23 -4
  33. package/src/parser/source/text.ts +3 -0
  34. package/src/parser/source.ts +1 -1
  35. package/src/parser/util.ts +2 -2
@@ -3,7 +3,7 @@ import { ExtensionParser } from '../../inline';
3
3
  import { union, some, validate, guard, context, creator, surround, open, lazy, fmap } from '../../../combinator';
4
4
  import { inline } from '../../inline';
5
5
  import { indexee, identity } from './indexee';
6
- import { txt, str } from '../../source';
6
+ import { txt, str, stropt } from '../../source';
7
7
  import { startTight, trimNodeEnd } from '../../util';
8
8
  import { html, define, defrag } from 'typed-dom';
9
9
  import { join } from 'spica/array';
@@ -23,10 +23,11 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
23
23
  media: false,
24
24
  autolink: false,
25
25
  }}},
26
+ open(stropt('|'),
26
27
  some(union([
27
28
  signature,
28
29
  inline,
29
- ]), ']', /^\\?\n/)))),
30
+ ]), ']', /^\\?\n/), true)))),
30
31
  ']'),
31
32
  ns => [html('a', trimNodeEnd(defrag(ns)))])),
32
33
  ([el]: [HTMLAnchorElement]) => [
@@ -40,7 +41,7 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
40
41
  ]))));
41
42
 
42
43
  const signature: IndexParser.SignatureParser = lazy(() => creator(fmap(open(
43
- /^\s+\|#/,
44
+ '|#',
44
45
  startTight(some(union([bracket, txt]), ']'))),
45
46
  ns => [
46
47
  html('span', { class: 'indexer', 'data-index': identity(join(ns)).slice(6) }),
@@ -22,12 +22,13 @@ export function text(source: HTMLElement | DocumentFragment, optional = false):
22
22
  assert(source instanceof DocumentFragment || !source.matches('.indexer'));
23
23
  assert(source.querySelectorAll(':scope > .indexer').length <= 1);
24
24
  const indexer = source.querySelector(':scope > .indexer');
25
- if (indexer) return indexer.getAttribute('data-index')!;
26
- if (optional) return '';
25
+ if (!indexer && optional) return '';
26
+ const index = indexer?.getAttribute('data-index');
27
+ if (index) return index;
27
28
  assert(!source.querySelector('.annotation, br'));
28
29
  const target = source.cloneNode(true) as typeof source;
29
30
  for (
30
- let es = target.querySelectorAll('code[data-src], .math[data-src], .comment, rt, rp, .reference'),
31
+ let es = target.querySelectorAll('code[data-src], .math[data-src], .comment, rt, rp, .reference, .checkbox, ul, ol'),
31
32
  i = 0, len = es.length; i < len; ++i) {
32
33
  const el = es[i];
33
34
  switch (el.tagName) {
@@ -36,6 +37,8 @@ export function text(source: HTMLElement | DocumentFragment, optional = false):
36
37
  continue;
37
38
  case 'RT':
38
39
  case 'RP':
40
+ case 'UL':
41
+ case 'OL':
39
42
  el.remove();
40
43
  continue;
41
44
  }
@@ -44,6 +47,7 @@ export function text(source: HTMLElement | DocumentFragment, optional = false):
44
47
  define(el, el.getAttribute('data-src')!);
45
48
  continue;
46
49
  case 'comment':
50
+ case 'checkbox':
47
51
  el.remove();
48
52
  continue;
49
53
  case 'reference':
@@ -12,13 +12,14 @@ describe('Unit: parser/inline/extension/indexer', () => {
12
12
  assert.deepStrictEqual(inspect(parser(' ')), undefined);
13
13
  assert.deepStrictEqual(inspect(parser(' #')), undefined);
14
14
  assert.deepStrictEqual(inspect(parser(' #a')), undefined);
15
- assert.deepStrictEqual(inspect(parser(' [#]')), undefined);
15
+ assert.deepStrictEqual(inspect(parser(' [# ]')), undefined);
16
16
  assert.deepStrictEqual(inspect(parser(' [#]]')), undefined);
17
17
  assert.deepStrictEqual(inspect(parser(' [#a]]')), undefined);
18
18
  assert.deepStrictEqual(inspect(parser(' [#&a;]')), undefined);
19
19
  });
20
20
 
21
21
  it('valid', () => {
22
+ assert.deepStrictEqual(inspect(parser(' [#]')), [['<span class="indexer" data-index=""></span>'], '']);
22
23
  assert.deepStrictEqual(inspect(parser(' [#a]')), [['<span class="indexer" data-index="a"></span>'], '']);
23
24
  assert.deepStrictEqual(inspect(parser(' [#a] ')), [['<span class="indexer" data-index="a"></span>'], '']);
24
25
  assert.deepStrictEqual(inspect(parser(' [#a ]')), [['<span class="indexer" data-index="a"></span>'], '']);
@@ -1,14 +1,17 @@
1
1
  import { ExtensionParser } from '../../inline';
2
- import { union, verify, creator, context, surround, fmap } from '../../../combinator';
2
+ import { union, verify, focus, creator, context, surround, fmap } from '../../../combinator';
3
3
  import { index } from './index';
4
4
  import { html } from 'typed-dom';
5
5
 
6
6
  export const indexer: ExtensionParser.IndexerParser = creator(fmap(verify(surround(
7
- /^\s+(?=\[#[^\s\]])/,
7
+ /^\s+(?=\[#\S)/,
8
8
  context({ syntax: { inline: {
9
9
  index: true,
10
10
  }}},
11
- union([index])),
11
+ union([
12
+ focus('[#]', () => [[html('a', { href: '#' })], '']),
13
+ index,
14
+ ])),
12
15
  /^\s*$/),
13
16
  // Indexer is invisible but invalids must be visible.
14
17
  ([el]) => el.getElementsByClassName('invalid').length === 0),
@@ -6,12 +6,12 @@ import { reduce } from 'spica/memoize';
6
6
 
7
7
  export const unsafehtmlentity: UnsafeHTMLEntityParser = creator(validate('&', focus(
8
8
  /^&[0-9A-Za-z]+;/,
9
- entity => [[parse(entity) ?? `\0${entity}`], ''])));
9
+ entity => [[parse(entity) ?? `\x1B${entity}`], ''])));
10
10
 
11
11
  export const htmlentity: HTMLEntityParser = fmap(
12
12
  union([unsafehtmlentity]),
13
13
  ([test]) => [
14
- test[0] === '\0'
14
+ test[0] === '\x1B'
15
15
  ? html('span', {
16
16
  class: 'invalid',
17
17
  'data-invalid-syntax': 'htmlentity',
@@ -95,13 +95,13 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
95
95
  });
96
96
  return false;
97
97
  }
98
- if (alt.includes('\0')) {
98
+ if (alt.includes('\x1B')) {
99
99
  define(target, {
100
100
  class: void target.classList.add('invalid'),
101
101
  'data-invalid-syntax': 'media',
102
102
  'data-invalid-type': 'content',
103
103
  'data-invalid-message': `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`,
104
- alt: target.getAttribute('alt')?.replace(/\0/g, ''),
104
+ alt: target.getAttribute('alt')?.replace(/\x1B/g, ''),
105
105
  });
106
106
  return false;
107
107
  }
@@ -83,8 +83,8 @@ function attributes(texts: string[], rubies: string[]): Record<string, string> {
83
83
  let attrs: Record<string, string> | undefined;
84
84
  for (const ss of [texts, rubies]) {
85
85
  for (let i = 0; i < ss.length; ++i) {
86
- if (ss[i].indexOf('\0') === -1) continue;
87
- ss[i] = ss[i].replace(/\0/g, '');
86
+ if (ss[i].indexOf('\x1B') === -1) continue;
87
+ ss[i] = ss[i].replace(/\x1B/g, '');
88
88
  attrs ??= {
89
89
  class: 'invalid',
90
90
  'data-invalid-syntax': 'ruby',
@@ -21,6 +21,7 @@ describe('Unit: parser/processor/figure', () => {
21
21
  const target = parse([
22
22
  '$fig-a\n> ',
23
23
  '$fig-a',
24
+ '$fig-b',
24
25
  '$fig-a',
25
26
  ].join('\n\n'));
26
27
  for (let i = 0; i < 3; ++i) {
@@ -30,6 +31,7 @@ describe('Unit: parser/processor/figure', () => {
30
31
  [
31
32
  '<figure data-type="quote" data-label="fig-a" data-group="fig" data-number="1" id="label:fig-a"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1. </span></figcaption></figure>',
32
33
  '<p><a class="label" data-label="fig-a" href="#label:fig-a">Fig. 1</a></p>',
34
+ '<p><a class="label invalid" data-label="fig-b" data-invalid-syntax="label" data-invalid-type="reference" data-invalid-message="Missing the target figure">$fig-b</a></p>',
33
35
  '<p><a class="label" data-label="fig-a" href="#label:fig-a">Fig. 1</a></p>',
34
36
  ]);
35
37
  }
@@ -98,7 +100,7 @@ describe('Unit: parser/processor/figure', () => {
98
100
  '<figure data-type="quote" data-label="fig-a" data-group="fig" data-number="1" id="label:fig-a"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1. </span></figcaption></figure>',
99
101
  '<p><a class="label" data-label="fig-2" href="#label:fig-2">Fig. 2</a></p>',
100
102
  '<p><a class="label" data-label="$-4.1.1" href="#label:$-4.1.1">(4.1.1)</a></p>',
101
- '<p><a class="label disabled invalid" data-label="fig-1" data-invalid-syntax="label" data-invalid-type="reference" data-invalid-message="Missing the target figure">$fig-1</a></p>',
103
+ '<p><a class="label invalid" data-label="fig-1" data-invalid-syntax="label" data-invalid-type="reference" data-invalid-message="Missing the target figure">$fig-1</a></p>',
102
104
  ]);
103
105
  }
104
106
  });
@@ -181,7 +183,7 @@ describe('Unit: parser/processor/figure', () => {
181
183
  '<h2 id="index:0">0</h2>',
182
184
  '<figure data-label="$-0.1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
183
185
  '<figure data-type="quote" data-label="fig-d" data-group="fig" data-number="4.1" id="label:fig-d"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 4.1. </span></figcaption></figure>',
184
- '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 or 2 headings"></figure>',
186
+ '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
185
187
  '<figure data-label="$-0.1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
186
188
  '<figure data-label="$-0.4.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
187
189
  '<figure data-label="$-0.1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
@@ -232,17 +234,17 @@ describe('Unit: parser/processor/figure', () => {
232
234
  assert.deepStrictEqual(
233
235
  [...target.children].map(el => normalize(el.outerHTML)),
234
236
  [
235
- '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 or 2 headings"></figure>',
237
+ '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
236
238
  '<h2 id="index:0">0</h2>',
237
239
  '<figure data-label="$-0.0" data-group="$" hidden="" data-number="0.0"></figure>',
238
240
  '<figure data-type="quote" data-label="fig-a" data-group="fig" data-number="0.1" id="label:fig-a"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 0.1. </span></figcaption></figure>',
239
- '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 or 2 headings"></figure>',
241
+ '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
240
242
  '<h2 id="index:0">0</h2>',
241
243
  '<figure data-type="quote" data-label="fig-b" data-group="fig" data-number="1.1" id="label:fig-b"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1.1. </span></figcaption></figure>',
242
- '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 or 2 headings"></figure>',
244
+ '<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
243
245
  '<h3 id="index:0">0</h3>',
244
246
  '<figure data-type="quote" data-label="fig-c" data-group="fig" data-number="1.2" id="label:fig-c"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1.2. </span></figcaption></figure>',
245
- '<figure data-label="$-1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 or 2 headings"></figure>',
247
+ '<figure data-label="$-1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
246
248
  '<h2 id="index:0">0</h2>',
247
249
  '<figure data-label="$-0.0" data-group="$" hidden="" data-number="2.0"></figure>',
248
250
  '<figure data-type="quote" data-label="fig-d" data-group="fig" data-number="2.1" id="label:fig-d"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 2.1. </span></figcaption></figure>',
@@ -29,7 +29,7 @@ export function* figure(
29
29
  yield;
30
30
  const def = defs[i];
31
31
  if (def.parentNode !== target) continue;
32
- const { tagName, classList } = def;
32
+ const { tagName } = def;
33
33
  if (bases.length === 1 && tagName[0] === 'H') continue;
34
34
  assert(base === '0' || bases.length > 1);
35
35
  const label = tagName === 'FIGURE'
@@ -38,7 +38,7 @@ export function* figure(
38
38
  if (label.endsWith('-')) continue;
39
39
  if (label.endsWith('-0')) {
40
40
  define(def, {
41
- class: void classList.add('invalid'),
41
+ class: void def.classList.add('invalid'),
42
42
  'data-invalid-syntax': 'figure',
43
43
  'data-invalid-type': 'argument',
44
44
  'data-invalid-message': 'Invalid base index',
@@ -50,7 +50,7 @@ export function* figure(
50
50
  // $-x.x.0 is disabled.
51
51
  if (label.lastIndexOf('.', label.length - 3) !== -1) {
52
52
  define(def, {
53
- class: void classList.add('invalid'),
53
+ class: void def.classList.add('invalid'),
54
54
  'data-invalid-syntax': 'figure',
55
55
  'data-invalid-type': 'argument',
56
56
  'data-invalid-message': 'Base index must be $-x.0 format',
@@ -61,17 +61,17 @@ export function* figure(
61
61
  // $-x.0 after h1-h6.
62
62
  if (!/^H[1-6]$/.test(def.previousElementSibling?.tagName ?? '')) {
63
63
  define(def, {
64
- class: void classList.add('invalid'),
64
+ class: void def.classList.add('invalid'),
65
65
  'data-invalid-syntax': 'figure',
66
66
  'data-invalid-type': 'position',
67
- 'data-invalid-message': 'Base index declarations must be after level 1 or 2 headings',
67
+ 'data-invalid-message': messages.declaration,
68
68
  hidden: null,
69
69
  });
70
70
  continue;
71
71
  }
72
- else {
73
- classList.contains('invalid') && define(def, {
74
- class: void classList.remove('invalid'),
72
+ else if (def.getAttribute('data-invalid-message') === messages.declaration) {
73
+ define(def, {
74
+ class: void def.classList.remove('invalid'),
75
75
  'data-invalid-syntax': null,
76
76
  'data-invalid-type': null,
77
77
  'data-invalid-message': null,
@@ -118,7 +118,6 @@ export function* figure(
118
118
  assert(number.split('.').pop() !== '0');
119
119
  !isFixed(label) && numbers.set(group, number);
120
120
  assert(!+def.setAttribute('data-number', number));
121
- opts.id !== '' && def.setAttribute('id', `label:${opts.id ? `${opts.id}:` : ''}${label}`);
122
121
  const figindex = group === '$'
123
122
  ? `(${number})`
124
123
  : `${capitalize(group)}${group === 'fig' ? '.' : ''} ${number}`;
@@ -126,27 +125,35 @@ export function* figure(
126
125
  def.querySelector(':scope > figcaption > .figindex')!,
127
126
  group === '$' ? figindex : `${figindex}. `);
128
127
  if (labels.has(label)) {
129
- if (classList.contains('invalid') &&
130
- def.getAttribute('data-invalid-message') !== 'Duplicate label') continue;
128
+ if (def.classList.contains('invalid')) continue;
131
129
  define(def, {
132
130
  id: null,
133
- class: void classList.add('invalid'),
131
+ class: void def.classList.add('invalid'),
134
132
  'data-invalid-syntax': 'figure',
135
133
  'data-invalid-type': 'argument',
136
- 'data-invalid-message': 'Duplicate label',
134
+ 'data-invalid-message': messages.duplicate,
137
135
  });
138
136
  continue;
139
137
  }
140
- else {
141
- labels.add(label);
138
+ else if (def.getAttribute('data-invalid-message') === messages.duplicate) {
142
139
  define(def, {
143
- class: void classList.remove('invalid'),
140
+ class: void def.classList.remove('invalid'),
144
141
  'data-invalid-syntax': null,
145
142
  'data-invalid-type': null,
146
143
  'data-invalid-message': null,
147
144
  });
148
145
  }
146
+ labels.add(label);
147
+ opts.id !== '' && def.setAttribute('id', `label:${opts.id ? `${opts.id}:` : ''}${label}`);
149
148
  for (const ref of refs.take(label, Infinity)) {
149
+ if (ref.getAttribute('data-invalid-message') === messages.reference) {
150
+ define(ref, {
151
+ class: void ref.classList.remove('invalid'),
152
+ 'data-invalid-syntax': null,
153
+ 'data-invalid-type': null,
154
+ 'data-invalid-message': null,
155
+ });
156
+ }
150
157
  if (ref.hash.slice(1) === def.id && ref.innerText === figindex) continue;
151
158
  yield define(ref,
152
159
  opts.id !== '' ? { href: `#${def.id}` } : { class: `${ref.className} disabled` },
@@ -154,12 +161,12 @@ export function* figure(
154
161
  }
155
162
  }
156
163
  for (const [, ref] of refs) {
157
- if (opts.id !== '') {
164
+ if (opts.id !== '' && !ref.classList.contains('invalid')) {
158
165
  define(ref, {
159
- class: `${ref.className} disabled invalid`,
166
+ class: void ref.classList.add('invalid'),
160
167
  'data-invalid-syntax': 'label',
161
168
  'data-invalid-type': 'reference',
162
- 'data-invalid-message': 'Missing the target figure',
169
+ 'data-invalid-message': messages.reference,
163
170
  });
164
171
  }
165
172
  yield ref;
@@ -167,6 +174,12 @@ export function* figure(
167
174
  return;
168
175
  }
169
176
 
177
+ const messages = {
178
+ declaration: 'Base index declarations must be after level 1 to 6 headings',
179
+ duplicate: 'Duplicate label',
180
+ reference: 'Missing the target figure',
181
+ } as const;
182
+
170
183
  function increment(bases: readonly string[], el: HTMLHeadingElement): string {
171
184
  const index = (+el.tagName[1] - 1 || 1) - 1;
172
185
  assert(index >= 0);
@@ -4,12 +4,12 @@ describe('Unit: parser/segment', () => {
4
4
  describe('segment', () => {
5
5
  it('huge input', () => {
6
6
  const result = segment(`${'\n'.repeat(10 * 1000 ** 2)}`).next().value?.split('\n', 1)[0];
7
- assert(result?.startsWith('\0Too large input'));
7
+ assert(result?.startsWith('\x07Too large input'));
8
8
  });
9
9
 
10
10
  it('huge segment', () => {
11
11
  const result = segment(`${'\n'.repeat(1000 ** 2 - 1)}`).next().value?.split('\n', 1)[0];
12
- assert(result?.startsWith('\0Too large segment'));
12
+ assert(result?.startsWith('\x07Too large segment'));
13
13
  });
14
14
 
15
15
  it('basic', () => {
@@ -23,7 +23,7 @@ const parser: SegmentParser = union([
23
23
  ]);
24
24
 
25
25
  export function* segment(source: string): Generator<string, undefined, undefined> {
26
- if (!validate(source, MAX_INPUT_SIZE)) return yield `\0Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`;
26
+ if (!validate(source, MAX_INPUT_SIZE)) return yield `\x07Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`;
27
27
  assert(source.length < Number.MAX_SAFE_INTEGER);
28
28
  while (source !== '') {
29
29
  const result = parser(source, {})!;
@@ -36,7 +36,7 @@ export function* segment(source: string): Generator<string, undefined, undefined
36
36
  const seg = segs[i];
37
37
  validate(seg, MAX_SEGMENT_SIZE)
38
38
  ? yield seg
39
- : yield `\0Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`
39
+ : yield `\x07Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`
40
40
  }
41
41
  source = rest;
42
42
  }
@@ -3,8 +3,8 @@ import { StrParser } from '../source';
3
3
  import { Parser, Context } from '../../combinator/data/parser';
4
4
  import { creator } from '../../combinator';
5
5
 
6
- export function str(pattern: string | RegExp, mustConsume?: boolean): StrParser;
7
- export function str(pattern: string | RegExp, mustConsume = true): Parser<string, Context<StrParser>, []> {
6
+ export function str(pattern: string | RegExp): StrParser;
7
+ export function str(pattern: string | RegExp): Parser<string, Context<StrParser>, []> {
8
8
  assert(pattern);
9
9
  return typeof pattern === 'string'
10
10
  ? creator(source => {
@@ -16,8 +16,27 @@ export function str(pattern: string | RegExp, mustConsume = true): Parser<string
16
16
  : creator(source => {
17
17
  if (source === '') return;
18
18
  const m = source.match(pattern);
19
- return m && (!mustConsume || m[0].length > 0)
19
+ return m && m[0].length > 0
20
20
  ? [[m[0]], source.slice(m[0].length)]
21
21
  : undefined;
22
22
  });
23
- };
23
+ }
24
+
25
+ export function stropt(pattern: string | RegExp): StrParser;
26
+ export function stropt(pattern: string | RegExp): Parser<string, Context<StrParser>, []> {
27
+ assert(pattern);
28
+ return typeof pattern === 'string'
29
+ ? creator(source => {
30
+ if (source === '') return;
31
+ return source.slice(0, pattern.length) === pattern
32
+ ? [[pattern], source.slice(pattern.length)]
33
+ : [[], source];
34
+ })
35
+ : creator(source => {
36
+ if (source === '') return;
37
+ const m = source.match(pattern);
38
+ return m
39
+ ? [[m[0]], source.slice(m[0].length)]
40
+ : [[], source];
41
+ });
42
+ }
@@ -24,6 +24,7 @@ export const text: TextParser = creator((source, context) => {
24
24
  case '。':
25
25
  case '!':
26
26
  case '?':
27
+ assert(source[0] !== '\x1B');
27
28
  return text(source.slice(1), context);
28
29
  }
29
30
  break;
@@ -39,8 +40,10 @@ export const text: TextParser = creator((source, context) => {
39
40
  case '\\':
40
41
  switch (source[1]) {
41
42
  case undefined:
43
+ assert(source[0] !== '\x1B');
42
44
  return [[], ''];
43
45
  case '\n':
46
+ assert(source[0] !== '\x1B');
44
47
  return [[html('span', { class: 'linebreak' }, ' ')], source.slice(2)];
45
48
  default:
46
49
  return [[source.slice(1, 2)], source.slice(2)];
@@ -14,5 +14,5 @@ export import AnyLineParser = SourceParser.AnyLineParser;
14
14
  export { text, txt, linebreak } from './source/text';
15
15
  export { escsource } from './source/escapable';
16
16
  export { unescsource } from './source/unescapable';
17
- export { str } from './source/str';
17
+ export { str, stropt } from './source/str';
18
18
  export { contentline, emptyline, anyline } from './source/line';
@@ -55,7 +55,7 @@ export function startLoose<T extends HTMLElement | string>(parser: Parser<T>, ex
55
55
  }
56
56
  export const isStartLoose = reduce((source: string, context: MarkdownParser.Context, except?: string): boolean => {
57
57
  return isStartTight(source.replace(/^[^\S\n]+/, ''), context, except);
58
- }, (source, _, except = '') => `${source}\0${except}`);
58
+ }, (source, _, except = '') => `${source}\x1E${except}`);
59
59
  export function startTight<P extends Parser<unknown>>(parser: P, except?: string): P;
60
60
  export function startTight<T>(parser: Parser<T>, except?: string): Parser<T> {
61
61
  return (source, context) =>
@@ -93,7 +93,7 @@ const isStartTight = reduce((source: string, context: MarkdownParser.Context, ex
93
93
  default:
94
94
  return source[0].trimStart() !== '';
95
95
  }
96
- }, (source, _, except = '') => `${source}\0${except}`);
96
+ }, (source, _, except = '') => `${source}\x1E${except}`);
97
97
  export function isStartTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
98
98
  if (nodes.length === 0) return true;
99
99
  return isVisible(nodes[0], 0);