securemark 0.294.3 → 0.294.5

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 (63) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +164 -144
  3. package/markdown.d.ts +3 -12
  4. package/package.json +3 -3
  5. package/src/combinator/control/manipulation/fence.ts +2 -0
  6. package/src/combinator/control/manipulation/indent.ts +1 -1
  7. package/src/combinator/control/manipulation/match.ts +11 -8
  8. package/src/parser/api/parse.test.ts +3 -3
  9. package/src/parser/block/blockquote.test.ts +3 -9
  10. package/src/parser/block/blockquote.ts +4 -4
  11. package/src/parser/block/dlist.ts +4 -4
  12. package/src/parser/block/extension/example.ts +1 -3
  13. package/src/parser/block/extension/fig.test.ts +0 -1
  14. package/src/parser/block/extension/fig.ts +6 -6
  15. package/src/parser/block/extension/figbase.ts +1 -1
  16. package/src/parser/block/extension/figure.test.ts +1 -1
  17. package/src/parser/block/extension/figure.ts +6 -6
  18. package/src/parser/block/extension/message.ts +1 -1
  19. package/src/parser/block/extension/table.ts +4 -4
  20. package/src/parser/block/heading.ts +4 -4
  21. package/src/parser/block/reply/cite.ts +2 -2
  22. package/src/parser/block/reply/quote.ts +2 -2
  23. package/src/parser/block/sidefence.test.ts +1 -3
  24. package/src/parser/block/sidefence.ts +4 -4
  25. package/src/parser/block/table.ts +2 -2
  26. package/src/parser/block.ts +1 -1
  27. package/src/parser/header.ts +3 -3
  28. package/src/parser/inline/autolink/account.test.ts +18 -17
  29. package/src/parser/inline/autolink/account.ts +14 -20
  30. package/src/parser/inline/autolink/anchor.test.ts +2 -1
  31. package/src/parser/inline/autolink/anchor.ts +10 -13
  32. package/src/parser/inline/autolink/channel.test.ts +6 -6
  33. package/src/parser/inline/autolink/channel.ts +28 -32
  34. package/src/parser/inline/autolink/email.test.ts +19 -19
  35. package/src/parser/inline/autolink/email.ts +7 -7
  36. package/src/parser/inline/autolink/hashnum.test.ts +20 -20
  37. package/src/parser/inline/autolink/hashnum.ts +6 -8
  38. package/src/parser/inline/autolink/hashtag.test.ts +27 -27
  39. package/src/parser/inline/autolink/hashtag.ts +15 -16
  40. package/src/parser/inline/autolink/url.test.ts +6 -6
  41. package/src/parser/inline/autolink/url.ts +5 -4
  42. package/src/parser/inline/autolink.ts +4 -5
  43. package/src/parser/inline/code.ts +12 -18
  44. package/src/parser/inline/deletion.test.ts +1 -1
  45. package/src/parser/inline/deletion.ts +3 -3
  46. package/src/parser/inline/emstrong.ts +3 -3
  47. package/src/parser/inline/extension/indexer.ts +1 -1
  48. package/src/parser/inline/html.test.ts +1 -0
  49. package/src/parser/inline/html.ts +1 -1
  50. package/src/parser/inline/insertion.test.ts +1 -1
  51. package/src/parser/inline/insertion.ts +3 -3
  52. package/src/parser/inline/italic.test.ts +2 -2
  53. package/src/parser/inline/italic.ts +3 -3
  54. package/src/parser/inline/link.test.ts +0 -1
  55. package/src/parser/inline/link.ts +3 -3
  56. package/src/parser/inline/mark.test.ts +1 -1
  57. package/src/parser/inline/mark.ts +3 -3
  58. package/src/parser/inline/remark.ts +3 -3
  59. package/src/parser/inline.test.ts +11 -11
  60. package/src/parser/inline.ts +2 -0
  61. package/src/parser/source/text.ts +8 -4
  62. package/src/parser/util.ts +1 -1
  63. package/src/parser/visibility.ts +4 -6
@@ -1,26 +1,20 @@
1
1
  import { CodeParser } from '../inline';
2
2
  import { List, Data } from '../../combinator/data/parser';
3
- import { open, match } from '../../combinator';
4
- import { Backtrack } from '../context';
3
+ import { match } from '../../combinator';
5
4
  import { invalid } from '../util';
6
5
  import { html } from 'typed-dom/dom';
7
6
 
8
- export const code: CodeParser = open(
9
- /(?=`)/y,
10
- match(
11
- /(`+)(?!`)([^\n]*?)(?:((?<!`)\1(?!`))|(?=$|\n))/y,
12
- ([whole, opener, body, closer]) => () =>
13
- closer
14
- ? new List([new Data(html('code', { 'data-src': whole }, format(body)))])
15
- : body
16
- ? new List([new Data(html('code', {
17
- class: 'invalid',
18
- ...invalid('code', 'syntax', `Missing the closing symbol "${opener}"`)
19
- }, whole))])
20
- : new List([new Data(opener)]),
21
- true),
22
- false,
23
- [3 | Backtrack.bracket]);
7
+ export const code: CodeParser = match(
8
+ /(`+)(?!`)([^\n]*?)(?:((?<!`)\1(?!`))|(?=$|\n))/y,
9
+ ([whole, opener, body, closer]) => () =>
10
+ closer
11
+ ? new List([new Data(html('code', { 'data-src': whole }, format(body)))])
12
+ : body
13
+ ? new List([new Data(html('code', {
14
+ class: 'invalid',
15
+ ...invalid('code', 'syntax', `Missing the closing symbol "${opener}"`)
16
+ }, whole))])
17
+ : new List([new Data(opener)]));
24
18
 
25
19
  function format(text: string): string {
26
20
  return text.length > 2
@@ -13,7 +13,7 @@ describe('Unit: parser/inline/deletion', () => {
13
13
  assert.deepStrictEqual(inspect(parser('~'), ctx), undefined);
14
14
  assert.deepStrictEqual(inspect(parser('~~'), ctx), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('~~a'), ctx), [['~~', 'a'], '']);
16
- assert.deepStrictEqual(inspect(parser('~~a~'), ctx), [['~~', 'a', '~'], '']);
16
+ assert.deepStrictEqual(inspect(parser('~~a~'), ctx), [['~~', 'a~'], '']);
17
17
  assert.deepStrictEqual(inspect(parser(' ~~a~~'), ctx), undefined);
18
18
  });
19
19
 
@@ -1,13 +1,13 @@
1
1
  import { DeletionParser } from '../inline';
2
2
  import { Recursion, Command } from '../context';
3
3
  import { List, Data } from '../../combinator/data/parser';
4
- import { union, some, recursion, precedence, validate, surround, open, lazy } from '../../combinator';
4
+ import { union, some, recursion, precedence, surround, open, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { blankWith } from '../visibility';
7
7
  import { unwrap, repeat } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
- export const deletion: DeletionParser = lazy(() => validate('~~',
10
+ export const deletion: DeletionParser = lazy(() =>
11
11
  precedence(0, repeat('~~', surround(
12
12
  '',
13
13
  recursion(Recursion.inline,
@@ -18,4 +18,4 @@ export const deletion: DeletionParser = lazy(() => validate('~~',
18
18
  '~~', false,
19
19
  ([, bs], { buffer }) => buffer!.import(bs),
20
20
  ([, bs], { buffer }) => bs && buffer!.import(bs).push(new Data(Command.Cancel)) && buffer!),
21
- nodes => new List([new Data(html('del', defrag(unwrap(nodes))))])))));
21
+ nodes => new List([new Data(html('del', defrag(unwrap(nodes))))]))));
@@ -1,7 +1,7 @@
1
1
  import { EmStrongParser, EmphasisParser, StrongParser } from '../inline';
2
2
  import { Recursion, Command } from '../context';
3
3
  import { Result, List, Data, Node, Context, IntermediateParser } from '../../combinator/data/parser';
4
- import { union, some, recursion, precedence, validate, surround, open, lazy, bind } from '../../combinator';
4
+ import { union, some, recursion, precedence, surround, open, lazy, bind } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { strong } from './strong';
7
7
  import { emphasis } from './emphasis';
@@ -24,7 +24,7 @@ const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
24
24
  // 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず
25
25
  // 可能な限り早く閉じるよう解析しなければならない。
26
26
  // このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
27
- export const emstrong: EmStrongParser = lazy(() => validate('***',
27
+ export const emstrong: EmStrongParser = lazy(() =>
28
28
  precedence(0, repeat('***', surround(
29
29
  '',
30
30
  recursion(Recursion.inline,
@@ -142,7 +142,7 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
142
142
  nodes = prepend('*'.repeat(prefix - postfix), nodes);
143
143
  }
144
144
  return nodes;
145
- }))));
145
+ })));
146
146
 
147
147
  function prepend<N>(prefix: string, nodes: List<Data<N>>): List<Data<N>> {
148
148
  if (typeof nodes.head?.value === 'string') {
@@ -11,7 +11,7 @@ import { html } from 'typed-dom/dom';
11
11
  // 継続的編集において最も簡便となる。
12
12
 
13
13
  export const indexer: ExtensionParser.IndexerParser = surround(
14
- /\s\[(?=\|\S)/y,
14
+ / \[(?=\|\S)/y,
15
15
  union([
16
16
  signature,
17
17
  focus(/\|(?=\])/y, () => new List([new Data(html('span', { class: 'indexer', 'data-index': '' }))])),
@@ -65,6 +65,7 @@ describe('Unit: parser/inline/html', () => {
65
65
  it('basic', () => {
66
66
  assert.deepStrictEqual(inspect(parser('<wbr>'), ctx), [['<wbr>'], '']);
67
67
  assert.deepStrictEqual(inspect(parser('<wbr >'), ctx), [['<wbr>'], '']);
68
+ assert.deepStrictEqual(inspect(parser('<wbr >'), ctx), [['<span class="invalid">&lt;wbr </span>'], ' >']);
68
69
  assert.deepStrictEqual(inspect(parser('<wbr>a'), ctx), [['<wbr>'], 'a']);
69
70
  assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<bdi>a</bdi>'], '']);
70
71
  assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<span class="invalid">&lt;bdi &gt;a&lt;/bdi&gt;</span>'], '']);
@@ -32,7 +32,7 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
32
32
  ([as, bs = new List()], context) =>
33
33
  new List([new Data(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs))], new List(), new List(), context))])),
34
34
  match(
35
- new RegExp(String.raw`<(${TAGS.join('|')})(?=[^\S\n]|>)`, 'y'),
35
+ new RegExp(String.raw`<(${TAGS.join('|')})(?=[ >])`, 'y'),
36
36
  memoize(
37
37
  ([, tag]) =>
38
38
  surround<HTMLParser.TagParser, string>(
@@ -13,7 +13,7 @@ describe('Unit: parser/inline/insertion', () => {
13
13
  assert.deepStrictEqual(inspect(parser('+'), ctx), undefined);
14
14
  assert.deepStrictEqual(inspect(parser('++'), ctx), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('++a'), ctx), [['++', 'a'], '']);
16
- assert.deepStrictEqual(inspect(parser('++a+'), ctx), [['++', 'a', '+'], '']);
16
+ assert.deepStrictEqual(inspect(parser('++a+'), ctx), [['++', 'a+'], '']);
17
17
  assert.deepStrictEqual(inspect(parser(' ++a++'), ctx), undefined);
18
18
  });
19
19
 
@@ -1,13 +1,13 @@
1
1
  import { InsertionParser } from '../inline';
2
2
  import { Recursion, Command } from '../context';
3
3
  import { List, Data } from '../../combinator/data/parser';
4
- import { union, some, recursion, precedence, validate, surround, open, lazy } from '../../combinator';
4
+ import { union, some, recursion, precedence, surround, open, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { blankWith } from '../visibility';
7
7
  import { unwrap, repeat } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
- export const insertion: InsertionParser = lazy(() => validate('++',
10
+ export const insertion: InsertionParser = lazy(() =>
11
11
  precedence(0, repeat('++', surround(
12
12
  '',
13
13
  recursion(Recursion.inline,
@@ -18,4 +18,4 @@ export const insertion: InsertionParser = lazy(() => validate('++',
18
18
  '++', false,
19
19
  ([, bs], { buffer }) => buffer!.import(bs),
20
20
  ([, bs], { buffer }) => bs && buffer!.import(bs).push(new Data(Command.Cancel)) && buffer!),
21
- nodes => new List([new Data(html('ins', defrag(unwrap(nodes))))])))));
21
+ nodes => new List([new Data(html('ins', defrag(unwrap(nodes))))]))));
@@ -16,8 +16,8 @@ describe('Unit: parser/inline/italic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('///a\n///'), ctx), [['///', 'a', '<br>', '///'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('///a\\ ///'), ctx), [['///', 'a', ' ', '///'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('///a\\\n///'), ctx), [['///', 'a', '<br>', '///'], '']);
19
- assert.deepStrictEqual(inspect(parser('///a/b'), ctx), [['///', 'a', '/b'], '']);
20
- assert.deepStrictEqual(inspect(parser('///a//b'), ctx), [['///', 'a', '//b'], '']);
19
+ assert.deepStrictEqual(inspect(parser('///a/b'), ctx), [['///', 'a/b'], '']);
20
+ assert.deepStrictEqual(inspect(parser('///a//b'), ctx), [['///', 'a//b'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('///a*b///'), ctx), [['///', 'a', '*', 'b', '///'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('/// ///'), ctx), undefined);
23
23
  assert.deepStrictEqual(inspect(parser('/// a///'), ctx), undefined);
@@ -1,7 +1,7 @@
1
1
  import { ItalicParser } from '../inline';
2
2
  import { Recursion, Command } from '../context';
3
3
  import { List, Data } from '../../combinator/data/parser';
4
- import { union, some, recursion, precedence, validate, surround, open, lazy } from '../../combinator';
4
+ import { union, some, recursion, precedence, surround, open, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { tightStart, blankWith } from '../visibility';
7
7
  import { unwrap, repeat } from '../util';
@@ -10,7 +10,7 @@ import { html, defrag } from 'typed-dom/dom';
10
10
  // 可読性のため実際にはオブリーク体を指定する。
11
11
  // 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
12
12
  // ある程度の長さのある文に使うのが望ましい。
13
- export const italic: ItalicParser = lazy(() => validate('///',
13
+ export const italic: ItalicParser = lazy(() =>
14
14
  precedence(0, repeat('///', surround(
15
15
  '',
16
16
  recursion(Recursion.inline,
@@ -21,4 +21,4 @@ export const italic: ItalicParser = lazy(() => validate('///',
21
21
  '///', false,
22
22
  ([, bs], { buffer }) => buffer!.import(bs),
23
23
  ([, bs], { buffer }) => bs && buffer!.import(bs).push(new Data(Command.Cancel)) && buffer!),
24
- nodes => new List([new Data(html('i', defrag(unwrap(nodes))))])))));
24
+ nodes => new List([new Data(html('i', defrag(unwrap(nodes))))]))));
@@ -180,7 +180,6 @@ describe('Unit: parser/inline/link', () => {
180
180
  assert.deepStrictEqual(inspect(parser('[[![]{a}]{b}]{b}'), ctx), [['<a class="link" href="b">[![]{a}]{b}</a>'], '']);
181
181
  assert.deepStrictEqual(inspect(parser('[((a))]{b}'), ctx), [['<a class="link" href="b"><span class="paren">((a))</span></a>'], '']);
182
182
  assert.deepStrictEqual(inspect(parser('[[[a]]]{b}'), ctx), [['<a class="link" href="b">[[a]]</a>'], '']);
183
- assert.deepStrictEqual(inspect(parser('[!http://host]{/}'), ctx), [['<a class="link" href="/" target="_blank"><img class="media" data-src="http://host" alt="http://host"></a>'], '']);
184
183
  assert.deepStrictEqual(inspect(parser('[#a]{b}'), ctx), [['<a class="link" href="b">#a</a>'], '']);
185
184
  assert.deepStrictEqual(inspect(parser('[@a]{b}'), ctx), [['<a class="link" href="b">@a</a>'], '']);
186
185
  assert.deepStrictEqual(inspect(parser('[@a@b]{c}'), ctx), [['<a class="link" href="c">@a@b</a>'], '']);
@@ -2,7 +2,7 @@ import { MarkdownParser } from '../../../markdown';
2
2
  import { LinkParser } from '../inline';
3
3
  import { State, Backtrack, Command } from '../context';
4
4
  import { List, Data } from '../../combinator/data/parser';
5
- import { union, inits, tails, sequence, subsequence, some, creation, precedence, state, constraint, validate, surround, open, setBacktrack, dup, reverse, lazy, fmap, bind } from '../../combinator';
5
+ import { union, inits, tails, sequence, subsequence, some, creation, precedence, state, constraint, surround, open, setBacktrack, dup, reverse, lazy, fmap, bind } from '../../combinator';
6
6
  import { inline, media, shortmedia } from '../inline';
7
7
  import { attributes } from './html';
8
8
  import { unescsource, str } from '../source';
@@ -75,7 +75,7 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
75
75
  return new List([new Data(parse(content, params as List<Data<string>>, context))]);
76
76
  }))))));
77
77
 
78
- export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State.link | State.media, validate(/[[{]/y, creation(10,
78
+ export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State.link | State.media, creation(10,
79
79
  state(State.linkers,
80
80
  bind(sequence([
81
81
  dup(surround(
@@ -85,7 +85,7 @@ export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State
85
85
  dup(surround(/{(?![{}])/y, inits([uri, some(option)]), / ?}/y)),
86
86
  ]),
87
87
  ([{ value: content }, { value: params }], context) =>
88
- new List([new Data(parse(content, params as List<Data<string>>, context))])))))));
88
+ new List([new Data(parse(content, params as List<Data<string>>, context))]))))));
89
89
 
90
90
  export const unsafelink: LinkParser.UnsafeLinkParser = lazy(() =>
91
91
  creation(10,
@@ -13,7 +13,7 @@ describe('Unit: parser/inline/mark', () => {
13
13
  assert.deepStrictEqual(inspect(parser('='), ctx), undefined);
14
14
  assert.deepStrictEqual(inspect(parser('=='), ctx), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('==a'), ctx), [['==', 'a'], '']);
16
- assert.deepStrictEqual(inspect(parser('==a='), ctx), [['==', 'a', '='], '']);
16
+ assert.deepStrictEqual(inspect(parser('==a='), ctx), [['==', 'a='], '']);
17
17
  assert.deepStrictEqual(inspect(parser('==a =='), ctx), [['==', 'a', ' ', '=='], '']);
18
18
  assert.deepStrictEqual(inspect(parser('==a =='), ctx), [['==', 'a', ' ', '=='], '']);
19
19
  assert.deepStrictEqual(inspect(parser('==a\n=='), ctx), [['==', 'a', '<br>', '=='], '']);
@@ -1,14 +1,14 @@
1
1
  import { MarkParser } from '../inline';
2
2
  import { State, Recursion, Command } from '../context';
3
3
  import { List, Data } from '../../combinator/data/parser';
4
- import { union, some, recursion, precedence, state, constraint, validate, surround, open, lazy } from '../../combinator';
4
+ import { union, some, recursion, precedence, state, constraint, surround, open, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { identity, signature } from './extension/indexee';
7
7
  import { tightStart, blankWith } from '../visibility';
8
8
  import { unwrap, repeat } from '../util';
9
9
  import { html, define, defrag } from 'typed-dom/dom';
10
10
 
11
- export const mark: MarkParser = lazy(() => constraint(State.linkers & ~State.mark, validate('==',
11
+ export const mark: MarkParser = lazy(() => constraint(State.linkers & ~State.mark,
12
12
  precedence(0, state(State.mark, repeat('==', surround(
13
13
  '',
14
14
  recursion(Recursion.inline,
@@ -25,4 +25,4 @@ export const mark: MarkParser = lazy(() => constraint(State.linkers & ~State.mar
25
25
  return el.id
26
26
  ? new List([new Data(el), new Data(html('a', { href: `#${el.id}` }))])
27
27
  : new List([new Data(el)]);
28
- }))))));
28
+ })))));
@@ -8,9 +8,9 @@ import { unwrap, invalid } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const remark: RemarkParser = lazy(() => fallback(surround(
11
- str(/\[%(?=\s)/y),
11
+ str(/\[%(?=[ \n])/y),
12
12
  precedence(3, recursion(Recursion.inline,
13
- some(union([inline]), /\s%\]/y, [[/\s%\]/y, 3]]))),
13
+ some(union([inline]), /[ \n]%\]/y, [[/[ \n]%\]/y, 3]]))),
14
14
  close(text, str(`%]`)), true,
15
15
  ([as, bs = new List(), cs]) => new List([
16
16
  new Data(html('span', { class: 'remark' }, [
@@ -19,6 +19,6 @@ export const remark: RemarkParser = lazy(() => fallback(surround(
19
19
  ])),
20
20
  ]),
21
21
  ([as, bs]) => bs && as.import(bs as List<Data<string>>)),
22
- focus(/\[%+(?=\s)/y, ({ context: { source } }) => new List([
22
+ focus(/\[%+(?=[ \n])/y, ({ context: { source } }) => new List([
23
23
  new Data(html('span', { class: 'invalid', ...invalid('remark', 'syntax', 'Invalid start symbol') }, source))
24
24
  ]))));
@@ -165,19 +165,19 @@ 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/<span class="paren">(<span class="invalid">&lt;bdi&gt;</span>)</span></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/<span class="paren">(<span class="invalid">&lt;bdi&gt;</span>)</span></a>', '</bdi', '>'], '']);
170
170
  assert.deepStrictEqual(inspect(parser('[#a|<bdi>]</bdi>'), ctx), [['<a class="index" href="#index::a|&lt;bdi&gt;">a|<span class="invalid">&lt;bdi&gt;</span></a>', '</bdi', '>'], '']);
171
171
  assert.deepStrictEqual(inspect(parser('[[#a|<bdi>]</bdi>'), ctx), [['[', '<a class="index" href="#index::a|&lt;bdi&gt;">a|<span class="invalid">&lt;bdi&gt;</span></a>', '</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
- assert.deepStrictEqual(inspect(parser('0?http://host'), ctx), [['0', '?', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
180
+ assert.deepStrictEqual(inspect(parser('0?http://host'), ctx), [['0?', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
181
181
  assert.deepStrictEqual(inspect(parser('_http://host'), ctx), [['_', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
182
182
  assert.deepStrictEqual(inspect(parser('_http://host_'), ctx), [['_', '<a class="url" href="http://host" target="_blank">http://host</a>', '_'], '']);
183
183
  assert.deepStrictEqual(inspect(parser('*http://host*'), ctx), [['<em><a class="url" href="http://host" target="_blank">http://host</a></em>'], '']);
@@ -210,7 +210,7 @@ describe('Unit: parser/inline', () => {
210
210
 
211
211
  it('account', () => {
212
212
  assert.deepStrictEqual(inspect(parser('@a'), ctx), [['<a class="account" href="/@a">@a</a>'], '']);
213
- assert.deepStrictEqual(inspect(parser('@http://host'), ctx), [['@http', '://host'], '']);
213
+ assert.deepStrictEqual(inspect(parser('@http://host'), ctx), [['@', 'http', '://host'], '']);
214
214
  assert.deepStrictEqual(inspect(parser('_@a'), ctx), [['_', '<a class="account" href="/@a">@a</a>'], '']);
215
215
  assert.deepStrictEqual(inspect(parser('_@a_'), ctx), [['_', '<a class="account" href="/@a">@a</a>', '_'], '']);
216
216
  assert.deepStrictEqual(inspect(parser('*@a*'), ctx), [['<em><a class="account" href="/@a">@a</a></em>'], '']);
@@ -222,9 +222,9 @@ describe('Unit: parser/inline', () => {
222
222
  assert.deepStrictEqual(inspect(parser('#a#'), ctx), [['#a', '#'], '']);
223
223
  assert.deepStrictEqual(inspect(parser('#a#b'), ctx), [['#a', '#b'], '']);
224
224
  assert.deepStrictEqual(inspect(parser('#a'), ctx), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
225
- assert.deepStrictEqual(inspect(parser('#http://host'), ctx), [['#http', '://host'], '']);
225
+ assert.deepStrictEqual(inspect(parser('#http://host'), ctx), [['#', 'http', '://host'], '']);
226
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>'], '']);
227
- assert.deepStrictEqual(inspect(parser('##a'), ctx), [['##a'], '']);
227
+ assert.deepStrictEqual(inspect(parser('##a'), ctx), [['#', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
228
228
  assert.deepStrictEqual(inspect(parser('_#a'), ctx), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
229
229
  assert.deepStrictEqual(inspect(parser('_#a_'), ctx), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>', '_'], '']);
230
230
  assert.deepStrictEqual(inspect(parser('_#a_b'), ctx), [['_', '<a class="hashtag" href="/hashtags/a_b">#a_b</a>'], '']);
@@ -233,8 +233,8 @@ describe('Unit: parser/inline', () => {
233
233
  assert.deepStrictEqual(inspect(parser('0a#b'), ctx), [['0a', '#b'], '']);
234
234
  assert.deepStrictEqual(inspect(parser('あ#b'), ctx), [['あ', '#b'], '']);
235
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'], '']);
236
+ assert.deepStrictEqual(inspect(parser('0aあ#b'), ctx), [['0aあ', '#b'], '']);
237
+ assert.deepStrictEqual(inspect(parser('0aあい#b'), ctx), [['0aあい', '#b'], '']);
238
238
  assert.deepStrictEqual(inspect(parser('「#あ」'), ctx), [['「', '<a class="hashtag" href="/hashtags/あ">#あ</a>', '」'], '']);
239
239
  assert.deepStrictEqual(inspect(parser('a\n#b'), ctx), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
240
240
  assert.deepStrictEqual(inspect(parser('a\\\n#b'), ctx), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
@@ -248,7 +248,7 @@ describe('Unit: parser/inline', () => {
248
248
  it('hashnum', () => {
249
249
  assert.deepStrictEqual(inspect(parser('#1'), ctx), [['<a class="hashnum">#1</a>'], '']);
250
250
  assert.deepStrictEqual(inspect(parser(`#1'`), ctx), [[`<a class="hashnum">#1</a>`, `'`], '']);
251
- assert.deepStrictEqual(inspect(parser('#1234567890@a'), ctx), [['#1234567890', '@a'], '']);
251
+ assert.deepStrictEqual(inspect(parser('#1234567890@a'), ctx), [['#', '1234567890', '@a'], '']);
252
252
  assert.deepStrictEqual(inspect(parser('_#1'), ctx), [['_', '<a class="hashnum">#1</a>'], '']);
253
253
  assert.deepStrictEqual(inspect(parser('_#1_'), ctx), [['_', '<a class="hashnum">#1</a>', '_'], '']);
254
254
  assert.deepStrictEqual(inspect(parser('_#1_0'), ctx), [['_', '<a class="hashtag" href="/hashtags/1_0">#1_0</a>'], '']);
@@ -66,6 +66,7 @@ export const inline: InlineParser = lazy(() => union([
66
66
  case '%':
67
67
  return remark(input)
68
68
  || textlink(input)
69
+ || ruby(input)
69
70
  || bracket(input);
70
71
  case '#':
71
72
  case '$':
@@ -74,6 +75,7 @@ export const inline: InlineParser = lazy(() => union([
74
75
  case '|':
75
76
  return extension(input)
76
77
  || textlink(input)
78
+ || ruby(input)
77
79
  || bracket(input);
78
80
  }
79
81
  return textlink(input)
@@ -103,7 +103,6 @@ export function next(source: string, position: number, delimiter?: RegExp): numb
103
103
  const char = source[index];
104
104
  switch (char) {
105
105
  case '$':
106
- case '%':
107
106
  case '*':
108
107
  case '+':
109
108
  case '~':
@@ -111,10 +110,15 @@ export function next(source: string, position: number, delimiter?: RegExp): numb
111
110
  case '/':
112
111
  index = backToWhitespace(source, position, index);
113
112
  break;
113
+ case '%':
114
+ index += index - 1 > position && source.startsWith(' %]', index - 1)
115
+ ? -1
116
+ : 0;
117
+ break;
114
118
  case '[':
115
- index = source[index + 1] === '|'
116
- ? backToWhitespace(source, position, index)
117
- : index;
119
+ index += index - 1 > position && source.startsWith(' [|', index - 1)
120
+ ? -1
121
+ : 0;
118
122
  break;
119
123
  case ':':
120
124
  index = source.startsWith('//', index + 1)
@@ -28,7 +28,7 @@ export function repeat<N extends HTMLElement | string>(symbol: string, parser: P
28
28
  return failsafe(input => {
29
29
  const { context } = input;
30
30
  const { source, position } = context;
31
- assert(source.startsWith(symbol, context.position));
31
+ if (!source.startsWith(symbol, context.position)) return;
32
32
  let nodes = new List<Data<N>>();
33
33
  let i = symbol.length;
34
34
  for (; source[context.position + i] === source[context.position];) ++i;
@@ -8,11 +8,11 @@ import { invisibleHTMLEntityNames } from './api/normalize';
8
8
  export namespace blank {
9
9
  export const line = new RegExp(
10
10
  // TODO: 行全体をエスケープ
11
- /^(?:[^\S\r\n])*(?!\s)(\\?[^\S\r\n]|&IHN;|<wbr[^\S\n]*>|\\$)+$/mg.source
11
+ /^(\\?[^\S\r\n]|&IHN;|<wbr ?>|\\$)+$/mg.source
12
12
  .replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`),
13
13
  'gm');
14
14
  export const start = new RegExp(
15
- /(?:\\?[^\S\r\n]|&IHN;|<wbr[^\S\n]*>)+/y.source
15
+ /(?:\\?[^\S\r\n]|&IHN;|<wbr ?>)+/y.source
16
16
  .replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`), 'y');
17
17
  }
18
18
 
@@ -46,7 +46,7 @@ export function blankWith(starts: '' | '\n', delimiter: string | RegExp): RegExp
46
46
  export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegExp {
47
47
  if (delimiter === undefined) return blankWith('', starts);
48
48
  return new RegExp(String.raw
49
- `(?:(?=${starts})(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr[^\S\n]*>)${
49
+ `(?:(?=${starts})(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr ?>)${
50
50
  // 空行除去
51
51
  // 完全な空行はエスケープ済みなので再帰的バックトラックにはならない。
52
52
  starts && '+'
@@ -75,7 +75,6 @@ export function tightStart<N>(parser: Parser<N>, except?: string): Parser<N> {
75
75
  ? parser(input)
76
76
  : undefined;
77
77
  }
78
- const wbr = /<wbr[^\S\n]*>/y;
79
78
  function isTightStart(input: Input<MarkdownParser.Context>, except?: string): boolean {
80
79
  const { context } = input;
81
80
  const { source, position } = context;
@@ -100,11 +99,10 @@ function isTightStart(input: Input<MarkdownParser.Context>, except?: string): bo
100
99
  context.position = position;
101
100
  return true;
102
101
  case '<':
103
- wbr.lastIndex = position;
104
102
  switch (true) {
105
103
  case source.length - position >= 5
106
104
  && source.startsWith('<wbr', position)
107
- && (source[position + 5] === '>' || wbr.test(source)):
105
+ && (source[position + 4] === '>' || source.startsWith(' >', position + 4)):
108
106
  return false;
109
107
  }
110
108
  return true;