securemark 0.292.0 → 0.293.1

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 (127) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +597 -416
  3. package/markdown.d.ts +35 -60
  4. package/package.json +1 -1
  5. package/src/combinator/control/constraint/contract.ts +6 -15
  6. package/src/combinator/control/manipulation/clear.ts +3 -4
  7. package/src/combinator/control/manipulation/fence.ts +3 -1
  8. package/src/combinator/control/manipulation/indent.ts +5 -11
  9. package/src/combinator/control/manipulation/match.ts +3 -2
  10. package/src/combinator/control/manipulation/recovery.ts +1 -1
  11. package/src/combinator/control/manipulation/scope.ts +3 -10
  12. package/src/combinator/control/manipulation/surround.ts +4 -24
  13. package/src/combinator/data/parser/context/delimiter.ts +10 -9
  14. package/src/combinator/data/parser/context.ts +31 -0
  15. package/src/combinator/data/parser/inits.ts +1 -1
  16. package/src/combinator/data/parser/sequence.ts +1 -1
  17. package/src/combinator/data/parser/some.test.ts +1 -1
  18. package/src/combinator/data/parser/some.ts +4 -5
  19. package/src/combinator/data/parser.ts +0 -1
  20. package/src/combinator.ts +0 -1
  21. package/src/parser/api/parse.test.ts +16 -8
  22. package/src/parser/api/parse.ts +1 -2
  23. package/src/parser/autolink.test.ts +7 -7
  24. package/src/parser/autolink.ts +2 -4
  25. package/src/parser/block/blockquote.test.ts +1 -1
  26. package/src/parser/block/blockquote.ts +7 -7
  27. package/src/parser/block/codeblock.ts +8 -8
  28. package/src/parser/block/dlist.test.ts +1 -1
  29. package/src/parser/block/dlist.ts +5 -6
  30. package/src/parser/block/extension/aside.ts +4 -4
  31. package/src/parser/block/extension/example.ts +4 -4
  32. package/src/parser/block/extension/fig.ts +5 -5
  33. package/src/parser/block/extension/figbase.ts +1 -1
  34. package/src/parser/block/extension/figure.test.ts +1 -1
  35. package/src/parser/block/extension/figure.ts +6 -6
  36. package/src/parser/block/extension/message.ts +4 -4
  37. package/src/parser/block/extension/placeholder.ts +8 -8
  38. package/src/parser/block/extension/table.test.ts +1 -1
  39. package/src/parser/block/extension/table.ts +32 -22
  40. package/src/parser/block/extension.ts +3 -3
  41. package/src/parser/block/heading.test.ts +1 -1
  42. package/src/parser/block/heading.ts +2 -2
  43. package/src/parser/block/ilist.test.ts +3 -3
  44. package/src/parser/block/ilist.ts +5 -5
  45. package/src/parser/block/mathblock.ts +8 -8
  46. package/src/parser/block/mediablock.ts +6 -6
  47. package/src/parser/block/olist.test.ts +16 -14
  48. package/src/parser/block/olist.ts +13 -13
  49. package/src/parser/block/pagebreak.ts +1 -1
  50. package/src/parser/block/paragraph.test.ts +3 -4
  51. package/src/parser/block/paragraph.ts +1 -2
  52. package/src/parser/block/reply/cite.ts +7 -9
  53. package/src/parser/block/reply/quote.ts +6 -6
  54. package/src/parser/block/reply.ts +3 -3
  55. package/src/parser/block/sidefence.ts +3 -3
  56. package/src/parser/block/table.ts +13 -13
  57. package/src/parser/block/ulist.test.ts +8 -7
  58. package/src/parser/block/ulist.ts +6 -7
  59. package/src/parser/block.ts +48 -15
  60. package/src/parser/context.ts +4 -4
  61. package/src/parser/header.ts +3 -3
  62. package/src/parser/inline/annotation.ts +1 -1
  63. package/src/parser/inline/autolink/account.test.ts +8 -7
  64. package/src/parser/inline/autolink/account.ts +11 -8
  65. package/src/parser/inline/autolink/anchor.test.ts +1 -1
  66. package/src/parser/inline/autolink/anchor.ts +21 -17
  67. package/src/parser/inline/autolink/channel.test.ts +8 -8
  68. package/src/parser/inline/autolink/channel.ts +40 -15
  69. package/src/parser/inline/autolink/email.test.ts +15 -15
  70. package/src/parser/inline/autolink/email.ts +5 -7
  71. package/src/parser/inline/autolink/hashnum.test.ts +4 -9
  72. package/src/parser/inline/autolink/hashnum.ts +8 -4
  73. package/src/parser/inline/autolink/hashtag.test.ts +9 -10
  74. package/src/parser/inline/autolink/hashtag.ts +9 -6
  75. package/src/parser/inline/autolink/url.test.ts +72 -74
  76. package/src/parser/inline/autolink/url.ts +19 -24
  77. package/src/parser/inline/autolink.ts +21 -24
  78. package/src/parser/inline/bracket.ts +3 -3
  79. package/src/parser/inline/code.ts +2 -2
  80. package/src/parser/inline/deletion.test.ts +2 -2
  81. package/src/parser/inline/deletion.ts +1 -1
  82. package/src/parser/inline/emphasis.test.ts +5 -5
  83. package/src/parser/inline/emphasis.ts +2 -7
  84. package/src/parser/inline/emstrong.test.ts +14 -14
  85. package/src/parser/inline/emstrong.ts +4 -16
  86. package/src/parser/inline/extension/index.test.ts +5 -3
  87. package/src/parser/inline/extension/index.ts +8 -10
  88. package/src/parser/inline/extension/indexer.test.ts +1 -2
  89. package/src/parser/inline/extension/indexer.ts +3 -3
  90. package/src/parser/inline/extension/label.ts +1 -1
  91. package/src/parser/inline/extension/placeholder.ts +1 -1
  92. package/src/parser/inline/html.test.ts +2 -2
  93. package/src/parser/inline/html.ts +21 -20
  94. package/src/parser/inline/htmlentity.ts +4 -4
  95. package/src/parser/inline/insertion.test.ts +2 -2
  96. package/src/parser/inline/insertion.ts +1 -1
  97. package/src/parser/inline/italic.test.ts +9 -9
  98. package/src/parser/inline/italic.ts +1 -1
  99. package/src/parser/inline/link.ts +14 -19
  100. package/src/parser/inline/mark.test.ts +5 -5
  101. package/src/parser/inline/mark.ts +1 -1
  102. package/src/parser/inline/math.test.ts +6 -6
  103. package/src/parser/inline/math.ts +9 -13
  104. package/src/parser/inline/media.ts +9 -14
  105. package/src/parser/inline/reference.test.ts +4 -4
  106. package/src/parser/inline/reference.ts +5 -5
  107. package/src/parser/inline/remark.test.ts +19 -23
  108. package/src/parser/inline/remark.ts +17 -19
  109. package/src/parser/inline/ruby.ts +5 -3
  110. package/src/parser/inline/shortmedia.ts +6 -9
  111. package/src/parser/inline/strong.test.ts +5 -5
  112. package/src/parser/inline/strong.ts +2 -7
  113. package/src/parser/inline.test.ts +25 -23
  114. package/src/parser/inline.ts +21 -9
  115. package/src/parser/segment.ts +23 -5
  116. package/src/parser/source/escapable.test.ts +6 -6
  117. package/src/parser/source/escapable.ts +20 -5
  118. package/src/parser/source/line.ts +28 -4
  119. package/src/parser/source/str.ts +5 -27
  120. package/src/parser/source/text.test.ts +60 -60
  121. package/src/parser/source/text.ts +121 -6
  122. package/src/parser/source/unescapable.test.ts +8 -8
  123. package/src/parser/source/unescapable.ts +19 -3
  124. package/src/parser/util.ts +3 -3
  125. package/src/parser/visibility.ts +45 -40
  126. package/src/combinator/control/manipulation/trim.test.ts +0 -23
  127. package/src/combinator/control/manipulation/trim.ts +0 -17
@@ -1,6 +1,30 @@
1
1
  import { AnyLineParser, EmptyLineParser, ContentLineParser } from '../source';
2
- import { line, isBlank } from '../../combinator';
3
2
 
4
- export const anyline: AnyLineParser = line(() => [[]]);
5
- export const emptyline: EmptyLineParser = line(({ context: { source } }) => isBlank(source) ? [[]] : undefined);
6
- export const contentline: ContentLineParser = line(({ context: { source } }) => !isBlank(source) ? [[]] : undefined);
3
+ export const anyline: AnyLineParser = input => {
4
+ const { context } = input;
5
+ const { source, position } = context;
6
+ context.position = source.indexOf('\n', position) + 1 || source.length;
7
+ return [[]];
8
+ };
9
+ const regEmptyline = /[^\S\n]*(?:$|\n)/y;
10
+ export const emptyline: EmptyLineParser = input => {
11
+ const { context } = input;
12
+ const { source, position } = context;
13
+ regEmptyline.lastIndex = position;
14
+ regEmptyline.test(source);
15
+ const i = regEmptyline.lastIndex;
16
+ if (i === 0) return;
17
+ context.position = i;
18
+ return [[]];
19
+ };
20
+ const regContentline = /[^\S\n]*\S[^\n]*(?:$|\n)/y;
21
+ export const contentline: ContentLineParser = input => {
22
+ const { context } = input;
23
+ const { source, position } = context;
24
+ regContentline.lastIndex = position;
25
+ regContentline.test(source);
26
+ const i = regContentline.lastIndex;
27
+ if (i === 0) return;
28
+ context.position = i;
29
+ return [[]];
30
+ }
@@ -1,32 +1,10 @@
1
1
  import { StrParser } from '../source';
2
- import { Parser, Context } from '../../combinator/data/parser';
3
- import { consume } from '../../combinator';
2
+ import { Parser } from '../../combinator/data/parser';
3
+ import { matcher } from '../../combinator';
4
4
 
5
- export function str(pattern: string | RegExp, not?: string): StrParser;
6
- export function str(pattern: string | RegExp, not?: string): Parser<string, Context<StrParser>, []> {
7
- assert(pattern);
8
- const count = typeof pattern === 'object'
9
- ? /[^^\\*+][*+]/.test(pattern.source)
10
- : false;
11
- return typeof pattern === 'string'
12
- ? ({ context }) => {
13
- const { source, position } = context;
14
- if (position === source.length) return;
15
- if (!source.startsWith(pattern, position)) return;
16
- if (not && source.startsWith(not, position + pattern.length)) return;
17
- context.position += pattern.length;
18
- return [[pattern]];
19
- }
20
- : ({ context }) => {
21
- const { source, position } = context;
22
- if (position === source.length) return;
23
- const m = source.slice(position).match(pattern);
24
- if (m === null) return;
25
- count && consume(m[0].length, context);
26
- if (not && source.slice(position + m[0].length, position + m[0].length + not.length) === not) return;
27
- context.position += m[0].length;
28
- return [[m[0]]];
29
- };
5
+ export function str(pattern: string | RegExp): StrParser;
6
+ export function str(pattern: string | RegExp): Parser<string> {
7
+ return matcher(pattern, true);
30
8
  }
31
9
 
32
10
  export function strs(pattern: string): StrParser;
@@ -14,8 +14,8 @@ describe('Unit: parser/source/text', () => {
14
14
 
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
- assert.deepStrictEqual(inspect(parser('ab'), ctx), [['a', 'b'], '']);
18
- assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['0', '9', 'あ', 'い', 'A', 'Z', 'a', 'z'], '']);
17
+ assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
19
19
  assert.deepStrictEqual(inspect(parser('a\nb'), ctx), [['a', '<br>', 'b'], '']);
20
20
  });
21
21
 
@@ -34,41 +34,41 @@ describe('Unit: parser/source/text', () => {
34
34
  });
35
35
 
36
36
  it('space', () => {
37
- assert.deepStrictEqual(inspect(parser(' '), ctx), [[' '], '']);
38
- assert.deepStrictEqual(inspect(parser(' '), ctx), [[' ', ' '], '']);
39
- assert.deepStrictEqual(inspect(parser(' '), ctx), [[' ', ' ', ' '], '']);
40
- assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', '<br>'], '']);
41
- assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', ' ', '<br>'], '']);
42
- assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', '<br>'], '']);
43
- assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', ' ', '<br>'], '']);
44
- assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', 'a'], '']);
45
- assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' ', 'a'], '']);
46
- assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' ', ' ', 'a'], '']);
47
- assert.deepStrictEqual(inspect(parser('a '), ctx), [['a', ' '], '']);
48
- assert.deepStrictEqual(inspect(parser('a '), ctx), [['a', ' ', ' '], '']);
49
- assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', ' ', '<br>'], '']);
50
- assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', ' ', ' ', '<br>'], '']);
51
- assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', '<br>'], '']);
52
- assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', ' ', '<br>'], '']);
53
- assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' ', 'b'], '']);
54
- assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' ', ' ', 'b'], '']);
55
- assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' ', ' ', ' ', 'b'], '']);
37
+ assert.deepStrictEqual(inspect(parser(' '), ctx), [[], '']);
38
+ assert.deepStrictEqual(inspect(parser(' '), ctx), [[], '']);
39
+ assert.deepStrictEqual(inspect(parser(' '), ctx), [[], '']);
40
+ assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
41
+ assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
42
+ assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
43
+ assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
44
+ assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' a'], '']);
45
+ assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
46
+ assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
47
+ assert.deepStrictEqual(inspect(parser('a '), ctx), [['a'], '']);
48
+ assert.deepStrictEqual(inspect(parser('a '), ctx), [['a'], '']);
49
+ assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
50
+ assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
51
+ assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
52
+ assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
53
+ assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a b'], '']);
54
+ assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
55
+ assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
56
56
  });
57
57
 
58
58
  it('hardbreak', () => {
59
59
  assert.deepStrictEqual(inspect(parser('\n'), ctx), [['<br>'], '']);
60
- assert.deepStrictEqual(inspect(parser('\n '), ctx), [['<br>', ' '], '']);
61
- assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', '<br>'], '']);
60
+ assert.deepStrictEqual(inspect(parser('\n '), ctx), [['<br>'], '']);
61
+ assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
62
62
  assert.deepStrictEqual(inspect(parser('\n\n'), ctx), [['<br>', '<br>'], '']);
63
- assert.deepStrictEqual(inspect(parser(' \n\n'), ctx), [[' ', '<br>', '<br>'], '']);
64
- assert.deepStrictEqual(inspect(parser('\n \n'), ctx), [['<br>', ' ', '<br>'], '']);
65
- assert.deepStrictEqual(inspect(parser('\n\n '), ctx), [['<br>', '<br>', ' '], '']);
63
+ assert.deepStrictEqual(inspect(parser(' \n\n'), ctx), [['<br>', '<br>'], '']);
64
+ assert.deepStrictEqual(inspect(parser('\n \n'), ctx), [['<br>', '<br>'], '']);
65
+ assert.deepStrictEqual(inspect(parser('\n\n '), ctx), [['<br>', '<br>'], '']);
66
66
  assert.deepStrictEqual(inspect(parser('。\n'), ctx), [['。', '<br>'], '']);
67
67
  });
68
68
 
69
69
  it('softbreak', () => {
70
70
  assert.deepStrictEqual(inspect(parser('\\\n'), ctx), [['<br>'], '']);
71
- assert.deepStrictEqual(inspect(parser('\\\n '), ctx), [['<br>', ' '], '']);
71
+ assert.deepStrictEqual(inspect(parser('\\\n '), ctx), [['<br>'], '']);
72
72
  assert.deepStrictEqual(inspect(parser('\\\na'), ctx), [['<br>', 'a'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('a\\\n'), ctx), [['a', '<br>'], '']);
74
74
  assert.deepStrictEqual(inspect(parser('a\\\nb\\\n'), ctx), [['a', '<br>', 'b', '<br>'], '']);
@@ -76,45 +76,45 @@ describe('Unit: parser/source/text', () => {
76
76
  });
77
77
 
78
78
  it('account', () => {
79
- assert.deepStrictEqual(inspect(parser('@0'), ctx), [['@', '0'], '']);
80
- assert.deepStrictEqual(inspect(parser('_@0'), ctx), [['_', '@', '0'], '']);
81
- assert.deepStrictEqual(inspect(parser('$@0'), ctx), [['$', '@', '0'], '']);
82
- assert.deepStrictEqual(inspect(parser('+@0'), ctx), [['+', '@', '0'], '']);
83
- assert.deepStrictEqual(inspect(parser('-@0'), ctx), [['-', '@', '0'], '']);
84
- assert.deepStrictEqual(inspect(parser('0@0'), ctx), [['0', '@', '0'], '']);
85
- assert.deepStrictEqual(inspect(parser('a@0'), ctx), [['a', '@', '0'], '']);
86
- assert.deepStrictEqual(inspect(parser('A@0'), ctx), [['A', '@', '0'], '']);
87
- assert.deepStrictEqual(inspect(parser('aA@0'), ctx), [['a', 'A', '@', '0'], '']);
88
- assert.deepStrictEqual(inspect(parser(' @0'), ctx), [[' ', '@', '0'], '']);
89
- assert.deepStrictEqual(inspect(parser('@@0'), ctx), [['@', '@', '0'], '']);
79
+ assert.deepStrictEqual(inspect(parser('@0'), ctx), [['@0'], '']);
80
+ assert.deepStrictEqual(inspect(parser('_@0'), ctx), [['_', '@0'], '']);
81
+ assert.deepStrictEqual(inspect(parser('$@0'), ctx), [['$', '@0'], '']);
82
+ assert.deepStrictEqual(inspect(parser('+@0'), ctx), [['+', '@0'], '']);
83
+ assert.deepStrictEqual(inspect(parser('-@0'), ctx), [['-', '@0'], '']);
84
+ assert.deepStrictEqual(inspect(parser('0@0'), ctx), [['0', '@0'], '']);
85
+ assert.deepStrictEqual(inspect(parser('a@0'), ctx), [['a', '@0'], '']);
86
+ assert.deepStrictEqual(inspect(parser('A@0'), ctx), [['A', '@0'], '']);
87
+ assert.deepStrictEqual(inspect(parser('aA@0'), ctx), [['aA', '@0'], '']);
88
+ assert.deepStrictEqual(inspect(parser(' @0'), ctx), [[' ', '@0'], '']);
89
+ assert.deepStrictEqual(inspect(parser('@@0'), ctx), [['@', '@0'], '']);
90
90
  });
91
91
 
92
92
  it('hashtag', () => {
93
- assert.deepStrictEqual(inspect(parser('#0'), ctx), [['#', '0'], '']);
94
- assert.deepStrictEqual(inspect(parser('_#0'), ctx), [['_', '#', '0'], '']);
95
- assert.deepStrictEqual(inspect(parser('$#0'), ctx), [['$', '#', '0'], '']);
96
- assert.deepStrictEqual(inspect(parser('+#0'), ctx), [['+', '#', '0'], '']);
97
- assert.deepStrictEqual(inspect(parser('-#0'), ctx), [['-', '#', '0'], '']);
98
- assert.deepStrictEqual(inspect(parser('0#0'), ctx), [['0', '#', '0'], '']);
99
- assert.deepStrictEqual(inspect(parser('a#0'), ctx), [['a', '#', '0'], '']);
100
- assert.deepStrictEqual(inspect(parser('A#0'), ctx), [['A', '#', '0'], '']);
101
- assert.deepStrictEqual(inspect(parser('aA#0'), ctx), [['a', 'A', '#', '0'], '']);
102
- assert.deepStrictEqual(inspect(parser(' #0'), ctx), [[' ', '#', '0'], '']);
103
- assert.deepStrictEqual(inspect(parser('##0'), ctx), [['#', '#', '0'], '']);
93
+ assert.deepStrictEqual(inspect(parser('#0'), ctx), [['#0'], '']);
94
+ assert.deepStrictEqual(inspect(parser('_#0'), ctx), [['_', '#0'], '']);
95
+ assert.deepStrictEqual(inspect(parser('$#0'), ctx), [['$', '#0'], '']);
96
+ assert.deepStrictEqual(inspect(parser('+#0'), ctx), [['+', '#0'], '']);
97
+ assert.deepStrictEqual(inspect(parser('-#0'), ctx), [['-', '#0'], '']);
98
+ assert.deepStrictEqual(inspect(parser('0#0'), ctx), [['0', '#0'], '']);
99
+ assert.deepStrictEqual(inspect(parser('a#0'), ctx), [['a', '#0'], '']);
100
+ assert.deepStrictEqual(inspect(parser('A#0'), ctx), [['A', '#0'], '']);
101
+ assert.deepStrictEqual(inspect(parser('aA#0'), ctx), [['aA', '#0'], '']);
102
+ assert.deepStrictEqual(inspect(parser(' #0'), ctx), [[' ', '#0'], '']);
103
+ assert.deepStrictEqual(inspect(parser('##0'), ctx), [['#', '#0'], '']);
104
104
  });
105
105
 
106
106
  it('anchor', () => {
107
- assert.deepStrictEqual(inspect(parser('>>0'), ctx), [['>', '>', '0'], '']);
108
- assert.deepStrictEqual(inspect(parser('_>>0'), ctx), [['_', '>', '>', '0'], '']);
109
- assert.deepStrictEqual(inspect(parser('$>>0'), ctx), [['$', '>', '>', '0'], '']);
110
- assert.deepStrictEqual(inspect(parser('+>>0'), ctx), [['+', '>', '>', '0'], '']);
111
- assert.deepStrictEqual(inspect(parser('->>0'), ctx), [['-', '>', '>', '0'], '']);
112
- assert.deepStrictEqual(inspect(parser('0>>0'), ctx), [['0', '>', '>', '0'], '']);
113
- assert.deepStrictEqual(inspect(parser('a>>0'), ctx), [['a', '>', '>', '0'], '']);
114
- assert.deepStrictEqual(inspect(parser('A>>0'), ctx), [['A', '>', '>', '0'], '']);
115
- assert.deepStrictEqual(inspect(parser('aA>>0'), ctx), [['a', 'A', '>', '>', '0'], '']);
116
- assert.deepStrictEqual(inspect(parser(' >>0'), ctx), [[' ', '>', '>', '0'], '']);
117
- assert.deepStrictEqual(inspect(parser('>>>>0'), ctx), [['>', '>', '>', '>', '0'], '']);
107
+ assert.deepStrictEqual(inspect(parser('>>0'), ctx), [['>', '>0'], '']);
108
+ assert.deepStrictEqual(inspect(parser('_>>0'), ctx), [['_', '>', '>0'], '']);
109
+ assert.deepStrictEqual(inspect(parser('$>>0'), ctx), [['$', '>', '>0'], '']);
110
+ assert.deepStrictEqual(inspect(parser('+>>0'), ctx), [['+', '>', '>0'], '']);
111
+ assert.deepStrictEqual(inspect(parser('->>0'), ctx), [['-', '>', '>0'], '']);
112
+ assert.deepStrictEqual(inspect(parser('0>>0'), ctx), [['0', '>', '>0'], '']);
113
+ assert.deepStrictEqual(inspect(parser('a>>0'), ctx), [['a', '>', '>0'], '']);
114
+ assert.deepStrictEqual(inspect(parser('A>>0'), ctx), [['A', '>', '>0'], '']);
115
+ assert.deepStrictEqual(inspect(parser('aA>>0'), ctx), [['aA', '>', '>0'], '']);
116
+ assert.deepStrictEqual(inspect(parser(' >>0'), ctx), [[' ', '>', '>0'], '']);
117
+ assert.deepStrictEqual(inspect(parser('>>>>0'), ctx), [['>', '>', '>', '>0'], '']);
118
118
  });
119
119
 
120
120
  });
@@ -3,13 +3,17 @@ import { Command } from '../context';
3
3
  import { union, consume, focus } from '../../combinator';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
+ export const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|+~=/]|\s(?:\\?(?:$|\s)|[$*%|]|([+~=])\1)|\/{3}|:\/\/|\n)/g;
7
+ export const nonWhitespace = /[\S\r\n]/g;
8
+
6
9
  export const text: TextParser = input => {
7
10
  const { context } = input;
8
11
  const { source, position } = context;
9
12
  if (position === source.length) return;
13
+ const char = source[position];
10
14
  consume(1, context);
11
15
  context.position += 1;
12
- switch (source[position]) {
16
+ switch (char) {
13
17
  case '\r':
14
18
  assert(!source.includes('\r', position + 1));
15
19
  consume(-1, context);
@@ -20,19 +24,41 @@ export const text: TextParser = input => {
20
24
  case undefined:
21
25
  return [[]];
22
26
  case '\n':
23
- assert(source[0] !== Command.Escape);
27
+ assert(char !== Command.Escape);
24
28
  return [[]];
25
29
  default:
26
30
  consume(1, context);
27
31
  context.position += 1;
28
- return [[source.slice(position + 1, position + 2)]];
32
+ return [[source.slice(position + 1, context.position)]];
29
33
  }
30
34
  case '\n':
31
35
  context.linebreak ||= source.length - position;
32
36
  return [[html('br')]];
33
37
  default:
34
- assert(source[position] !== '\n');
35
- return [[source[position]]];
38
+ assert(char !== '\n');
39
+ if (context.sequential) return [[char]];
40
+ nonWhitespace.lastIndex = position + 1;
41
+ const b = isBlank(source, position);
42
+ let i = b
43
+ ? nonWhitespace.test(source)
44
+ ? nonWhitespace.lastIndex - 1
45
+ : source.length
46
+ : next(source, position, delimiter);
47
+ assert(i > position);
48
+ const lineend = 0
49
+ || b && i === source.length
50
+ || b && source[i] === '\n'
51
+ || b && source[i] === '\\' && source[i + 1] === '\n';
52
+ i -= position;
53
+ i = lineend ? i : i - +b || 1;
54
+ consume(i - 1, context);
55
+ context.position += i - 1;
56
+ const linestart = position === 0 || source[position - 1] === '\n';
57
+ i = linestart && b && i >= 3 ? i - 3 : 0;
58
+ i += position;
59
+ return i === context.position || b && !linestart || lineend
60
+ ? [[]]
61
+ : [[source.slice(i, context.position)]];
36
62
  }
37
63
  };
38
64
 
@@ -40,10 +66,99 @@ export const txt: TxtParser = union([
40
66
  text,
41
67
  ]) as TxtParser;
42
68
 
43
- export const linebreak: LinebreakParser = focus(/^[\r\n]/, union([
69
+ export const linebreak: LinebreakParser = focus(/[\r\n]/y, union([
44
70
  text,
45
71
  ])) as LinebreakParser;
46
72
 
73
+ export function next(source: string, position: number, delimiter: RegExp): number {
74
+ delimiter.lastIndex = position + 1;
75
+ delimiter.test(source);
76
+ let index = delimiter.lastIndex;
77
+ if (index === 0) return source.length;
78
+ assert(index > position);
79
+ const char = source[index];
80
+ switch (char) {
81
+ case ':':
82
+ index = backToUrlHead(source, position, index);
83
+ break;
84
+ case '@':
85
+ index = backToEmailHead(source, position, index);
86
+ break;
87
+ }
88
+ if (index > position + 1) switch (char) {
89
+ case '*':
90
+ case '+':
91
+ case '~':
92
+ case '=':
93
+ case '/':
94
+ case '%':
95
+ case '|':
96
+ index -= /\s/.test(source[index - 1]) ? 1 : 0;
97
+ }
98
+ assert(index > position);
99
+ return index;
100
+ }
101
+ export function backToUrlHead(source: string, position: number, index: number): number {
102
+ const delim = index;
103
+ let state = false;
104
+ let offset = 0;
105
+ for (let i = index; --i > position;) {
106
+ index = i;
107
+ const char = source[i];
108
+ if (state) switch (char) {
109
+ case '.':
110
+ case '+':
111
+ case '-':
112
+ state = false;
113
+ offset = 1;
114
+ continue;
115
+ }
116
+ if (isAlphanumeric(char)) {
117
+ state = true;
118
+ offset = 0;
119
+ continue;
120
+ }
121
+ break;
122
+ }
123
+ if (index === position + 1 && offset === 0 && isAlphanumeric(source[index - 1])) {
124
+ return delim;
125
+ }
126
+ return index + offset;
127
+ }
128
+ export function backToEmailHead(source: string, position: number, index: number): number {
129
+ const delim = index;
130
+ let state = false;
131
+ let offset = 0;
132
+ for (let i = index; --i > position;) {
133
+ index = i;
134
+ const char = source[i];
135
+ if (state) switch (char) {
136
+ case '_':
137
+ case '.':
138
+ case '+':
139
+ case '-':
140
+ state = false;
141
+ offset = 1;
142
+ continue;
143
+ }
144
+ if (isAlphanumeric(char)) {
145
+ state = true;
146
+ offset = 0;
147
+ continue;
148
+ }
149
+ break;
150
+ }
151
+ if (index === position + 1 && offset === 0 && isAlphanumeric(source[index - 1])) {
152
+ return delim;
153
+ }
154
+ return index + offset;
155
+ }
156
+
157
+ const blank = /\s(?:$|\s|\\\n)/y;
158
+ export function isBlank(source: string, position: number): boolean {
159
+ blank.lastIndex = position;
160
+ return blank.test(source);
161
+ }
47
162
  export function isAlphanumeric(char: string): boolean {
48
163
  assert(char.length === 1);
49
164
  if (char < '0' || '\x7F' < char) return false;
@@ -14,17 +14,17 @@ describe('Unit: parser/source/unescapable', () => {
14
14
 
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
- assert.deepStrictEqual(inspect(parser('ab'), ctx), [['a', 'b'], '']);
18
- assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['0', '9', 'あ', 'い', 'A', 'Z', 'a', 'z'], '']);
17
+ assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09', 'あいAZaz'], '']);
19
19
  });
20
20
 
21
21
  it('space', () => {
22
22
  assert.deepStrictEqual(inspect(parser(' '), ctx), [[' '], '']);
23
- assert.deepStrictEqual(inspect(parser(' '), ctx), [[' ', ' '], '']);
24
- assert.deepStrictEqual(inspect(parser(' '), ctx), [[' ', ' ', ' '], '']);
23
+ assert.deepStrictEqual(inspect(parser(' '), ctx), [[' '], '']);
24
+ assert.deepStrictEqual(inspect(parser(' '), ctx), [[' '], '']);
25
25
  assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', '<br>'], '']);
26
- assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', ' ', '<br>'], '']);
27
- assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', ' ', ' ', '<br>'], '']);
26
+ assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', '<br>'], '']);
27
+ assert.deepStrictEqual(inspect(parser(' \n'), ctx), [[' ', '<br>'], '']);
28
28
  });
29
29
 
30
30
  it('linebreak', () => {
@@ -37,8 +37,8 @@ describe('Unit: parser/source/unescapable', () => {
37
37
  assert.deepStrictEqual(inspect(parser('\\\\\\'), ctx), [['\\', '\\', '\\'], '']);
38
38
  assert.deepStrictEqual(inspect(parser('\\ '), ctx), [['\\', ' '], '']);
39
39
  assert.deepStrictEqual(inspect(parser('\\_'), ctx), [['\\', '_'], '']);
40
- assert.deepStrictEqual(inspect(parser('\\0'), ctx), [['\\', '0'], '']);
41
- assert.deepStrictEqual(inspect(parser('\\a'), ctx), [['\\', 'a'], '']);
40
+ assert.deepStrictEqual(inspect(parser('\\0'), ctx), [['\\0'], '']);
41
+ assert.deepStrictEqual(inspect(parser('\\a'), ctx), [['\\a'], '']);
42
42
  assert.deepStrictEqual(inspect(parser('\\`'), ctx), [['\\', '`'], '']);
43
43
  assert.deepStrictEqual(inspect(parser('\\ '), ctx), [['\\', ' '], '']);
44
44
  assert.deepStrictEqual(inspect(parser('\\\n'), ctx), [['\\', '<br>'], '']);
@@ -1,14 +1,18 @@
1
1
  import { UnescapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
+ import { nonWhitespace, isBlank, next } from './text';
4
5
  import { html } from 'typed-dom/dom';
5
6
 
7
+ export const delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
8
+
6
9
  export const unescsource: UnescapableSourceParser = ({ context }) => {
7
10
  const { source, position } = context;
8
11
  if (position === source.length) return;
12
+ const char = source[position];
9
13
  consume(1, context);
10
14
  context.position += 1;
11
- switch (source[position]) {
15
+ switch (char) {
12
16
  case '\r':
13
17
  assert(!source.includes('\r', position + 1));
14
18
  consume(-1, context);
@@ -21,7 +25,19 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
21
25
  context.linebreak ||= source.length - position;
22
26
  return [[html('br')]];
23
27
  default:
24
- assert(source[position] !== '\n');
25
- return [[source[position]]];
28
+ assert(char !== '\n');
29
+ if (context.sequential) return [[char]];
30
+ nonWhitespace.lastIndex = position + 1;
31
+ const b = isBlank(source, position);
32
+ let i = b
33
+ ? nonWhitespace.test(source)
34
+ ? nonWhitespace.lastIndex - 1
35
+ : source.length
36
+ : next(source, position, delimiter);
37
+ assert(i > position);
38
+ i -= position;
39
+ consume(i - 1, context);
40
+ context.position += i - 1;
41
+ return [[source.slice(position, context.position)]];
26
42
  }
27
43
  };
@@ -5,10 +5,10 @@ import { Parser, Result, Ctx, Node, Context, eval, failsafe } from '../combinato
5
5
  import { convert } from '../combinator';
6
6
  import { define } from 'typed-dom/dom';
7
7
 
8
- export function lineable<P extends Parser<HTMLElement | string>>(parser: P, trim?: 0 | 1 | -1): P;
9
- export function lineable<N extends HTMLElement | string>(parser: Parser<N>, trim = -1): Parser<N> {
8
+ export function linearize<P extends Parser<HTMLElement | string>>(parser: P, trim?: 0 | 1 | -1): P;
9
+ export function linearize<N extends HTMLElement | string>(parser: Parser<N>, trim = 0): Parser<N> {
10
10
  return convert(
11
- source => `\r${
11
+ source => `${
12
12
  trim === 0
13
13
  ? source
14
14
  : trim > 0
@@ -1,56 +1,53 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
2
  import { Command } from './context';
3
- import { Parser, Input, eval } from '../combinator/data/parser';
4
- import { union, some, verify, convert, fmap } from '../combinator';
3
+ import { Parser, Input, eval, failsafe } from '../combinator/data/parser';
4
+ import { convert, fmap } from '../combinator';
5
5
  import { unsafehtmlentity } from './inline/htmlentity';
6
- import { linebreak, unescsource } from './source';
7
6
  import { invisibleHTMLEntityNames } from './api/normalize';
8
7
  import { push } from 'spica/array';
9
8
 
10
9
  export namespace blank {
11
10
  export const line = new RegExp(
12
- /^(?:\\?[^\S\r\n]|&IHN;|<wbr[^\S\n]*>|\\$)+$/.source
11
+ // TODO: 行全体をエスケープ
12
+ /^(?:[^\S\r\n])*(?!\s)(\\?[^\S\r\n]|&IHN;|<wbr[^\S\n]*>|\\$)+$/mg.source
13
13
  .replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`),
14
14
  'gm');
15
15
  export const start = new RegExp(
16
- /^(?:\\?[^\S\r\n]|&IHN;|<wbr[^\S\n]*>)+/.source
17
- .replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`));
16
+ /(?:\\?[^\S\r\n]|&IHN;|<wbr[^\S\n]*>)+/y.source
17
+ .replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`), 'y');
18
18
  }
19
19
 
20
20
  export function visualize<P extends Parser<HTMLElement | string>>(parser: P): P;
21
21
  export function visualize<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
22
- return union([
23
- convert(
24
- source => source.replace(blank.line, line => line.replace(/[\\&<]/g, `${Command.Escape}$&`)),
25
- verify(parser, (ns, { source, position }) => position === source.length && hasVisible(ns)),
26
- false),
27
- some(union([linebreak, unescsource])),
28
- ]);
29
- }
30
- function hasVisible(
31
- nodes: readonly (HTMLElement | string)[],
32
- ): boolean {
33
- for (let i = 0; i < nodes.length; ++i) {
34
- const node = nodes[i];
35
- if (typeof node === 'string') {
36
- if (node && node.trimStart()) return true;
37
- }
38
- else {
39
- if (node.innerText.trimStart()) return true;
40
- if (node.classList.contains('reference')) return true;
41
- //if (state & State.media ^ State.media &&
42
- // (node.classList.contains('media') || node.getElementsByClassName('media')[0])) return true;
43
- }
44
- }
45
- return false;
22
+ return convert(
23
+ source => source.replace(blank.line, `${Command.Escape}$1`),
24
+ parser,
25
+ false);
46
26
  }
27
+ //function hasVisible(
28
+ // nodes: readonly (HTMLElement | string)[],
29
+ //): boolean {
30
+ // for (let i = 0; i < nodes.length; ++i) {
31
+ // const node = nodes[i];
32
+ // if (typeof node === 'string') {
33
+ // if (node && node.trimStart()) return true;
34
+ // }
35
+ // else {
36
+ // if (node.innerText.trimStart()) return true;
37
+ // if (node.classList.contains('reference')) return true;
38
+ // //if (state & State.media ^ State.media &&
39
+ // // (node.classList.contains('media') || node.getElementsByClassName('media')[0])) return true;
40
+ // }
41
+ // }
42
+ // return false;
43
+ //}
47
44
 
48
45
  export function blankWith(delimiter: string | RegExp): RegExp;
49
46
  export function blankWith(starts: '' | '\n', delimiter: string | RegExp): RegExp;
50
47
  export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegExp {
51
48
  if (delimiter === undefined) return blankWith('', starts);
52
49
  return new RegExp(String.raw
53
- `^(?:(?=${starts})(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr[^\S\n]*>)${
50
+ `(?:(?=${starts})(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr[^\S\n]*>)${
54
51
  // 空行除去
55
52
  // 完全な空行はエスケープ済みなので再帰的バックトラックにはならない。
56
53
  starts && '+'
@@ -58,7 +55,7 @@ export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegEx
58
55
  typeof delimiter === 'string'
59
56
  ? delimiter.replace(/[*+()\[\]]/g, '\\$&')
60
57
  : delimiter.source
61
- }`);
58
+ }`, 'y');
62
59
  }
63
60
 
64
61
  //export function looseStart<P extends Parser<HTMLElement | string>>(parser: P, except?: string): P;
@@ -79,6 +76,7 @@ export function tightStart<N>(parser: Parser<N>, except?: string): Parser<N> {
79
76
  ? parser(input)
80
77
  : undefined;
81
78
  }
79
+ const wbr = /<wbr[^\S\n]*>/y;
82
80
  function isTightStart(input: Input<MarkdownParser.Context>, except?: string): boolean {
83
81
  const { context } = input;
84
82
  const { source, position } = context;
@@ -103,10 +101,11 @@ function isTightStart(input: Input<MarkdownParser.Context>, except?: string): bo
103
101
  context.position = position;
104
102
  return true;
105
103
  case '<':
104
+ wbr.lastIndex = position;
106
105
  switch (true) {
107
106
  case source.length - position >= 5
108
107
  && source.startsWith('<wbr', position)
109
- && (source[position + 5] === '>' || /^<wbr[^\S\n]*>/.test(source.slice(position))):
108
+ && (source[position + 5] === '>' || wbr.test(source)):
110
109
  return false;
111
110
  }
112
111
  return true;
@@ -165,16 +164,22 @@ export function trimBlank<N extends HTMLElement | string>(parser: Parser<N>): Pa
165
164
  }
166
165
  export function trimBlankStart<P extends Parser<unknown>>(parser: P): P;
167
166
  export function trimBlankStart<N>(parser: Parser<N>): Parser<N> {
168
- return convert(
169
- source => source.replace(blank.start, ''),
170
- parser,
171
- true);
167
+ return failsafe(input => {
168
+ const { context } = input;
169
+ const { source, position } = context;
170
+ if (position === source.length) return;
171
+ const reg = blank.start;
172
+ reg.lastIndex = position;
173
+ reg.test(source);
174
+ context.position = reg.lastIndex || position;
175
+ return context.position === source.length
176
+ ? [[]]
177
+ : parser(input);
178
+ });
172
179
  }
173
180
  export function trimBlankEnd<P extends Parser<HTMLElement | string>>(parser: P): P;
174
181
  export function trimBlankEnd<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
175
- return fmap(
176
- parser,
177
- trimBlankNodeEnd);
182
+ return fmap(parser, trimBlankNodeEnd);
178
183
  }
179
184
  //export function trimBlankNode<N extends HTMLElement | string>(nodes: N[]): N[] {
180
185
  // return trimBlankNodeStart(trimBlankNodeEnd(nodes));