securemark 0.293.0 → 0.293.2

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 (66) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +254 -193
  3. package/markdown.d.ts +13 -21
  4. package/package.json +1 -1
  5. package/src/combinator/control/manipulation/scope.ts +3 -4
  6. package/src/combinator/data/parser/context.ts +5 -5
  7. package/src/combinator.ts +0 -1
  8. package/src/parser/api/parse.test.ts +2 -2
  9. package/src/parser/autolink.test.ts +7 -7
  10. package/src/parser/block/blockquote.ts +2 -2
  11. package/src/parser/block/codeblock.ts +7 -7
  12. package/src/parser/block/dlist.ts +1 -2
  13. package/src/parser/block/extension/aside.ts +3 -3
  14. package/src/parser/block/extension/example.ts +3 -3
  15. package/src/parser/block/extension/fig.ts +2 -2
  16. package/src/parser/block/extension/figure.test.ts +1 -1
  17. package/src/parser/block/extension/figure.ts +2 -2
  18. package/src/parser/block/extension/message.ts +3 -3
  19. package/src/parser/block/extension/placeholder.ts +7 -7
  20. package/src/parser/block/extension/table.ts +26 -16
  21. package/src/parser/block/extension.ts +3 -3
  22. package/src/parser/block/ilist.ts +3 -3
  23. package/src/parser/block/mathblock.ts +7 -7
  24. package/src/parser/block/mediablock.ts +6 -6
  25. package/src/parser/block/olist.ts +3 -3
  26. package/src/parser/block/paragraph.test.ts +1 -2
  27. package/src/parser/block/paragraph.ts +1 -2
  28. package/src/parser/block/reply/cite.ts +3 -5
  29. package/src/parser/block/reply/quote.ts +2 -3
  30. package/src/parser/block/sidefence.ts +2 -2
  31. package/src/parser/block/table.ts +5 -5
  32. package/src/parser/block/ulist.ts +2 -3
  33. package/src/parser/block.ts +2 -2
  34. package/src/parser/context.ts +4 -4
  35. package/src/parser/inline/annotation.ts +1 -1
  36. package/src/parser/inline/autolink/url.test.ts +7 -7
  37. package/src/parser/inline/autolink/url.ts +1 -2
  38. package/src/parser/inline/autolink.ts +1 -1
  39. package/src/parser/inline/bracket.test.ts +2 -2
  40. package/src/parser/inline/extension/index.ts +5 -4
  41. package/src/parser/inline/extension/indexer.test.ts +0 -1
  42. package/src/parser/inline/extension/indexer.ts +1 -1
  43. package/src/parser/inline/html.ts +7 -6
  44. package/src/parser/inline/htmlentity.ts +3 -3
  45. package/src/parser/inline/italic.test.ts +11 -11
  46. package/src/parser/inline/link.ts +1 -6
  47. package/src/parser/inline/mark.test.ts +5 -5
  48. package/src/parser/inline/math.ts +3 -3
  49. package/src/parser/inline/media.ts +3 -8
  50. package/src/parser/inline/reference.ts +1 -1
  51. package/src/parser/inline/remark.test.ts +14 -18
  52. package/src/parser/inline/remark.ts +17 -19
  53. package/src/parser/inline/ruby.ts +3 -3
  54. package/src/parser/inline/shortmedia.ts +1 -1
  55. package/src/parser/inline.test.ts +25 -24
  56. package/src/parser/inline.ts +21 -9
  57. package/src/parser/segment.ts +23 -5
  58. package/src/parser/source/escapable.test.ts +1 -1
  59. package/src/parser/source/escapable.ts +4 -12
  60. package/src/parser/source/text.test.ts +40 -40
  61. package/src/parser/source/text.ts +77 -24
  62. package/src/parser/source/unescapable.test.ts +3 -3
  63. package/src/parser/source/unescapable.ts +4 -12
  64. package/src/parser/visibility.ts +32 -32
  65. package/src/combinator/control/manipulation/trim.test.ts +0 -23
  66. package/src/combinator/control/manipulation/trim.ts +0 -17
@@ -1,26 +1,24 @@
1
1
  import { RemarkParser } from '../inline';
2
2
  import { Recursion } from '../context';
3
- import { union, some, recursion, precedence, surround, close, match, lazy } from '../../combinator';
3
+ import { union, some, recursion, precedence, focus, surround, close, fallback, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { text, str } from '../source';
6
- import { memoize } from 'spica/memoize';
6
+ import { invalid } from '../util';
7
7
  import { unshift, push } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
- export const remark: RemarkParser = lazy(() => match(
11
- /\[(%+)\s/y,
12
- memoize(
13
- ([, fence]) =>
14
- surround(
15
- str(`[${fence}`),
16
- precedence(4, recursion(Recursion.inline,
17
- some(union([inline]), new RegExp(String.raw`\s+${fence}\]`, 'y'), [[new RegExp(String.raw`\s+${fence}\]`, 'y'), 4]]))),
18
- close(some(text, '%'), str(`${fence}]`)), true,
19
- ([as, bs = [], cs]) => [[
20
- html('span', { class: 'remark' }, [
21
- html('input', { type: 'checkbox' }),
22
- html('span', defrag(push(unshift(as, bs), cs))),
23
- ]),
24
- ]],
25
- ([as, bs]) => bs && [unshift(as, bs)]),
26
- ([, fence]) => fence.length, {})));
10
+ export const remark: RemarkParser = lazy(() => fallback(surround(
11
+ str(/\[%(?=\s)/y),
12
+ precedence(4, recursion(Recursion.inline,
13
+ some(union([inline]), /\s%\]/y, [[/\s%\]/y, 4]]))),
14
+ close(text, str(`%]`)), true,
15
+ ([as, bs = [], cs]) => [[
16
+ html('span', { class: 'remark' }, [
17
+ html('input', { type: 'checkbox' }),
18
+ html('span', defrag(push(unshift(as, bs), cs))),
19
+ ]),
20
+ ]],
21
+ ([as, bs]) => bs && [unshift(as, bs)]),
22
+ focus(/\[%+(?=\s)/y, ({ context: { source } }) => [[
23
+ html('span', { class: 'invalid', ...invalid('remark', 'syntax', 'Invalid start symbol') }, source)
24
+ ]])));
@@ -18,11 +18,11 @@ export const ruby: RubyParser = lazy(() => bind(
18
18
  return isTightNodeStart(ns) ? [ns] : undefined;
19
19
  },
20
20
  undefined,
21
- [3 | Backtrack.ruby, 1 | Backtrack.bracket])),
21
+ [1 | Backtrack.bracket, 3 | Backtrack.ruby])),
22
22
  dup(surround(
23
23
  '(', text, ')',
24
24
  false, undefined, undefined,
25
- [3 | Backtrack.ruby, 1 | Backtrack.bracket])),
25
+ [1 | Backtrack.bracket, 3 | Backtrack.ruby])),
26
26
  ]),
27
27
  ([texts, rubies], context) => {
28
28
  if (rubies === undefined) {
@@ -69,7 +69,7 @@ const text: RubyParser.TextParser = input => {
69
69
  let state = false;
70
70
  context.sequential = true;
71
71
  for (let { position } = context; position < source.length; position = context.position) {
72
- if (!/(?:\\[^\n]|[^\\[\](){}<>"`$#:^|\n])/y.test(source.slice(position, position + 2))) break;
72
+ if (/[$"`\[\](){}<>()[]{}]|\\?\n/yi.test(source.slice(position, position + 2))) break;
73
73
  assert(source[position] !== '\n');
74
74
  switch (source[position]) {
75
75
  case '&': {
@@ -12,7 +12,7 @@ export const shortmedia: ShortMediaParser = constraint(State.media, rewrite(
12
12
  false)));
13
13
 
14
14
  export const lineshortmedia: ShortMediaParser.LineShortMediaParser = focus(
15
- /(?<=^|[\r\n])!?https?:\/\/\S+(?=[^\S\n]*(?:$|\n))/y,
15
+ /(?<=^|[\r\n])!https?:\/\/\S+(?=[^\S\n]*(?:$|\n))/y,
16
16
  convert(
17
17
  source => `!{ ${source.slice(1)} }`,
18
18
  union([media]),
@@ -22,10 +22,10 @@ describe('Unit: parser/inline', () => {
22
22
  it('nest', () => {
23
23
  assert.deepStrictEqual(inspect(parser('あ(A)'), ctx), [['あ', '(', 'A', ')'], '']);
24
24
  assert.deepStrictEqual(inspect(parser('あ(い)'), ctx), [['あ', '<span class="paren">(い)</span>'], '']);
25
- assert.deepStrictEqual(inspect(parser('* a*'), ctx), [['*', ' ', 'a', '*'], '']);
26
- assert.deepStrictEqual(inspect(parser('** a**'), ctx), [['**', ' ', 'a', '**'], '']);
27
- assert.deepStrictEqual(inspect(parser('*** a***'), ctx), [['***', ' ', 'a', '***'], '']);
28
- assert.deepStrictEqual(inspect(parser('**** a****'), ctx), [['****', ' ', 'a', '****'], '']);
25
+ assert.deepStrictEqual(inspect(parser('* a*'), ctx), [['*', ' a', '*'], '']);
26
+ assert.deepStrictEqual(inspect(parser('** a**'), ctx), [['**', ' a', '**'], '']);
27
+ assert.deepStrictEqual(inspect(parser('*** a***'), ctx), [['***', ' a', '***'], '']);
28
+ assert.deepStrictEqual(inspect(parser('**** a****'), ctx), [['****', ' a', '****'], '']);
29
29
  assert.deepStrictEqual(inspect(parser('*a**'), ctx), [['<em>a</em>', '*'], '']);
30
30
  assert.deepStrictEqual(inspect(parser('*a**b'), ctx), [['*', 'a', '**', 'b'], '']);
31
31
  assert.deepStrictEqual(inspect(parser('*a**b*'), ctx), [['<em>a**b</em>'], '']);
@@ -98,11 +98,11 @@ describe('Unit: parser/inline', () => {
98
98
  assert.deepStrictEqual(inspect(parser('++\na\n++\n~~\nb\n~~\nc'), ctx), [['<ins><br>a</ins>', '<br>', '<del><br>b</del>', '<br>', 'c'], '']);
99
99
  assert.deepStrictEqual(inspect(parser('[@a]'), ctx), [['[', '<a class="account" href="/@a">@a</a>', ']'], '']);
100
100
  assert.deepStrictEqual(inspect(parser('[#1][#2]'), ctx), [['<a class="index" href="#index::1">1</a>', '<a class="index" href="#index::2">2</a>'], '']);
101
- assert.deepStrictEqual(inspect(parser('[$1]'), ctx), [['[', '$', '1', ']'], '']);
102
- assert.deepStrictEqual(inspect(parser('[$1-2]'), ctx), [['[', '$', '1-2', ']'], '']);
101
+ assert.deepStrictEqual(inspect(parser('[$1]'), ctx), [['[', '$1', ']'], '']);
102
+ assert.deepStrictEqual(inspect(parser('[$1-2]'), ctx), [['[', '$1-2', ']'], '']);
103
103
  assert.deepStrictEqual(inspect(parser('[$-1][$-2]'), ctx), [['<a class="label" data-label="$-1">$-1</a>', '<a class="label" data-label="$-2">$-2</a>'], '']);
104
104
  assert.deepStrictEqual(inspect(parser('$-1, $-2'), ctx), [['<a class="label" data-label="$-1">$-1</a>', ',', ' ', '<a class="label" data-label="$-2">$-2</a>'], '']);
105
- assert.deepStrictEqual(inspect(parser('$-1 and $-2'), ctx), [['<a class="label" data-label="$-1">$-1</a>', ' ', 'and', ' ', '<a class="label" data-label="$-2">$-2</a>'], '']);
105
+ assert.deepStrictEqual(inspect(parser('$-1 and $-2'), ctx), [['<a class="label" data-label="$-1">$-1</a>', ' and', ' ', '<a class="label" data-label="$-2">$-2</a>'], '']);
106
106
  assert.deepStrictEqual(inspect(parser('$$-1'), ctx), [['$', '<a class="label" data-label="$-1">$-1</a>'], '']);
107
107
  assert.deepStrictEqual(inspect(parser('[[#a]]'), ctx), [['<sup class="reference"><span><a class="hashtag" href="/hashtags/a">#a</a></span></sup>'], '']);
108
108
  assert.deepStrictEqual(inspect(parser('[[$-1]]'), ctx), [['<sup class="reference"><span><a class="label" data-label="$-1">$-1</a></span></sup>'], '']);
@@ -129,8 +129,8 @@ describe('Unit: parser/inline', () => {
129
129
  assert.deepStrictEqual(inspect(parser('${a}'), ctx), [['$', '<a class="url" href="a">a</a>'], '']);
130
130
  assert.deepStrictEqual(inspect(parser('${{a}}'), ctx), [['$', '<span class="template">{{a}}</span>'], '']);
131
131
  assert.deepStrictEqual(inspect(parser('${{{a}}}'), ctx), [['$', '<span class="template">{{{a}}}</span>'], '']);
132
- assert.deepStrictEqual(inspect(parser('Di$ney Micro$oft'), ctx), [['Di', '$', 'ney', ' ', 'Micro', '$', 'oft'], '']);
133
- assert.deepStrictEqual(inspect(parser('Di$ney, Micro$oft'), ctx), [['Di', '$', 'ney', ',', ' ', 'Micro', '$', 'oft'], '']);
132
+ assert.deepStrictEqual(inspect(parser('Di$ney Micro$oft'), ctx), [['Di', '$ney Micro', '$oft'], '']);
133
+ assert.deepStrictEqual(inspect(parser('Di$ney, Micro$oft'), ctx), [['Di', '$ney, Micro', '$oft'], '']);
134
134
  assert.deepStrictEqual(inspect(parser('(((a))'), ctx), [['(', '<sup class="annotation"><span>a</span></sup>'], '']);
135
135
  assert.deepStrictEqual(inspect(parser('((((a))'), ctx), [['(', '(', '<sup class="annotation"><span>a</span></sup>'], '']);
136
136
  assert.deepStrictEqual(inspect(parser('((((a))))'), ctx), [['<sup class="annotation"><span><span class="paren">((a))</span></span></sup>'], '']);
@@ -165,16 +165,16 @@ describe('Unit: parser/inline', () => {
165
165
  assert.deepStrictEqual(inspect(parser('"[% *"*"*'), ctx), [['"', '[%', ' ', '*', '"', '*', '"', '*'], '']);
166
166
  assert.deepStrictEqual(inspect(parser('"[% "*"* %]'), ctx), [['"', '<span class="remark"><input type="checkbox"><span>[% "*"* %]</span></span>'], '']);
167
167
  assert.deepStrictEqual(inspect(parser('"{{""}}'), ctx), [['"', '{', '{', '"', '"', '}', '}'], '']);
168
- assert.deepStrictEqual(inspect(parser('[#http://host/(<bdi>)]</bdi>'), ctx), [['<a class="index" href="#index::http://host/(&lt;bdi&gt;)">http://host/(&lt;bdi&gt;)</a>', '<', '/', 'bdi', '>'], '']);
169
- assert.deepStrictEqual(inspect(parser('[#@a/http://host/(<bdi>)]</bdi>'), ctx), [['<a class="index" href="#index::@a/http://host/(&lt;bdi&gt;)">@a/http://host/(&lt;bdi&gt;)</a>', '<', '/', 'bdi', '>'], '']);
168
+ assert.deepStrictEqual(inspect(parser('[#http://host/(<bdi>)]</bdi>'), ctx), [['<a class="index" href="#index::http://host/(&lt;bdi&gt;)">http://host/(&lt;bdi&gt;)</a>', '</bdi', '>'], '']);
169
+ assert.deepStrictEqual(inspect(parser('[#@a/http://host/(<bdi>)]</bdi>'), ctx), [['<a class="index" href="#index::@a/http://host/(&lt;bdi&gt;)">@a/http://host/(&lt;bdi&gt;)</a>', '</bdi', '>'], '']);
170
170
  assert.deepStrictEqual(inspect(parser('[#a|<bdi>]</bdi>'), ctx), [['[', '<a class="hashtag" href="/hashtags/a">#a</a>', '|', '<bdi>]</bdi>'], '']);
171
171
  assert.deepStrictEqual(inspect(parser('[[#a|<bdi>]</bdi>'), ctx), [['[', '[', '<a class="hashtag" href="/hashtags/a">#a</a>', '|', '<bdi>]</bdi>'], '']);
172
172
  });
173
173
 
174
174
  it('uri', () => {
175
175
  assert.deepStrictEqual(inspect(parser('\nhttp://host'), ctx), [['<br>', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
176
- assert.deepStrictEqual(inspect(parser('0http://host'), ctx), [['0http', ':', '/', '/', 'host'], '']);
177
- assert.deepStrictEqual(inspect(parser('0aAhttp://host'), ctx), [['0aAhttp', ':', '/', '/', 'host'], '']);
176
+ assert.deepStrictEqual(inspect(parser('0http://host'), ctx), [['0http:', '//host'], '']);
177
+ assert.deepStrictEqual(inspect(parser('0aAhttp://host'), ctx), [['0aAhttp:', '//host'], '']);
178
178
  assert.deepStrictEqual(inspect(parser('?http://host'), ctx), [['?', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
179
179
  assert.deepStrictEqual(inspect(parser('0!http://host'), ctx), [['0', '!', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
180
180
  assert.deepStrictEqual(inspect(parser('0?http://host'), ctx), [['0', '?', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
@@ -196,7 +196,8 @@ describe('Unit: parser/inline', () => {
196
196
  assert.deepStrictEqual(inspect(parser('*a@b*'), ctx), [['<em><a class="email" href="mailto:a@b">a@b</a></em>'], '']);
197
197
  assert.deepStrictEqual(inspect(parser('(a@b)'), ctx), [['<span class="paren">(<a class="email" href="mailto:a@b">a@b</a>)</span>'], '']);
198
198
  assert.deepStrictEqual(inspect(parser(' a@b'), ctx), [[' ', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
199
- assert.deepStrictEqual(inspect(parser('++a++b@c++'), ctx), [['<ins>a</ins>', '<a class="email" href="mailto:b@c">b@c</a>', '+', '+'], '']);
199
+ assert.deepStrictEqual(inspect(parser('++a@b++'), ctx), [['<ins><a class="email" href="mailto:a@b">a@b</a></ins>'], '']);
200
+ assert.deepStrictEqual(inspect(parser('++a++b@c++'), ctx), [['<ins>a</ins>', '<a class="email" href="mailto:b@c">b@c</a>', '++'], '']);
200
201
  });
201
202
 
202
203
  it('channel', () => {
@@ -209,7 +210,7 @@ describe('Unit: parser/inline', () => {
209
210
 
210
211
  it('account', () => {
211
212
  assert.deepStrictEqual(inspect(parser('@a'), ctx), [['<a class="account" href="/@a">@a</a>'], '']);
212
- assert.deepStrictEqual(inspect(parser('@http://host'), ctx), [['@http', ':', '/', '/', 'host'], '']);
213
+ assert.deepStrictEqual(inspect(parser('@http://host'), ctx), [['@http', '://host'], '']);
213
214
  assert.deepStrictEqual(inspect(parser('_@a'), ctx), [['_', '<a class="account" href="/@a">@a</a>'], '']);
214
215
  assert.deepStrictEqual(inspect(parser('_@a_'), ctx), [['_', '<a class="account" href="/@a">@a</a>', '_'], '']);
215
216
  assert.deepStrictEqual(inspect(parser('*@a*'), ctx), [['<em><a class="account" href="/@a">@a</a></em>'], '']);
@@ -219,21 +220,21 @@ describe('Unit: parser/inline', () => {
219
220
 
220
221
  it('hashtag', () => {
221
222
  assert.deepStrictEqual(inspect(parser('#a#'), ctx), [['#a', '#'], '']);
222
- assert.deepStrictEqual(inspect(parser('#a#b'), ctx), [['#a', '#', 'b'], '']);
223
+ assert.deepStrictEqual(inspect(parser('#a#b'), ctx), [['#a', '#b'], '']);
223
224
  assert.deepStrictEqual(inspect(parser('#a'), ctx), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
224
- assert.deepStrictEqual(inspect(parser('#http://host'), ctx), [['#http', ':', '/', '/', 'host'], '']);
225
+ assert.deepStrictEqual(inspect(parser('#http://host'), ctx), [['#http', '://host'], '']);
225
226
  assert.deepStrictEqual(inspect(parser('#a\nb\n#c\n[#d]'), ctx), [['<a class="hashtag" href="/hashtags/a">#a</a>', '<br>', 'b', '<br>', '<a class="hashtag" href="/hashtags/c">#c</a>', '<br>', '<a class="index" href="#index::d">d</a>'], '']);
226
227
  assert.deepStrictEqual(inspect(parser('##a'), ctx), [['##a'], '']);
227
228
  assert.deepStrictEqual(inspect(parser('_#a'), ctx), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
228
229
  assert.deepStrictEqual(inspect(parser('_#a_'), ctx), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>', '_'], '']);
229
230
  assert.deepStrictEqual(inspect(parser('_#a_b'), ctx), [['_', '<a class="hashtag" href="/hashtags/a_b">#a_b</a>'], '']);
230
231
  assert.deepStrictEqual(inspect(parser('_#a_b_'), ctx), [['_', '<a class="hashtag" href="/hashtags/a_b">#a_b</a>', '_'], '']);
231
- assert.deepStrictEqual(inspect(parser('a#b'), ctx), [['a', '#', 'b'], '']);
232
- assert.deepStrictEqual(inspect(parser('0a#b'), ctx), [['0a', '#', 'b'], '']);
233
- assert.deepStrictEqual(inspect(parser('あ#b'), ctx), [['あ', '#', 'b'], '']);
234
- assert.deepStrictEqual(inspect(parser('あい#b'), ctx), [['あい', '#', 'b'], '']);
235
- assert.deepStrictEqual(inspect(parser('0aあ#b'), ctx), [['0a', 'あ', '#', 'b'], '']);
236
- assert.deepStrictEqual(inspect(parser('0aあい#b'), ctx), [['0a', 'あい', '#', 'b'], '']);
232
+ assert.deepStrictEqual(inspect(parser('a#b'), ctx), [['a', '#b'], '']);
233
+ assert.deepStrictEqual(inspect(parser('0a#b'), ctx), [['0a', '#b'], '']);
234
+ assert.deepStrictEqual(inspect(parser('あ#b'), ctx), [['あ', '#b'], '']);
235
+ assert.deepStrictEqual(inspect(parser('あい#b'), ctx), [['あい', '#b'], '']);
236
+ assert.deepStrictEqual(inspect(parser('0aあ#b'), ctx), [['0a', 'あ', '#b'], '']);
237
+ assert.deepStrictEqual(inspect(parser('0aあい#b'), ctx), [['0a', 'あい', '#b'], '']);
237
238
  assert.deepStrictEqual(inspect(parser('「#あ」'), ctx), [['「', '<a class="hashtag" href="/hashtags/あ">#あ</a>', '」'], '']);
238
239
  assert.deepStrictEqual(inspect(parser('a\n#b'), ctx), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
239
240
  assert.deepStrictEqual(inspect(parser('a\\\n#b'), ctx), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
@@ -247,7 +248,7 @@ describe('Unit: parser/inline', () => {
247
248
  it('hashnum', () => {
248
249
  assert.deepStrictEqual(inspect(parser('#1'), ctx), [['<a class="hashnum">#1</a>'], '']);
249
250
  assert.deepStrictEqual(inspect(parser(`#1'`), ctx), [[`<a class="hashnum">#1</a>`, `'`], '']);
250
- assert.deepStrictEqual(inspect(parser('#1234567890@a'), ctx), [['#1234567890', '@', 'a'], '']);
251
+ assert.deepStrictEqual(inspect(parser('#1234567890@a'), ctx), [['#1234567890', '@a'], '']);
251
252
  assert.deepStrictEqual(inspect(parser('_#1'), ctx), [['_', '<a class="hashnum">#1</a>'], '']);
252
253
  assert.deepStrictEqual(inspect(parser('_#1_'), ctx), [['_', '<a class="hashnum">#1</a>', '_'], '']);
253
254
  assert.deepStrictEqual(inspect(parser('_#1_0'), ctx), [['_', '<a class="hashtag" href="/hashtags/1_0">#1_0</a>'], '']);
@@ -55,22 +55,27 @@ export const inline: InlineParser = lazy(() => union([
55
55
  if (position === source.length) return;
56
56
  switch (source.slice(position, position + 2)) {
57
57
  case '((':
58
- return annotation(input);
58
+ return annotation(input)
59
+ || bracket(input);
59
60
  case '[[':
60
61
  return reference(input)
61
- || textlink(input);
62
+ || textlink(input)
63
+ || bracket(input);
62
64
  case '{{':
63
- return template(input);
65
+ return template(input)
66
+ || bracket(input);
64
67
  case '[%':
65
68
  return remark(input)
66
- || textlink(input);
69
+ || textlink(input)
70
+ || bracket(input);
67
71
  case '[#':
68
72
  case '[$':
69
73
  case '[:':
70
74
  case '[^':
71
75
  case '[|':
72
76
  return extension(input)
73
- || textlink(input);
77
+ || textlink(input)
78
+ || bracket(input);
74
79
  case '${':
75
80
  return math(input);
76
81
  case '++':
@@ -89,9 +94,11 @@ export const inline: InlineParser = lazy(() => union([
89
94
  switch (source[position]) {
90
95
  case '[':
91
96
  return textlink(input)
92
- || ruby(input);
97
+ || ruby(input)
98
+ || bracket(input);
93
99
  case '{':
94
- return textlink(input);
100
+ return textlink(input)
101
+ || bracket(input);
95
102
  case '<':
96
103
  return html(input);
97
104
  case '$':
@@ -104,9 +111,14 @@ export const inline: InlineParser = lazy(() => union([
104
111
  || stars(input);
105
112
  case '&':
106
113
  return htmlentity(input);
114
+ case '(':
115
+ case '(':
116
+ case '[':
117
+ case '{':
118
+ case '"':
119
+ return bracket(input);
107
120
  }
108
121
  },
109
- bracket,
110
122
  autolink,
111
123
  text
112
124
  ])) as any;
@@ -116,4 +128,4 @@ export { indexer } from './inline/extension/indexer';
116
128
  export { dataindex } from './inline/extension/index';
117
129
  export { medialink } from './inline/link';
118
130
  export { media } from './inline/media';
119
- export { shortmedia } from './inline/shortmedia';
131
+ export { shortmedia, lineshortmedia } from './inline/shortmedia';
@@ -14,13 +14,31 @@ export const MAX_SEGMENT_SIZE = 100_000; // 100,000 bytes (Max value size of FDB
14
14
  export const MAX_INPUT_SIZE = MAX_SEGMENT_SIZE * 10;
15
15
 
16
16
  const parser: SegmentParser = union([
17
- heading,
18
- codeblock,
19
- mathblock,
20
- extension,
17
+ input => {
18
+ const { context: { source, position } } = input;
19
+ if (position === source.length) return;
20
+ switch (source.slice(position, position + 3)) {
21
+ case '~~~':
22
+ return extension(input);
23
+ case '```':
24
+ return codeblock(input);
25
+ }
26
+ switch (source.slice(position, position + 2)) {
27
+ case '$$':
28
+ return mathblock(input);
29
+ case '[$':
30
+ return extension(input);
31
+ }
32
+ switch (source[position]) {
33
+ case '#':
34
+ return heading(input);
35
+ case '$':
36
+ return extension(input);
37
+ }
38
+ },
21
39
  some(contentline, MAX_SEGMENT_SIZE + 1),
22
40
  some(emptyline, MAX_SEGMENT_SIZE + 1),
23
- ]);
41
+ ]) as any;
24
42
 
25
43
  export function* segment(source: string): Generator<string, undefined, undefined> {
26
44
  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)}`;
@@ -15,7 +15,7 @@ describe('Unit: parser/source/escsource', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
- assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09', 'あい', 'AZaz'], '']);
18
+ assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
19
19
  });
20
20
 
21
21
  it('space', () => {
@@ -1,9 +1,11 @@
1
1
  import { EscapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
- import { nonWhitespace, nonAlphanumeric, ASCII, isBlank, isAlphanumeric, isASCII } from './text';
4
+ import { nonWhitespace, isBlank, next } from './text';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
+ const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s(?:\$)|:\/\/)/g;
8
+
7
9
  export const escsource: EscapableSourceParser = ({ context }) => {
8
10
  const { source, position } = context;
9
11
  if (position === source.length) return;
@@ -36,23 +38,13 @@ export const escsource: EscapableSourceParser = ({ context }) => {
36
38
  default:
37
39
  assert(char !== '\n');
38
40
  if (context.sequential) return [[char]];
39
- nonAlphanumeric.lastIndex = position + 1;
40
41
  nonWhitespace.lastIndex = position + 1;
41
- ASCII.lastIndex = position + 1;
42
42
  const b = isBlank(source, position);
43
43
  let i = b
44
44
  ? nonWhitespace.test(source)
45
45
  ? nonWhitespace.lastIndex - 1
46
46
  : source.length
47
- : isAlphanumeric(char)
48
- ? nonAlphanumeric.test(source)
49
- ? nonAlphanumeric.lastIndex - 1
50
- : source.length
51
- : !isASCII(char)
52
- ? ASCII.test(source)
53
- ? ASCII.lastIndex - 1
54
- : source.length
55
- : position + 1;
47
+ : next(source, position, delimiter);
56
48
  assert(i > position);
57
49
  i -= position;
58
50
  consume(i - 1, context);
@@ -15,7 +15,7 @@ describe('Unit: parser/source/text', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
- assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09', 'あい', 'AZaz'], '']);
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
 
@@ -41,18 +41,18 @@ describe('Unit: parser/source/text', () => {
41
41
  assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
42
42
  assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
43
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'], '']);
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
47
  assert.deepStrictEqual(inspect(parser('a '), ctx), [['a'], '']);
48
48
  assert.deepStrictEqual(inspect(parser('a '), ctx), [['a'], '']);
49
49
  assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
50
50
  assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
51
51
  assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
52
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'], '']);
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', () => {
@@ -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), [['aA', '@', '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), [['aA', '#', '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), [['aA', '>', '>', '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,10 +3,8 @@ 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 category = /(\s)|(\p{ASCII})|(.)/yu;
6
+ export const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
7
7
  export const nonWhitespace = /[\S\r\n]/g;
8
- export const nonAlphanumeric = /[^0-9A-Za-z]/g;
9
- export const ASCII = /[\x00-\x7F()[]{}]/g;
10
8
 
11
9
  export const text: TextParser = input => {
12
10
  const { context } = input;
@@ -39,23 +37,13 @@ export const text: TextParser = input => {
39
37
  default:
40
38
  assert(char !== '\n');
41
39
  if (context.sequential) return [[char]];
42
- nonAlphanumeric.lastIndex = position + 1;
43
40
  nonWhitespace.lastIndex = position + 1;
44
- ASCII.lastIndex = position + 1;
45
41
  const b = isBlank(source, position);
46
42
  let i = b
47
43
  ? nonWhitespace.test(source)
48
44
  ? nonWhitespace.lastIndex - 1
49
45
  : source.length
50
- : isAlphanumeric(char)
51
- ? nonAlphanumeric.test(source)
52
- ? nonAlphanumeric.lastIndex - 1
53
- : source.length
54
- : !isASCII(char)
55
- ? ASCII.test(source)
56
- ? ASCII.lastIndex - 1
57
- : source.length
58
- : position + 1;
46
+ : next(source, position, delimiter);
59
47
  assert(i > position);
60
48
  const lineend = 0
61
49
  || b && i === source.length
@@ -82,24 +70,89 @@ export const linebreak: LinebreakParser = focus(/[\r\n]/y, union([
82
70
  text,
83
71
  ])) as LinebreakParser;
84
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
+ assert(index > position);
89
+ return index;
90
+ }
91
+ export function backToUrlHead(source: string, position: number, index: number): number {
92
+ const delim = index;
93
+ let state = false;
94
+ let offset = 0;
95
+ for (let i = index; --i > position;) {
96
+ index = i;
97
+ const char = source[i];
98
+ if (state) switch (char) {
99
+ case '.':
100
+ case '+':
101
+ case '-':
102
+ state = false;
103
+ offset = 1;
104
+ continue;
105
+ }
106
+ if (isAlphanumeric(char)) {
107
+ state = true;
108
+ offset = 0;
109
+ continue;
110
+ }
111
+ break;
112
+ }
113
+ if (index === position + 1 && offset === 0 && isAlphanumeric(source[index - 1])) {
114
+ return delim;
115
+ }
116
+ return index + offset;
117
+ }
118
+ export function backToEmailHead(source: string, position: number, index: number): number {
119
+ const delim = index;
120
+ let state = false;
121
+ let offset = 0;
122
+ for (let i = index; --i > position;) {
123
+ index = i;
124
+ const char = source[i];
125
+ if (state) switch (char) {
126
+ case '_':
127
+ case '.':
128
+ case '+':
129
+ case '-':
130
+ state = false;
131
+ offset = 1;
132
+ continue;
133
+ }
134
+ if (isAlphanumeric(char)) {
135
+ state = true;
136
+ offset = 0;
137
+ continue;
138
+ }
139
+ break;
140
+ }
141
+ if (index === position + 1 && offset === 0 && isAlphanumeric(source[index - 1])) {
142
+ return delim;
143
+ }
144
+ return index + offset;
145
+ }
146
+
85
147
  const blank = /\s(?:$|\s|\\\n)/y;
86
148
  export function isBlank(source: string, position: number): boolean {
87
149
  blank.lastIndex = position;
88
150
  return blank.test(source);
89
151
  }
90
-
91
- export function isAlphanumeric(char: string): boolean {
152
+ function isAlphanumeric(char: string): boolean {
92
153
  assert(char.length === 1);
93
154
  if (char < '0' || '\x7F' < char) return false;
94
155
  return '0' <= char && char <= '9'
95
156
  || 'a' <= char && char <= 'z'
96
157
  || 'A' <= char && char <= 'Z';
97
158
  }
98
-
99
- export function isASCII(char: string): boolean {
100
- assert(char.length === 1);
101
- return char <= '\x7F'
102
- || char === '(' || char === ')'
103
- || char === '[' || char === ']'
104
- || char === '{' || char === '}';
105
- }