securemark 0.283.7 → 0.285.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.
@@ -0,0 +1,72 @@
1
+ import { italic } from './italic';
2
+ import { some } from '../../combinator';
3
+ import { inspect } from '../../debug.test';
4
+
5
+ describe('Unit: parser/inline/italic', () => {
6
+ describe('italic', () => {
7
+ const parser = (source: string) => some(italic)({ source, context: {} });
8
+
9
+ it('invalid', () => {
10
+ assert.deepStrictEqual(inspect(parser('///')), undefined);
11
+ assert.deepStrictEqual(inspect(parser('///a')), [['///', 'a'], '']);
12
+ assert.deepStrictEqual(inspect(parser('///a ///')), [['///', 'a'], ' ///']);
13
+ assert.deepStrictEqual(inspect(parser('///a ///')), [['///', 'a', ' '], ' ///']);
14
+ assert.deepStrictEqual(inspect(parser('///a\n///')), [['///', 'a'], '\n///']);
15
+ assert.deepStrictEqual(inspect(parser('///a\\ ///')), [['///', 'a'], '\\ ///']);
16
+ assert.deepStrictEqual(inspect(parser('///a\\\n///')), [['///', 'a'], '\\\n///']);
17
+ assert.deepStrictEqual(inspect(parser('///a/b')), [['///', 'a', '/', 'b'], '']);
18
+ assert.deepStrictEqual(inspect(parser('///a//b')), [['///', 'a', '/', '/', 'b'], '']);
19
+ assert.deepStrictEqual(inspect(parser('///a*b///')), [['///', 'a', '*', 'b', '/', '/', '/'], '']);
20
+ assert.deepStrictEqual(inspect(parser('/// ///')), undefined);
21
+ assert.deepStrictEqual(inspect(parser('/// a///')), undefined);
22
+ assert.deepStrictEqual(inspect(parser('/// a ///')), undefined);
23
+ assert.deepStrictEqual(inspect(parser('///\n///')), undefined);
24
+ assert.deepStrictEqual(inspect(parser('///\na///')), undefined);
25
+ assert.deepStrictEqual(inspect(parser('///\\ a///')), undefined);
26
+ assert.deepStrictEqual(inspect(parser('///\\\na///')), undefined);
27
+ assert.deepStrictEqual(inspect(parser('///<wbr>a///')), undefined);
28
+ assert.deepStrictEqual(inspect(parser(' ///a///')), undefined);
29
+ });
30
+
31
+ it('basic', () => {
32
+ assert.deepStrictEqual(inspect(parser('///a///')), [['<i>a</i>'], '']);
33
+ assert.deepStrictEqual(inspect(parser('///ab///')), [['<i>ab</i>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('///a////')), [['<i>a</i>'], '/']);
35
+ assert.deepStrictEqual(inspect(parser('///a\nb///')), [['<i>a<br>b</i>'], '']);
36
+ assert.deepStrictEqual(inspect(parser('///a\\\nb///')), [['<i>a<br>b</i>'], '']);
37
+ });
38
+
39
+ it('nest', () => {
40
+ assert.deepStrictEqual(inspect(parser('////a///')), [['/', '<i>a</i>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('////a///b')), [['/', '<i>a</i>'], 'b']);
42
+ assert.deepStrictEqual(inspect(parser('////a////')), [['/', '<i>a</i>', '/'], '']);
43
+ assert.deepStrictEqual(inspect(parser('////a////b')), [['/', '<i>a</i>', '/'], 'b']);
44
+ assert.deepStrictEqual(inspect(parser('/////a///')), [['//', '<i>a</i>'], '']);
45
+ assert.deepStrictEqual(inspect(parser('/////a///b')), [['//', '<i>a</i>'], 'b']);
46
+ assert.deepStrictEqual(inspect(parser('/////a////')), [['//', '<i>a</i>', '/'], '']);
47
+ assert.deepStrictEqual(inspect(parser('/////a////b')), [['//', '<i>a</i>', '/'], 'b']);
48
+ assert.deepStrictEqual(inspect(parser('/////a/////')), [['//', '<i>a</i>', '//'], '']);
49
+ assert.deepStrictEqual(inspect(parser('/////a/////b')), [['//', '<i>a</i>', '//'], 'b']);
50
+ assert.deepStrictEqual(inspect(parser('//////a///')), [['///', '<i>a</i>'], '']);
51
+ assert.deepStrictEqual(inspect(parser('//////a///b')), [['///', '<i>a</i>', 'b'], '']);
52
+ assert.deepStrictEqual(inspect(parser('//////a////')), [['///', '<i>a</i>', '/'], '']);
53
+ assert.deepStrictEqual(inspect(parser('//////a////b')), [['///', '<i>a</i>', '/', 'b'], '']);
54
+ assert.deepStrictEqual(inspect(parser('//////a/////')), [['///', '<i>a</i>', '/', '/'], '']);
55
+ assert.deepStrictEqual(inspect(parser('//////a/////b')), [['///', '<i>a</i>', '/', '/', 'b'], '']);
56
+ assert.deepStrictEqual(inspect(parser('//////a//////')), [['<i><i>a</i></i>'], '']);
57
+ assert.deepStrictEqual(inspect(parser('//////a///b///')), [['<i><i>a</i>b</i>'], '']);
58
+ assert.deepStrictEqual(inspect(parser('///a ///b//////')), [['<i>a <i>b</i></i>'], '']);
59
+ assert.deepStrictEqual(inspect(parser('///a\\ ///b//////')), [['<i>a <i>b</i></i>'], '']);
60
+ assert.deepStrictEqual(inspect(parser('///a //////b/////////')), [['<i>a <i><i>b</i></i></i>'], '']);
61
+ assert.deepStrictEqual(inspect(parser('///a ///b///c///')), [['<i>a <i>b</i>c</i>'], '']);
62
+ assert.deepStrictEqual(inspect(parser('///a ///b ///c/////////')), [['<i>a <i>b <i>c</i></i></i>'], '']);
63
+ assert.deepStrictEqual(inspect(parser('///a&Tab;///b//////')), [['<i>a\t<i>b</i></i>'], '']);
64
+ assert.deepStrictEqual(inspect(parser('///a<wbr>///b//////')), [['<i>a<wbr><i>b</i></i>'], '']);
65
+ assert.deepStrictEqual(inspect(parser('///`a`///')), [['<i><code data-src="`a`">a</code></i>'], '']);
66
+ assert.deepStrictEqual(inspect(parser('///(///a///)///')), [['<i><span class="paren">(<i>a</i>)</span></i>'], '']);
67
+ assert.deepStrictEqual(inspect(parser('///{http://host/}///')), [['<i><a class="url" href="http://host/" target="_blank">http://host/</a></i>'], '']);
68
+ });
69
+
70
+ });
71
+
72
+ });
@@ -0,0 +1,22 @@
1
+ import { ItalicParser } from '../inline';
2
+ import { Recursion, Command } from '../context';
3
+ import { union, some, creation, precedence, validate, surround, open, lazy } from '../../combinator';
4
+ import { inline } from '../inline';
5
+ import { tightStart, blankWith } from '../visibility';
6
+ import { repeat } from '../util';
7
+ import { push } from 'spica/array';
8
+ import { html, defrag } from 'typed-dom/dom';
9
+
10
+ // 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
11
+ // ある程度の長さのある文に使うのが望ましい。
12
+ export const italic: ItalicParser = lazy(() => creation(1, Recursion.inline, validate('///',
13
+ precedence(0, repeat('///', surround(
14
+ '',
15
+ tightStart(some(union([
16
+ some(inline, blankWith('///')),
17
+ open(some(inline, '/'), italic),
18
+ ]))),
19
+ '///', false,
20
+ ([, bs], rest) => [bs, rest],
21
+ ([, bs], rest) => [push(bs, [Command.Escape]), rest]),
22
+ nodes => [html('i', defrag(nodes))])))));
@@ -1,7 +1,7 @@
1
1
  import { MarkdownParser } from '../../../markdown';
2
2
  import { LinkParser } from '../inline';
3
3
  import { State, Recursion, Backtrack } from '../context';
4
- import { union, inits, tails, sequence, some, constraint, creation, precedence, state, validate, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
4
+ import { union, inits, tails, sequence, some, creation, precedence, state, constraint, validate, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
5
5
  import { inline, media, shortmedia } from '../inline';
6
6
  import { attributes } from './html';
7
7
  import { linebreak, unescsource, str } from '../source';
@@ -1,26 +1,27 @@
1
1
  import { MarkParser } from '../inline';
2
- import { State, Recursion } from '../context';
3
- import { union, some, creation, precedence, constraint, surround, open, lazy } from '../../combinator';
2
+ import { State, Recursion, Command } from '../context';
3
+ import { union, some, creation, precedence, constraint, validate, surround, open, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { identity, signature } from './extension/indexee';
6
- import { str } from '../source';
7
- import { startTight, blankWith } from '../visibility';
8
- import { unshift } from 'spica/array';
6
+ import { tightStart, blankWith } from '../visibility';
7
+ import { repeat } from '../util';
8
+ import { push } from 'spica/array';
9
9
  import { html, define, defrag } from 'typed-dom/dom';
10
10
 
11
- export const mark: MarkParser = lazy(() => constraint(State.mark, false, creation(1, Recursion.inline, surround(
12
- str('==', '='),
13
- precedence(0,
14
- startTight(some(union([
15
- some(inline, blankWith('==')),
16
- open(some(inline, '='), mark),
17
- ])))),
18
- str('=='), false,
19
- ([, bs], rest, { id }) => {
20
- const el = html('mark', defrag(bs));
21
- return [[
22
- define(el, { id: identity('mark', id, signature(el)) }),
23
- el.id && html('a', { href: `#${el.id}` }),
24
- ], rest];
25
- },
26
- ([as, bs], rest) => [unshift(as, bs), rest]))));
11
+ export const mark: MarkParser = lazy(() => constraint(State.mark, false, creation(1, Recursion.inline, validate('==',
12
+ precedence(0, repeat('==', surround(
13
+ '',
14
+ tightStart(some(union([
15
+ some(inline, blankWith('==')),
16
+ open(some(inline, '='), mark),
17
+ ]))),
18
+ '==', false,
19
+ ([, bs], rest) => [bs, rest],
20
+ ([, bs], rest) => [push(bs, [Command.Escape]), rest]),
21
+ (nodes, { id }) => {
22
+ const el = html('mark', defrag(nodes));
23
+ define(el, { id: identity('mark', id, signature(el)) });
24
+ return el.id
25
+ ? [el, el.id && html('a', { href: `#${el.id}` })]
26
+ : [el];
27
+ }))))));
@@ -36,7 +36,7 @@ const bracket: MathParser.BracketParser = lazy(() => creation(0, Recursion.termi
36
36
  str('{'),
37
37
  some(union([
38
38
  bracket,
39
- some(escsource, /^(?:[{}$]|\\?\n)/),
39
+ some(escsource, /^[{}$\n]/),
40
40
  ])),
41
41
  str('}'),
42
42
  true)));
@@ -4,7 +4,7 @@ import { eval, exec } from '../../combinator/data/parser';
4
4
  import { sequence, creation, surround, lazy, fmap, bind } from '../../combinator';
5
5
  import { unsafehtmlentity } from './htmlentity';
6
6
  import { text as txt, str } from '../source';
7
- import { isStartTightNodes } from '../visibility';
7
+ import { isTightNodeStart } from '../visibility';
8
8
  import { unshift, push } from 'spica/array';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
@@ -13,7 +13,7 @@ export const ruby: RubyParser = lazy(() => creation(1, Recursion.ignore, fmap(
13
13
  bind(surround('[', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ']', false, undefined, undefined, 3 | Backtrack.ruby), ([source], rest, context) => {
14
14
  const ns = eval(text({ source, context }), [undefined])[0];
15
15
  ns && ns.at(-1) === '' && ns.pop();
16
- return ns && isStartTightNodes(ns) ? [[ns], rest] : undefined;
16
+ return ns && isTightNodeStart(ns) ? [[ns], rest] : undefined;
17
17
  }),
18
18
  bind(surround('(', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ')', false, undefined, undefined, 3 | Backtrack.ruby), ([source], rest, context) => {
19
19
  const ns = eval(text({ source, context }), [undefined])[0];
@@ -26,7 +26,8 @@ export const ruby: RubyParser = lazy(() => creation(1, Recursion.ignore, fmap(
26
26
  return [
27
27
  html('ruby', attributes(texts, rubies), defrag(texts
28
28
  .reduce((acc, _, i) =>
29
- push(acc, unshift([texts[i]],
29
+ push(acc, unshift(
30
+ [texts[i]],
30
31
  i < rubies.length && rubies[i]
31
32
  ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
32
33
  : [html('rt')]))
@@ -36,7 +37,8 @@ export const ruby: RubyParser = lazy(() => creation(1, Recursion.ignore, fmap(
36
37
  return [
37
38
  html('ruby', attributes(texts, rubies), defrag([...texts[0]]
38
39
  .reduce((acc, _, i, texts) =>
39
- push(acc, unshift([texts[i]],
40
+ push(acc, unshift(
41
+ [texts[i]],
40
42
  i < rubies.length && rubies[i]
41
43
  ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
42
44
  : [html('rt')]))
@@ -4,14 +4,14 @@ import { union, some, creation, precedence, surround, open, lazy } from '../../c
4
4
  import { inline } from '../inline';
5
5
  import { emstrong } from './emstrong';
6
6
  import { str } from '../source';
7
- import { startTight, blankWith } from '../visibility';
7
+ import { tightStart, blankWith } from '../visibility';
8
8
  import { unshift } from 'spica/array';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
11
11
  export const strong: StrongParser = lazy(() => creation(1, Recursion.inline, surround(
12
12
  str('**', '*'),
13
13
  precedence(0,
14
- startTight(some(union([
14
+ tightStart(some(union([
15
15
  some(inline, blankWith('**')),
16
16
  open(some(inline, '*'), union([
17
17
  emstrong,
@@ -20,6 +20,6 @@ const bracket: TemplateParser.BracketParser = lazy(() => creation(0, Recursion.t
20
20
  undefined, ([as, bs = []], rest) => [unshift(as, bs), rest], 3 | Backtrack.template),
21
21
  surround(str('{'), some(union([bracket, escsource]), '}'), str('}'), true,
22
22
  undefined, ([as, bs = []], rest) => [unshift(as, bs), rest], 3 | Backtrack.template),
23
- surround(str('"'), precedence(2, some(escsource, /^"|^\\?\n/)), str('"'), true,
23
+ surround(str('"'), precedence(2, some(escsource, /^["\n]/)), str('"'), true,
24
24
  undefined, undefined, 3 | Backtrack.template),
25
25
  ])));
@@ -20,9 +20,6 @@ describe('Unit: parser/inline', () => {
20
20
  it('nest', () => {
21
21
  assert.deepStrictEqual(inspect(parser('あ(A)')), [['あ', '(', 'A', ')'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('あ(い)')), [['あ', '<span class="paren">(い)</span>'], '']);
23
- assert.deepStrictEqual(inspect(parser('+++a+++')), [['+++', 'a', '+++'], '']);
24
- assert.deepStrictEqual(inspect(parser('~~~a~~~')), [['~~~', 'a', '~~~'], '']);
25
- assert.deepStrictEqual(inspect(parser('===a===')), [['===', 'a', '==='], '']);
26
23
  assert.deepStrictEqual(inspect(parser('* a*')), [['*', ' ', 'a', '*'], '']);
27
24
  assert.deepStrictEqual(inspect(parser('** a**')), [['**', ' ', 'a', '**'], '']);
28
25
  assert.deepStrictEqual(inspect(parser('*** a***')), [['***', ' ', 'a', '***'], '']);
@@ -47,6 +44,11 @@ describe('Unit: parser/inline', () => {
47
44
  assert.deepStrictEqual(inspect(parser('*a ***b****')), [['<em>a <em><strong>b</strong></em></em>'], '']);
48
45
  assert.deepStrictEqual(inspect(parser('*a ***b****c')), [['<em>a <em><strong>b</strong></em></em>', 'c'], '']);
49
46
  assert.deepStrictEqual(inspect(parser('**a*')), [['**', 'a', '*'], '']);
47
+ assert.deepStrictEqual(inspect(parser('**a*b')), [['**', 'a', '*', 'b'], '']);
48
+ assert.deepStrictEqual(inspect(parser('**a*b*')), [['**', 'a', '<em>b</em>'], '']);
49
+ assert.deepStrictEqual(inspect(parser('**a*b*c')), [['**', 'a', '<em>b</em>', 'c'], '']);
50
+ assert.deepStrictEqual(inspect(parser('**a*b*c*')), [['**', 'a', '<em>b</em>', 'c', '*'], '']);
51
+ assert.deepStrictEqual(inspect(parser('**a*b*c**')), [['<strong>a<em>b</em>c</strong>'], '']);
50
52
  assert.deepStrictEqual(inspect(parser('**a*b**')), [['**', 'a', '<em>b</em>', '*'], '']);
51
53
  assert.deepStrictEqual(inspect(parser('**a*b**c')), [['**', 'a', '*', 'b', '**', 'c'], '']);
52
54
  assert.deepStrictEqual(inspect(parser('**a*b***')), [['<strong>a<em>b</em></strong>'], '']);
@@ -61,41 +63,13 @@ describe('Unit: parser/inline', () => {
61
63
  assert.deepStrictEqual(inspect(parser('**a **b****c')), [['<strong>a <strong>b</strong></strong>', 'c'], '']);
62
64
  assert.deepStrictEqual(inspect(parser('**a ***b*****')), [['<strong>a <em><strong>b</strong></em></strong>'], '']);
63
65
  assert.deepStrictEqual(inspect(parser('**a ***b*****c')), [['<strong>a <em><strong>b</strong></em></strong>', 'c'], '']);
64
- assert.deepStrictEqual(inspect(parser('***a*b**')), [['<strong><em>a</em>b</strong>'], '']);
65
- assert.deepStrictEqual(inspect(parser('***a*b**c')), [['<strong><em>a</em>b</strong>', 'c'], '']);
66
- assert.deepStrictEqual(inspect(parser('***a*b*c***')), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
67
- assert.deepStrictEqual(inspect(parser('***a*b*c***d')), [['<strong><em>a</em>b<em>c</em></strong>', 'd'], '']);
68
- assert.deepStrictEqual(inspect(parser('***a*b**c****')), [['<strong><em>a</em>b</strong>', 'c', '****'], '']);
69
- assert.deepStrictEqual(inspect(parser('***a* **b****')), [['<strong><em>a</em> <strong>b</strong></strong>'], '']);
70
- assert.deepStrictEqual(inspect(parser('***a*\\ **b****')), [['<strong><em>a</em> <strong>b</strong></strong>'], '']);
71
- assert.deepStrictEqual(inspect(parser('***a*&Tab;**b****')), [['<strong><em>a</em>\t<strong>b</strong></strong>'], '']);
72
- assert.deepStrictEqual(inspect(parser('***a*<wbr>**b****')), [['<strong><em>a</em><wbr><strong>b</strong></strong>'], '']);
73
- assert.deepStrictEqual(inspect(parser('***a *b****')), [['<em><strong>a <em>b</em></strong></em>'], '']);
74
- assert.deepStrictEqual(inspect(parser('***a\\ *b****')), [['<em><strong>a <em>b</em></strong></em>'], '']);
75
- assert.deepStrictEqual(inspect(parser('***a&Tab;*b****')), [['<em><strong>a\t<em>b</em></strong></em>'], '']);
76
- assert.deepStrictEqual(inspect(parser('***a<wbr>*b****')), [['<em><strong>a<wbr><em>b</em></strong></em>'], '']);
77
- assert.deepStrictEqual(inspect(parser('***a*b **')), [['**', '<em>a</em>', 'b', ' ', '**'], '']);
78
- assert.deepStrictEqual(inspect(parser('***a*b\\ **')), [['**', '<em>a</em>', 'b', ' ', '**'], '']);
79
- assert.deepStrictEqual(inspect(parser('***a**b*')), [['<em><strong>a</strong>b</em>'], '']);
80
- assert.deepStrictEqual(inspect(parser('***a**b*c')), [['<em><strong>a</strong>b</em>', 'c'], '']);
81
- assert.deepStrictEqual(inspect(parser('***a**b*c**')), [['<em><strong>a</strong>b</em>', 'c', '**'], '']);
82
- assert.deepStrictEqual(inspect(parser('***a**b**c***')), [['<em><strong>a</strong>b<strong>c</strong></em>'], '']);
83
- assert.deepStrictEqual(inspect(parser('***a**b**c***d')), [['<em><strong>a</strong>b<strong>c</strong></em>', 'd'], '']);
84
- assert.deepStrictEqual(inspect(parser('***a** *b**')), [['<em><strong>a</strong> <em>b</em></em>'], '']);
85
- assert.deepStrictEqual(inspect(parser('***a**\\ *b**')), [['<em><strong>a</strong> <em>b</em></em>'], '']);
86
- assert.deepStrictEqual(inspect(parser('***a**&Tab;*b**')), [['<em><strong>a</strong>\t<em>b</em></em>'], '']);
87
- assert.deepStrictEqual(inspect(parser('***a**<wbr>*b**')), [['<em><strong>a</strong><wbr><em>b</em></em>'], '']);
88
- assert.deepStrictEqual(inspect(parser('***a **b*')), [['***', 'a', ' ', '**', 'b', '*'], '']);
89
- assert.deepStrictEqual(inspect(parser('***a\\ **b*')), [['***', 'a', ' ', '**', 'b', '*'], '']);
90
- assert.deepStrictEqual(inspect(parser('***a**b *')), [['*', '<strong>a</strong>', 'b', ' ', '*'], '']);
91
- assert.deepStrictEqual(inspect(parser('***a**b\\ *')), [['*', '<strong>a</strong>', 'b', ' ', '*'], '']);
92
66
  assert.deepStrictEqual(inspect(parser('***a*')), [['**', '<em>a</em>'], '']);
93
- assert.deepStrictEqual(inspect(parser('***a**')), [['*', '<strong>a</strong>'], '']);
94
- assert.deepStrictEqual(inspect(parser('***a***')), [['<em><strong>a</strong></em>'], '']);
95
- assert.deepStrictEqual(inspect(parser('***a***b')), [['<em><strong>a</strong></em>', 'b'], '']);
96
- assert.deepStrictEqual(inspect(parser('***a****')), [['<em><strong>a</strong></em>', '*'], '']);
97
- assert.deepStrictEqual(inspect(parser('****a***')), [['****', 'a', '***'], '']);
98
- assert.deepStrictEqual(inspect(parser('****a****')), [['****', 'a', '****'], '']);
67
+ assert.deepStrictEqual(inspect(parser('***a*b')), [['**', '<em>a</em>', 'b'], '']);
68
+ assert.deepStrictEqual(inspect(parser('***a*b*')), [['**', '<em>a</em>', 'b', '*'], '']);
69
+ assert.deepStrictEqual(inspect(parser('***a*b*c')), [['**', '<em>a</em>', 'b', '*', 'c'], '']);
70
+ assert.deepStrictEqual(inspect(parser('***a*b*c*')), [['**', '<em>a</em>', 'b', '<em>c</em>'], '']);
71
+ assert.deepStrictEqual(inspect(parser('***a*b*c**')), [['**', '<em>a</em>', 'b', '<em>c</em>', '*'], '']);
72
+ assert.deepStrictEqual(inspect(parser('***a*b*c***')), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
99
73
  assert.deepStrictEqual(inspect(parser('*(*a*)*')), [['<em><span class="paren">(<em>a</em>)</span></em>'], '']);
100
74
  assert.deepStrictEqual(inspect(parser('**(**a**)**')), [['<strong><span class="paren">(<strong>a</strong>)</span></strong>'], '']);
101
75
  assert.deepStrictEqual(inspect(parser('*++ ++*')), [['<em><ins> </ins></em>'], '']);
@@ -201,7 +175,7 @@ describe('Unit: parser/inline', () => {
201
175
  assert.deepStrictEqual(inspect(parser('*a@b*')), [['<em><a class="email" href="mailto:a@b">a@b</a></em>'], '']);
202
176
  assert.deepStrictEqual(inspect(parser('(a@b)')), [['<span class="paren">(<a class="email" href="mailto:a@b">a@b</a>)</span>'], '']);
203
177
  assert.deepStrictEqual(inspect(parser(' a@b')), [[' ', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
204
- assert.deepStrictEqual(inspect(parser('++a++b@c++')), [['<ins>a</ins>', '<a class="email" href="mailto:b@c">b@c</a>', '++'], '']);
178
+ assert.deepStrictEqual(inspect(parser('++a++b@c++')), [['<ins>a</ins>', '<a class="email" href="mailto:b@c">b@c</a>', '+', '+'], '']);
205
179
  });
206
180
 
207
181
  it('channel', () => {
@@ -5,8 +5,8 @@ import { reference } from './inline/reference';
5
5
  import { template } from './inline/template';
6
6
  import { remark } from './inline/remark';
7
7
  import { extension } from './inline/extension';
8
- import { ruby } from './inline/ruby';
9
8
  import { textlink } from './inline/link';
9
+ import { ruby } from './inline/ruby';
10
10
  import { html } from './inline/html';
11
11
  import { insertion } from './inline/insertion';
12
12
  import { deletion } from './inline/deletion';
@@ -14,6 +14,7 @@ import { mark } from './inline/mark';
14
14
  import { emstrong } from './inline/emstrong';
15
15
  import { strong } from './inline/strong';
16
16
  import { emphasis } from './inline/emphasis';
17
+ import { italic } from './inline/italic';
17
18
  import { math } from './inline/math';
18
19
  import { code } from './inline/code';
19
20
  import { htmlentity } from './inline/htmlentity';
@@ -27,8 +28,8 @@ export import ReferenceParser = InlineParser.ReferenceParser;
27
28
  export import TemplateParser = InlineParser.TemplateParser;
28
29
  export import RemarkParser = InlineParser.RemarkParser;
29
30
  export import ExtensionParser = InlineParser.ExtensionParser;
30
- export import RubyParser = InlineParser.RubyParser;
31
31
  export import LinkParser = InlineParser.LinkParser;
32
+ export import RubyParser = InlineParser.RubyParser;
32
33
  export import HTMLParser = InlineParser.HTMLParser;
33
34
  export import InsertionParser = InlineParser.InsertionParser;
34
35
  export import DeletionParser = InlineParser.DeletionParser;
@@ -36,6 +37,7 @@ export import MarkParser = InlineParser.MarkParser;
36
37
  export import EmStrongParser = InlineParser.EmStrongParser;
37
38
  export import StrongParser = InlineParser.StrongParser;
38
39
  export import EmphasisParser = InlineParser.EmphasisParser;
40
+ export import ItalicParser = InlineParser.ItalicParser;
39
41
  export import MathParser = InlineParser.MathParser;
40
42
  export import CodeParser = InlineParser.CodeParser;
41
43
  export import MediaParser = InlineParser.MediaParser;
@@ -75,11 +77,13 @@ export const inline: InlineParser = lazy(() => union([
75
77
  return deletion(input);
76
78
  case '==':
77
79
  return mark(input);
80
+ case '//':
81
+ return italic(input);
78
82
  }
79
83
  switch (source[0]) {
80
84
  case '[':
81
- return ruby(input)
82
- || textlink(input);
85
+ return textlink(input)
86
+ || ruby(input);
83
87
  case '{':
84
88
  return textlink(input);
85
89
  case '<':
@@ -34,9 +34,6 @@ export const text: TextParser = creation(1, Recursion.ignore, ({ source, context
34
34
  case '\n':
35
35
  return [[html('br')], source.slice(1)];
36
36
  case '*':
37
- case '+':
38
- case '~':
39
- case '=':
40
37
  case '`':
41
38
  return source[1] === source[0]
42
39
  ? repeat({ source, context })
@@ -1,4 +1,6 @@
1
- import { Parser } from '../combinator/data/parser';
1
+ import { min } from 'spica/alias';
2
+ import { Command } from './context';
3
+ import { Parser, Result, Ctx, Tree, Context, eval, exec } from '../combinator/data/parser';
2
4
  import { convert } from '../combinator';
3
5
  import { define } from 'typed-dom/dom';
4
6
 
@@ -7,6 +9,69 @@ export function lineable<T extends HTMLElement | string>(parser: Parser<T>): Par
7
9
  return convert(source => `\r${source}`, parser);
8
10
  }
9
11
 
12
+ export function repeat<P extends Parser<HTMLElement | string>>(symbol: string, parser: P, cons: (nodes: Tree<P>[], context: Context<P>) => Tree<P>[], termination?: (acc: Tree<P>[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | Tree<P>>): P;
13
+ export function repeat<T extends HTMLElement | string>(symbol: string, parser: Parser<T>, cons: (nodes: T[], context: Ctx) => T[], termination: (acc: T[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | T> = (acc, rest, prefix, postfix) => {
14
+ const nodes = [];
15
+ if (prefix > 0) {
16
+ nodes.push(symbol[0].repeat(prefix));
17
+ }
18
+ for (let i = 0; i < acc.length; ++i) {
19
+ nodes.push(...acc[i]);
20
+ }
21
+ if (postfix > 0) {
22
+ nodes.push(rest.slice(0, postfix));
23
+ rest = rest.slice(postfix);
24
+ }
25
+ return [nodes, rest];
26
+ }): Parser<string | T> {
27
+ return input => {
28
+ const { source, context } = input;
29
+ assert(source.startsWith(symbol));
30
+ let acc: T[][] = [];
31
+ let i = symbol.length;
32
+ while (source[i] === source[0]) ++i;
33
+ let rest = source.slice(i);
34
+ let state = false;
35
+ for (; i >= symbol.length; i -= symbol.length) {
36
+ if (acc.length > 0 && rest.startsWith(symbol)) {
37
+ acc = [cons(acc.flat(), context)];
38
+ rest = rest.slice(symbol.length);
39
+ continue;
40
+ }
41
+ const result = parser({ source: rest, context });
42
+ if (result === undefined) break;
43
+ const nodes = eval(result);
44
+ rest = exec(result);
45
+ acc.push(nodes);
46
+ switch (nodes.at(-1)) {
47
+ case Command.Escape:
48
+ assert(!rest.startsWith(symbol));
49
+ nodes.pop();
50
+ state = false;
51
+ break;
52
+ case Command.Separator:
53
+ assert(!rest.startsWith(symbol));
54
+ nodes.pop();
55
+ state = true;
56
+ continue;
57
+ default:
58
+ acc = [cons(acc.flat(), context)];
59
+ state = true;
60
+ continue;
61
+ }
62
+ break;
63
+ }
64
+ if (acc.length === 0) return;
65
+ const prefix = i;
66
+ i = 0;
67
+ for (let len = min(prefix, rest.length); i < len && rest[i] === symbol[0];) {
68
+ ++i;
69
+ }
70
+ const postfix = i;
71
+ return termination(acc, rest, prefix, postfix, state);
72
+ };
73
+ }
74
+
10
75
  export function markInvalid<T extends Element>(
11
76
  el: T,
12
77
  syntax: string,
@@ -45,36 +45,39 @@ function hasVisible(
45
45
  }
46
46
 
47
47
  export function blankWith(delimiter: string | RegExp): RegExp;
48
- export function blankWith(starting: '' | '\n', delimiter: string | RegExp): RegExp;
49
- export function blankWith(starting: '' | '\n', delimiter?: string | RegExp): RegExp {
50
- if (delimiter === undefined) return blankWith('', starting);
48
+ export function blankWith(starts: '' | '\n', delimiter: string | RegExp): RegExp;
49
+ export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegExp {
50
+ if (delimiter === undefined) return blankWith('', starts);
51
51
  return new RegExp(String.raw
52
- `^(?:(?=${
53
- starting
54
- })(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr[^\S\n]*>)${starting && '+'})?${
55
- typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source
52
+ `^(?:(?=${starts})(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr[^\S\n]*>)${
53
+ // 空行除去
54
+ starts && '+'
55
+ })?${
56
+ typeof delimiter === 'string'
57
+ ? delimiter.replace(/[*+()\[\]]/g, '\\$&')
58
+ : delimiter.source
56
59
  }`);
57
60
  }
58
61
 
59
- //export function startLoose<P extends Parser<HTMLElement | string>>(parser: P, except?: string): P;
60
- //export function startLoose<T extends HTMLElement | string>(parser: Parser<T>, except?: string): Parser<T> {
62
+ //export function looseStart<P extends Parser<HTMLElement | string>>(parser: P, except?: string): P;
63
+ //export function looseStart<T extends HTMLElement | string>(parser: Parser<T>, except?: string): Parser<T> {
61
64
  // return input =>
62
- // isStartLoose(input, except)
65
+ // isLooseStart(input, except)
63
66
  // ? parser(input)
64
67
  // : undefined;
65
68
  //}
66
- //const isStartLoose = reduce(({ source, context }: Input<MarkdownParser.Context>, except?: string): boolean => {
67
- // return isStartTight({ source: source.replace(blank.start, ''), context }, except);
69
+ //const isLooseStart = reduce(({ source, context }: Input<MarkdownParser.Context>, except?: string): boolean => {
70
+ // return isTightStart({ source: source.replace(blank.start, ''), context }, except);
68
71
  //}, ({ source }, except = '') => `${source}${Command.Separator}${except}`);
69
72
 
70
- export function startTight<P extends Parser<unknown>>(parser: P, except?: string): P;
71
- export function startTight<T>(parser: Parser<T>, except?: string): Parser<T> {
73
+ export function tightStart<P extends Parser<unknown>>(parser: P, except?: string): P;
74
+ export function tightStart<T>(parser: Parser<T>, except?: string): Parser<T> {
72
75
  return input =>
73
- isStartTight(input, except)
76
+ isTightStart(input, except)
74
77
  ? parser(input)
75
78
  : undefined;
76
79
  }
77
- const isStartTight = reduce((input: Input<MarkdownParser.Context>, except?: string): boolean => {
80
+ const isTightStart = reduce((input: Input<MarkdownParser.Context>, except?: string): boolean => {
78
81
  const { source } = input;
79
82
  if (source === '') return true;
80
83
  if (except && source.slice(0, except.length) === except) return false;
@@ -107,7 +110,7 @@ const isStartTight = reduce((input: Input<MarkdownParser.Context>, except?: stri
107
110
  }
108
111
  }, ({ source }, except = '') => `${source}${Command.Separator}${except}`);
109
112
 
110
- export function isStartLooseNodes(nodes: readonly (HTMLElement | string)[]): boolean {
113
+ export function isLooseNodeStart(nodes: readonly (HTMLElement | string)[]): boolean {
111
114
  if (nodes.length === 0) return true;
112
115
  for (let i = 0; i < nodes.length; ++i) {
113
116
  const node = nodes[i];
@@ -116,11 +119,11 @@ export function isStartLooseNodes(nodes: readonly (HTMLElement | string)[]): boo
116
119
  }
117
120
  return false;
118
121
  }
119
- export function isStartTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
122
+ export function isTightNodeStart(nodes: readonly (HTMLElement | string)[]): boolean {
120
123
  if (nodes.length === 0) return true;
121
124
  return isVisible(nodes[0], 0);
122
125
  }
123
- //export function isEndTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
126
+ //export function isTightNodeEnd(nodes: readonly (HTMLElement | string)[]): boolean {
124
127
  // if (nodes.length === 0) return true;
125
128
  // return isVisible(nodes.at(-1)!, -1);
126
129
  //}