securemark 0.296.3 → 0.296.4

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.
@@ -33,6 +33,7 @@ export class Context {
33
33
  {
34
34
  source,
35
35
  position,
36
+ segment,
36
37
  resources,
37
38
  delimiters,
38
39
  precedence,
@@ -45,6 +46,7 @@ export class Context {
45
46
  ) {
46
47
  this.source = source ?? '';
47
48
  this.position = position ?? 0;
49
+ this.segment = segment ?? 0;
48
50
  this.resources = resources;
49
51
  this.precedence = precedence ?? 0;
50
52
  this.delimiters = delimiters ?? new Delimiters();
@@ -56,6 +58,7 @@ export class Context {
56
58
  }
57
59
  public source: string;
58
60
  public position: number;
61
+ public segment: number;
59
62
  public readonly resources?: {
60
63
  clock: number;
61
64
  recursions: number[];
@@ -103,6 +106,10 @@ export class Context {
103
106
  public backtracks: Record<number, number>;
104
107
  }
105
108
  export type Options = Partial<Context>;
109
+ export const enum Segment {
110
+ unknown = 0,
111
+ write = 1,
112
+ }
106
113
 
107
114
  export function input<C extends Context>(source: string, context: C): Input<C> {
108
115
  context.source = source;
@@ -1,5 +1,5 @@
1
1
  import { ParserSettings, Progress } from '../../..';
2
- import { Context } from '../context';
2
+ import { Context, Segment } from '../context';
3
3
  import { input } from '../../combinator/data/parser';
4
4
  import { segment } from '../segment';
5
5
  import { block } from '../block';
@@ -47,8 +47,10 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
47
47
  context.url = url ? new ReadonlyURL(url as ':') : undefined;
48
48
  const rev = revision = Symbol();
49
49
  const sourceSegments: string[] = [];
50
- for (const seg of segment(source)) {
50
+ const sourceSegmentAttrs: Segment[] = [];
51
+ for (const [seg, attr] of segment(source)) {
51
52
  sourceSegments.push(seg);
53
+ sourceSegmentAttrs.push(attr);
52
54
  yield { type: 'segment', value: seg };
53
55
  }
54
56
  const targetSegments = blocks.map(([seg]) => seg);
@@ -74,12 +76,15 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
74
76
  context.header = true;
75
77
  for (; index < sourceSegments.length - last; ++index) {
76
78
  assert(rev === revision);
77
- const seg = sourceSegments[index];
78
- const es = block(input(seg, new Context(context)))
79
+ const src = sourceSegments[index];
80
+ context.segment = sourceSegmentAttrs[index] | Segment.write;
81
+ const es = block(input(src, new Context(context)))
79
82
  ?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? [];
80
83
  // @ts-expect-error
81
84
  context.header = false;
82
- blocks.splice(index, 0, [seg, es, url]);
85
+ blocks.length === index
86
+ ? blocks.push([src, es, url])
87
+ : blocks.splice(index, 0, [src, es, url]);
83
88
  if (es.length === 0) continue;
84
89
  // All deletion processes always run after all addition processes have done.
85
90
  // Therefore any `base` node will never be unavailable by deletions until all the dependent `el` nodes are added.
@@ -1,6 +1,6 @@
1
1
  import { ParserOptions } from '../../..';
2
2
  import { input } from '../../combinator/data/parser';
3
- import { Context } from '../context';
3
+ import { Context, Segment } from '../context';
4
4
  import { segment } from '../segment';
5
5
  import { block } from '../block';
6
6
  import { normalize } from './normalize';
@@ -32,7 +32,8 @@ export function parse(source: string, options: Options = {}, context?: Context):
32
32
  const node = frag();
33
33
  // @ts-expect-error
34
34
  context.header = true;
35
- for (const seg of segment(source)) {
35
+ for (const [seg, attr] of segment(source)) {
36
+ context.segment = attr | Segment.write;
36
37
  node.append(
37
38
  ...block(input(seg, new Context(context)))
38
39
  ?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? []);
@@ -1,4 +1,5 @@
1
1
  import { ExtensionParser } from '../../block';
2
+ import { Segment } from '../../context';
2
3
  import { union, sequence, some, block, line, verify, rewrite, close, convert } from '../../../combinator';
3
4
  import { contentline } from '../../source';
4
5
  import { figure } from './figure';
@@ -23,7 +24,7 @@ export const segment: FigParser.SegmentParser = block(
23
24
  seg_placeholder,
24
25
  some(contentline),
25
26
  ]),
26
- ]));
27
+ ]), true, Segment.fig);
27
28
 
28
29
  export const fig: FigParser = block(rewrite(segment, verify(convert(
29
30
  (source, context) => {
@@ -33,6 +34,7 @@ export const fig: FigParser = block(rewrite(segment, verify(convert(
33
34
  const { position } = context;
34
35
  const result = parser({ context });
35
36
  context.position = position;
37
+ context.segment = Segment.figure | Segment.write;
36
38
  return result
37
39
  ? `${fence}figure ${source.replace(/^(.+\n.+\n)([\S\s]+?)\n?$/, '$1\n$2')}\n${fence}`
38
40
  : `${fence}figure ${source}\n\n${fence}`;
@@ -1,4 +1,5 @@
1
1
  import { ExtensionParser } from '../../block';
2
+ import { Segment } from '../../context';
2
3
  import { List, Node, subinput } from '../../../combinator/data/parser';
3
4
  import { union, inits, sequence, some, block, line, fence, rewrite, close, match, convert, fallback, fmap } from '../../../combinator';
4
5
  import { str, contentline, emptyline } from '../../source';
@@ -44,7 +45,7 @@ export const segment: FigureParser.SegmentParser = block(match(
44
45
  ]),
45
46
  ]),
46
47
  closer),
47
- ([, fence]) => fence.length - 1, [], 2 ** 4 - 1)));
48
+ ([, fence]) => fence.length - 1, [], 2 ** 4 - 1)), true, Segment.figure);
48
49
 
49
50
  export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
50
51
  convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
@@ -57,7 +57,7 @@ export const message: MessageParser = block(fmap(
57
57
  'data-type': type,
58
58
  },
59
59
  [...segment(body)].reduce(
60
- (acc, seg) =>
60
+ (acc, [seg]) =>
61
61
  push(acc, unwrap(content(subinput(seg, context)))),
62
62
  [html('h1', title(type))])))
63
63
  ]);
@@ -30,9 +30,9 @@ describe('Unit: parser/block/extension', () => {
30
30
  assert(parser(input('~~~\na\n~~~', new Context())));
31
31
  assert(parser(input('~~~a\n~~~', new Context())));
32
32
  assert(parser(input('~~~a\nb\n~~~', new Context())));
33
- assert(parser(input('$-0', new Context())));
34
- assert(parser(input('$-0\n', new Context())));
35
- assert(parser(input('$-0\n\n', new Context())));
33
+ //assert(parser(input('$-0', new Context())));
34
+ //assert(parser(input('$-0\n', new Context())));
35
+ //assert(parser(input('$-0\n\n', new Context())));
36
36
  });
37
37
 
38
38
  });
@@ -1,7 +1,6 @@
1
1
  import { ExtensionParser } from '../block';
2
2
  import { union } from '../../combinator';
3
- import { figbase } from './extension/figbase';
4
- import { fig, segment as seg_fig } from './extension/fig';
3
+ import { segment as seg_fig } from './extension/fig';
5
4
  import { figure, segment as seg_figure } from './extension/figure';
6
5
  import { table, segment as seg_table } from './extension/table';
7
6
  import { message } from './extension/message';
@@ -17,8 +16,8 @@ export const segment: ExtensionParser.SegmentParser = union([
17
16
  ]);
18
17
 
19
18
  export const extension: ExtensionParser = union([
20
- figbase,
21
- fig,
19
+ //figbase,
20
+ //fig,
22
21
  figure,
23
22
  table,
24
23
  message,
@@ -1,9 +1,9 @@
1
1
  import { HeadingParser } from '../block';
2
- import { State } from '../context';
2
+ import { Segment, State } from '../context';
3
3
  import { List, Node } from '../../combinator/data/parser';
4
4
  import { union, some, state, block, line, focus, rewrite, open, fmap, firstline } from '../../combinator';
5
5
  import { inline, indexee, indexer, dataindex } from '../inline';
6
- import { str } from '../source';
6
+ import { str, strs } from '../source';
7
7
  import { visualize, trimBlank } from '../visibility';
8
8
  import { unwrap, invalid } from '../util';
9
9
  import { html, defrag } from 'typed-dom/dom';
@@ -20,14 +20,14 @@ export const segment: HeadingParser.SegmentParser = block(focus(
20
20
  context.position += line.length;
21
21
  }
22
22
  return acc;
23
- }, false));
23
+ }, false), true, Segment.heading);
24
24
 
25
25
  export const heading: HeadingParser = block(rewrite(segment,
26
26
  // その他の表示制御は各所のCSSで行う。
27
27
  state(State.annotation | State.reference | State.index | State.label | State.link,
28
28
  line(indexee(fmap(union([
29
29
  open(
30
- str(/##+/y),
30
+ strs('#', 2),
31
31
  visualize(trimBlank(some(union([indexer, inline])))), true),
32
32
  open(
33
33
  str('#'),
@@ -1,10 +1,10 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
- import { Recursion, Command } from './context';
2
+ import { Segment, Recursion, Command } from './context';
3
3
  import { List, Node } from '../combinator/data/parser';
4
- import { union, reset, open, fallback, recover } from '../combinator';
4
+ import { union, reset, firstline, recover } from '../combinator';
5
5
  import { MAX_SEGMENT_SIZE } from './segment';
6
6
  import { header } from './header';
7
- import { emptyline } from './source';
7
+ import { emptysegment } from './source';
8
8
  import { pagebreak } from './block/pagebreak';
9
9
  import { heading } from './block/heading';
10
10
  import { ulist } from './block/ulist';
@@ -15,6 +15,9 @@ import { table } from './block/table';
15
15
  import { codeblock } from './block/codeblock';
16
16
  import { mathblock } from './block/mathblock';
17
17
  import { extension } from './block/extension';
18
+ import { figbase } from './block/extension/figbase';
19
+ import { fig } from './block/extension/fig';
20
+ import { figure } from './block/extension/figure';
18
21
  import { sidefence } from './block/sidefence';
19
22
  import { blockquote } from './block/blockquote';
20
23
  import { mediablock } from './block/mediablock';
@@ -57,12 +60,22 @@ export const block: BlockParser = reset(
57
60
  backtracks: {},
58
61
  },
59
62
  error(union([
60
- emptyline,
63
+ emptysegment,
61
64
  input => {
62
- const { context: { source, position } } = input;
65
+ const { context: { source, position, segment } } = input;
63
66
  if (position === source.length) return;
67
+ switch (segment ^ Segment.write) {
68
+ case Segment.heading:
69
+ return heading(input);
70
+ case Segment.fig:
71
+ return fig(input);
72
+ case Segment.figure:
73
+ return figure(input);
74
+ }
64
75
  const fst = source[position];
65
76
  switch (fst) {
77
+ case Command.Error:
78
+ throw new Error(firstline(source, position + 1).trimEnd());
66
79
  case '=':
67
80
  if (source.startsWith('===', position)) return pagebreak(input);
68
81
  break;
@@ -84,7 +97,7 @@ export const block: BlockParser = reset(
84
97
  case '[':
85
98
  switch (source[position + 1]) {
86
99
  case '$':
87
- return extension(input);
100
+ return figbase(input);
88
101
  case '!':
89
102
  return mediablock(input);
90
103
  }
@@ -95,11 +108,9 @@ export const block: BlockParser = reset(
95
108
  case '>':
96
109
  if (source[position + 1] === '>') return blockquote(input) || reply(input);
97
110
  return blockquote(input);
98
- case '#':
99
- return heading(input);
100
111
  case '$':
101
112
  if (source[position + 1] === '$') return mathblock(input);
102
- return extension(input);
113
+ return figbase(input);
103
114
  case '|':
104
115
  return table(input) || sidefence(input);
105
116
  case '(':
@@ -113,9 +124,8 @@ export const block: BlockParser = reset(
113
124
 
114
125
  function error(parser: BlockParser): BlockParser {
115
126
  const reg = new RegExp(String.raw`^${Command.Error}[^\n]*\n`)
116
- return recover<BlockParser>(fallback(
117
- open(Command.Error, ({ context: { source, position } }) => { throw new Error(source.slice(position).split('\n', 1)[0]); }),
118
- parser),
127
+ return recover<BlockParser>(
128
+ parser,
119
129
  ({ context: { source, position, id } }, reason) => new List([
120
130
  new Node(html('h1',
121
131
  {
@@ -7,6 +7,7 @@ export class Context extends Ctx {
7
7
  ) {
8
8
  super(options);
9
9
  const {
10
+ segment,
10
11
  buffer,
11
12
  sequential,
12
13
  header,
@@ -15,6 +16,7 @@ export class Context extends Ctx {
15
16
  id,
16
17
  caches,
17
18
  } = options;
19
+ this.segment = segment ?? Segment.unknown;
18
20
  this.buffer = buffer ?? new List();
19
21
  this.sequential = sequential ?? false;
20
22
  this.header = header ?? true;
@@ -23,6 +25,7 @@ export class Context extends Ctx {
23
25
  this.id = id;
24
26
  this.caches = caches;
25
27
  }
28
+ public override segment: Segment;
26
29
  public buffer: List<Node<(string | HTMLElement)>>;
27
30
  public sequential: boolean;
28
31
  public readonly header: boolean;
@@ -37,6 +40,16 @@ export class Context extends Ctx {
37
40
  }
38
41
  export type Options = Partial<Context>;
39
42
 
43
+ export const enum Segment {
44
+ unknown = 0,
45
+ write = 1,
46
+ nonempty = 0,
47
+ empty = 1 << 1,
48
+ heading = 3 << 1,
49
+ fig = 4 << 1,
50
+ figure = 5 << 1,
51
+ }
52
+
40
53
  export const enum State {
41
54
  annotation = 1 << 7,
42
55
  reference = 1 << 6,
@@ -26,7 +26,7 @@ export const emstrong: EmStrongParser = lazy(() =>
26
26
  precedence(0, recursion(Recursion.inline, repeat('***', beforeNonblank, surround(
27
27
  '',
28
28
  some(union([some(inline, '*', afterNonblank)])),
29
- strs('*', 3),
29
+ strs('*', 1, 3),
30
30
  false, [],
31
31
  ([, bs, cs], context): Result<Parser.Node<EmStrongParser>, Parser.Context<EmStrongParser>> => {
32
32
  assert(cs.length === 1);
@@ -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 => {