securemark 0.286.0 → 0.286.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.286.2
4
+
5
+ - Refactoring.
6
+
7
+ ## 0.286.1
8
+
9
+ - Refactoring.
10
+
3
11
  ## 0.286.0
4
12
 
5
13
  - Refine resource control.
package/design.md CHANGED
@@ -288,7 +288,7 @@ CodeMirrorが素では速いがVimModeでは数万文字程度でも耐え難く
288
288
  SecuremarkのAnnotation構文に典型的であるように文脈を変更する構文の中にその文脈に依存し変更される他の構文が存在する場合一般に文脈の相違から解析結果を再利用不能な(`αA'β | αAB`)バックトラックが生じる。文脈依存構文の解析中に文脈により解釈の異なる字句(`[a((`)または現在の文脈を終了する字句(ただの括弧がスコープを作らない場合の`(([a))`)に遭遇したとき、現在の文脈での解析の継続を試行する解析方法はメモ化なしではバックトラックが再帰的に生じ線形時間で解析できず、現在の文脈での解析を中止し直前の文脈を継続または新たな文脈を開始する解析方法はバックトラックなしで線形時間で解析できる(バッファまたは解析試行ログを使用して解釈を変更する実装方法があるが開始記号の一部が重複しないよう構文が制限される場合がある)。
289
289
  CommonMarkはLink構文から明らかなように後者の解析方法により再帰的バックトラックなし(Markdown(2n)+正規表現(1n)で最悪計算量3n程度)で解析できる言語であるが同時にこれは文脈依存構文の正当な入れ子表現を解析できず文脈依存構文の存在する数に応じて多項式的(おそらく$n^{2+c}$)に構文の壊れやすさの増す脆く拡張性の低い言語であることを意味する。この問題はおそらくメモ化により解決できるがCommonMarkは実行性能追及のためメモ化を廃止しているためメモ化により性能を低下させてまで解決するつもりはないと思われる(すなわちCommonMarkは機械を至上とし人間に制約を課す低水準の言語であり人間の需要を至上とするSecuremarkとは対極に位置する)。
290
290
  従って現在の再帰的バックトラックなしで解析可能な文法に制約されるCommonMarkには文脈依存構文を入れ子表現の広範な制限ならびに構文の可読性および開始記号の信頼性の多項式的な低下と引き換えにしか追加できないという拡張性の欠陥が存在する。CommonMarkの仕様策定者が構文の拡張に(名称を維持するか否かにかかわらず)不自然なまでに消極的または進展がないのは正当な理由や怠慢からでなく文脈依存構文を追加するにつれて構文解析戦略の欠陥が明白になっていくためおよび現在の高い実行性能を低下させたくないためである。`~~a~~`のような文脈自由構文は容易に追加できるがこうしたマージンを失えばもはや後はない。CommonMarkは小さく単純であるがゆえに正しくいられる象牙の塔であり仕様策定者はこの正しさを失わず正しいままでいたいがために象牙の塔に引きこもり小さな完全性を固持し続けているのである(でなければ何年も隠さず速やかにこの拡張性の欠如を公表して助力を求めていなければならない)。
291
- Securemarkは線形時間で解析不能な文脈依存言語をおおよそ20nの最悪時間計算量に改善しさらに解析時間と解析範囲の局限により一定時間内で解析不能な入力の影響を局限することでこれらの問題を解決している。この解析方法はほとんどの自然な入力に対して1nに近い時間で効率的に動作し、最悪計算量で低速に動作させる入力に対してもこの開発効率と安全性優先の低速な実装においてはサーバーで多数のユーザーのリクエストに応じるには低速で脆弱性となる可能性があるがクライアントで単一のユーザーの操作に応じるには十分高速であるためクライアントで解析する限り解析の効率または速度が実用上問題となることはなく仕様が固まり実行効率優先の高速な実装に移れば速度面の懸念もないだろう。
291
+ Securemarkは線形時間で解析不能な文脈依存言語をおおよそ14nの最悪時間計算量に改善しさらに解析時間と解析範囲の局限により一定時間内で解析不能な入力の影響を局限することでこれらの問題を解決している。この解析方法はほとんどの自然な入力に対して1nに近い時間で効率的に動作し、最悪計算量で低速に動作させる入力に対してもこの開発効率と安全性優先の低速な実装においてはサーバーで多数のユーザーのリクエストに応じるには低速で脆弱性となる可能性があるがクライアントで単一のユーザーの操作に応じるには十分高速であるためクライアントで解析する限り解析の効率または速度が実用上問題となることはなく仕様が固まり実行効率優先の高速な実装に移れば速度面の懸念もないだろう。
292
292
 
293
293
  ### 最適化
294
294
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.286.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.286.2 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
2
2
  (function webpackUniversalModuleDefinition(root, factory) {
3
3
  if(typeof exports === 'object' && typeof module === 'object')
4
4
  module.exports = factory(require("Prism"), require("DOMPurify"));
@@ -507,7 +507,7 @@ const substrong = (0, combinator_1.lazy)(() => (0, combinator_1.some)((0, combin
507
507
  const subemphasis = (0, combinator_1.lazy)(() => (0, combinator_1.some)((0, combinator_1.union)([strong_1.strong, (0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('*')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), (0, combinator_1.union)([exports.emstrong, strong_1.strong, emphasis_1.emphasis]))])));
508
508
  // 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず早く閉じるよう解析しなければならない。
509
509
  // このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
510
- exports.emstrong = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.validate)('***', (0, combinator_1.precedence)(0, (0, util_1.repeat)('***', (0, combinator_1.surround)('', (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('*')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), inline_1.inline)]))), (0, source_1.str)(/^\*{1,3}/), false, ([, bs, cs], rest, context) => {
510
+ exports.emstrong = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('***', (0, combinator_1.precedence)(0, (0, util_1.repeat)('***', (0, combinator_1.surround)('', (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('*')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), inline_1.inline)])))), (0, source_1.str)(/^\*{1,3}/), false, ([, bs, cs], rest, context) => {
511
511
  switch (cs[0]) {
512
512
  case '***':
513
513
  return [bs, rest];
@@ -552,7 +552,7 @@ nodes => [(0, dom_1.html)('em', [(0, dom_1.html)('strong', (0, dom_1.defrag)(nod
552
552
  rest = rest.slice(postfix);
553
553
  }
554
554
  return [nodes, rest];
555
- })))));
555
+ }))));
556
556
 
557
557
  /***/ },
558
558
 
@@ -995,15 +995,15 @@ function indent(opener, parser, separation = false) {
995
995
  return (0, bind_1.bind)((0, block_1.block)((0, match_1.match)(opener, (0, memoize_1.memoize)(([indent]) => (0, some_1.some)((0, line_1.line)((0, surround_1.open)(indent, ({
996
996
  source
997
997
  }) => [[source], '']))), ([indent]) => indent.length * 2 + +(indent[0] === ' '), {}), false), separation), (lines, rest, context) => {
998
- const {
999
- backtracks
1000
- } = context;
1001
- context.backtracks = {};
998
+ // 影響する使用はないはず
999
+ //const { backtracks } = context;
1000
+ //context.backtracks = {};
1002
1001
  const result = parser({
1003
1002
  source: trimBlockEnd(lines.join('')),
1004
1003
  context
1005
1004
  });
1006
- context.backtracks = backtracks;
1005
+ //context.backtracks = backtracks;
1006
+
1007
1007
  return result && (0, parser_1.exec)(result) === '' ? [(0, parser_1.eval)(result), rest] : undefined;
1008
1008
  });
1009
1009
  }
@@ -1521,7 +1521,7 @@ const source_1 = __webpack_require__(8745);
1521
1521
  const visibility_1 = __webpack_require__(6364);
1522
1522
  const array_1 = __webpack_require__(6876);
1523
1523
  const dom_1 = __webpack_require__(394);
1524
- exports.emphasis = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.surround)((0, source_1.str)('*', '*'), (0, combinator_1.precedence)(0, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([strong_1.strong, (0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('*')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), (0, combinator_1.union)([emstrong_1.emstrong, strong_1.strong, exports.emphasis]))])))), (0, source_1.str)('*'), false, ([, bs], rest) => [[(0, dom_1.html)('em', (0, dom_1.defrag)(bs))], rest], ([as, bs], rest) => [(0, array_1.unshift)(as, bs), rest])));
1524
+ exports.emphasis = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)('*', '*'), (0, combinator_1.precedence)(0, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([strong_1.strong, (0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('*')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), (0, combinator_1.union)([emstrong_1.emstrong, strong_1.strong, exports.emphasis]))]))))), (0, source_1.str)('*'), false, ([, bs], rest) => [[(0, dom_1.html)('em', (0, dom_1.defrag)(bs))], rest], ([as, bs], rest) => [(0, array_1.unshift)(as, bs), rest]));
1525
1525
 
1526
1526
  /***/ },
1527
1527
 
@@ -2002,7 +2002,7 @@ exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['http://'
2002
2002
  exports.lineurl = (0, combinator_1.lazy)(() => (0, combinator_1.open)(source_1.linebreak, (0, combinator_1.focus)(/^!?https?:\/\/\S+(?=[^\S\n]*(?:$|\n))/, (0, combinator_1.tails)([(0, source_1.str)('!'), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, false, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.convert)(url => `{ ${url} }`, link_1.unsafelink))), ({
2003
2003
  source
2004
2004
  }) => [[source], '']])]))));
2005
- const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ')'), (0, source_1.str)(')'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ']'), (0, source_1.str)(']'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), '}'), (0, source_1.str)('}'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.some)(source_1.unescsource, '"')), (0, source_1.str)('"'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */)])));
2005
+ const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ')')), (0, source_1.str)(')'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ']')), (0, source_1.str)(']'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), '}')), (0, source_1.str)('}'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)(source_1.unescsource, '"'))), (0, source_1.str)('"'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 4 /* Backtrack.url */)]));
2006
2006
 
2007
2007
  /***/ },
2008
2008
 
@@ -2466,7 +2466,7 @@ exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('$', (0,
2466
2466
  'data-invalid-type': 'content',
2467
2467
  'data-invalid-message': `"${source.match(forbiddenCommand)[0]}" command is forbidden`
2468
2468
  }, source)], ''])));
2469
- const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.escsource, /^[{}$\n]/)])), (0, source_1.str)('}'), true)));
2469
+ const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.escsource, /^[{}$\n]/)]))), (0, source_1.str)('}'), true));
2470
2470
 
2471
2471
  /***/ },
2472
2472
 
@@ -3973,9 +3973,10 @@ const visibility_1 = __webpack_require__(6364);
3973
3973
  const util_1 = __webpack_require__(4992);
3974
3974
  const array_1 = __webpack_require__(6876);
3975
3975
  const dom_1 = __webpack_require__(394);
3976
+ // 可読性のため実際にはオブリーク体を指定する。
3976
3977
  // 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
3977
3978
  // ある程度の長さのある文に使うのが望ましい。
3978
- exports.italic = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.validate)('///', (0, combinator_1.precedence)(0, (0, util_1.repeat)('///', (0, combinator_1.surround)('', (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('///')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '/'), exports.italic)]))), '///', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), nodes => [(0, dom_1.html)('i', (0, dom_1.defrag)(nodes))])))));
3979
+ exports.italic = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('///', (0, combinator_1.precedence)(0, (0, util_1.repeat)('///', (0, combinator_1.surround)('', (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('///')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '/'), exports.italic)])))), '///', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), nodes => [(0, dom_1.html)('i', (0, dom_1.defrag)(nodes))]))));
3979
3980
 
3980
3981
  /***/ },
3981
3982
 
@@ -4321,6 +4322,7 @@ Object.defineProperty(exports, "__esModule", ({
4321
4322
  exports.str = void 0;
4322
4323
  const combinator_1 = __webpack_require__(3484);
4323
4324
  function str(pattern, not) {
4325
+ const count = typeof pattern === 'object' ? /[^^\\*+][*+]/.test(pattern.source) : false;
4324
4326
  return typeof pattern === 'string' ? ({
4325
4327
  source
4326
4328
  }) => {
@@ -4333,7 +4335,7 @@ function str(pattern, not) {
4333
4335
  }) => {
4334
4336
  if (source === '') return;
4335
4337
  const m = source.match(pattern);
4336
- m && (0, combinator_1.consume)(m[0].length, context);
4338
+ count && m && (0, combinator_1.consume)(m[0].length, context);
4337
4339
  if (m && not && source.slice(m[0].length, m[0].length + not.length) === not) return;
4338
4340
  return m && m[0].length > 0 ? [[m[0]], source.slice(m[0].length)] : undefined;
4339
4341
  };
@@ -5028,7 +5030,7 @@ const dom_1 = __webpack_require__(394);
5028
5030
  exports.template = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('{{', (0, combinator_1.precedence)(1, (0, combinator_1.some)((0, combinator_1.verify)((0, combinator_1.union)([bracket, source_1.escsource]), ns => ns[0] !== "\u001B" /* Command.Escape */), '}')), '}}', true, ([, ns = []], rest) => [[(0, dom_1.html)('span', {
5029
5031
  class: 'template'
5030
5032
  }, `{{${ns.join('')}}}`)], rest], undefined, 3 | 32 /* Backtrack.template */));
5031
- const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), ')'), (0, source_1.str)(')'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), ']'), (0, source_1.str)(']'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), '}'), (0, source_1.str)('}'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.some)(source_1.escsource, /^["\n]/)), (0, source_1.str)('"'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */)])));
5033
+ const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), ')')), (0, source_1.str)(')'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), ']')), (0, source_1.str)(']'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), '}')), (0, source_1.str)('}'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)(source_1.escsource, /^["\n]/))), (0, source_1.str)('"'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 32 /* Backtrack.template */)]));
5032
5034
 
5033
5035
  /***/ },
5034
5036
 
@@ -5048,7 +5050,7 @@ const visibility_1 = __webpack_require__(6364);
5048
5050
  const util_1 = __webpack_require__(4992);
5049
5051
  const array_1 = __webpack_require__(6876);
5050
5052
  const dom_1 = __webpack_require__(394);
5051
- exports.insertion = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.validate)('++', (0, combinator_1.precedence)(0, (0, util_1.repeat)('++', (0, combinator_1.surround)('', (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', '++')), (0, combinator_1.open)('\n', (0, combinator_1.some)(exports.insertion, '+'), true)])), '++', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), nodes => [(0, dom_1.html)('ins', (0, dom_1.defrag)(nodes))])))));
5053
+ exports.insertion = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('++', (0, combinator_1.precedence)(0, (0, util_1.repeat)('++', (0, combinator_1.surround)('', (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', '++')), (0, combinator_1.open)('\n', (0, combinator_1.some)(exports.insertion, '+'), true)]))), '++', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), nodes => [(0, dom_1.html)('ins', (0, dom_1.defrag)(nodes))]))));
5052
5054
 
5053
5055
  /***/ },
5054
5056
 
@@ -5166,7 +5168,7 @@ exports.signature = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('|',
5166
5168
  class: 'indexer',
5167
5169
  'data-index': (0, indexee_1.identity)('index', undefined, ns.join('')).slice(7)
5168
5170
  })])));
5169
- const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), ')'), (0, source_1.str)(')'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), ']'), (0, source_1.str)(']'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), '}'), (0, source_1.str)('}'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.some)(source_1.txt, '"')), (0, source_1.str)('"'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */)])));
5171
+ const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), ')')), (0, source_1.str)(')'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), ']')), (0, source_1.str)(']'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), '}')), (0, source_1.str)('}'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.some)(source_1.txt, '"'))), (0, source_1.str)('"'), true, undefined, () => [["\u001B" /* Command.Escape */], ''], 3 | 28 /* Backtrack.index */)]));
5170
5172
  function dataindex(ns) {
5171
5173
  if (ns.length === 0) return;
5172
5174
  for (let i = ns.length - 1; i >= 0; --i) {
@@ -5410,9 +5412,9 @@ const attrspecs = {
5410
5412
  };
5411
5413
  Object.setPrototypeOf(attrspecs, null);
5412
5414
  Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
5413
- exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/^<[a-z]+(?=[^\S\n]|>)/i, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.union)([(0, combinator_1.focus)(/^<wbr[^\S\n]*>/i, () => [[(0, dom_1.html)('wbr')], '']), (0, combinator_1.surround)(
5415
+ exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/^<[a-z]+(?=[^\S\n]|>)/i, (0, combinator_1.union)([(0, combinator_1.focus)(/^<wbr[^\S\n]*>/i, () => [[(0, dom_1.html)('wbr')], '']), (0, combinator_1.surround)(
5414
5416
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
5415
- (0, source_1.str)(/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/i), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, source_1.str)(/^[^\S\n]*>/), true, ([as, bs = [], cs], rest) => [[elem(as[0].slice(1), (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [])], rest]), (0, combinator_1.match)(new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.precedence)(3, (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)((0, combinator_1.open)(/^\n?/, (0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', `</${tag}>`), [[(0, visibility_1.blankWith)('\n', `</${tag}>`), 3]]), true))])), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => tag, new Map())), (0, combinator_1.match)(/^<([a-z]+)(?=[^\S\n]|>)/i, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.precedence)(3, (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)(inline_1.inline, `</${tag}>`, [[`</${tag}>`, 3]])])), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => tag, new clock_1.Clock(10000)))]))));
5417
+ (0, source_1.str)(/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/i), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, source_1.str)(/^[^\S\n]*>/), true, ([as, bs = [], cs], rest) => [[elem(as[0].slice(1), (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [])], rest]), (0, combinator_1.match)(new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.precedence)(3, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)((0, combinator_1.open)(/^\n?/, (0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', `</${tag}>`), [[(0, visibility_1.blankWith)('\n', `</${tag}>`), 3]]), true))]))), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => tag, new Map())), (0, combinator_1.match)(/^<([a-z]+)(?=[^\S\n]|>)/i, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.precedence)(3, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)(inline_1.inline, `</${tag}>`, [[`</${tag}>`, 3]])]))), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => tag, new clock_1.Clock(10000)))])));
5416
5418
  exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="[^"\n]*")?(?=[^\S\n]|>)/i)]);
5417
5419
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
5418
5420
  // [...document.querySelectorAll('tbody > tr > td:first-child')].map(el => el.textContent.slice(1, -1))
@@ -5494,7 +5496,7 @@ const visibility_1 = __webpack_require__(6364);
5494
5496
  const util_1 = __webpack_require__(4992);
5495
5497
  const array_1 = __webpack_require__(6876);
5496
5498
  const dom_1 = __webpack_require__(394);
5497
- exports.mark = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(2 /* State.mark */, false, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.validate)('==', (0, combinator_1.precedence)(0, (0, util_1.repeat)('==', (0, combinator_1.surround)('', (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('==')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '='), exports.mark)]))), '==', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), (nodes, {
5499
+ exports.mark = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(251 /* State.linkers */ & ~2 /* State.mark */, false, (0, combinator_1.validate)('==', (0, combinator_1.precedence)(0, (0, combinator_1.state)(2 /* State.mark */, (0, util_1.repeat)('==', (0, combinator_1.surround)('', (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('==')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '='), exports.mark)])))), '==', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), (nodes, {
5498
5500
  id
5499
5501
  }) => {
5500
5502
  const el = (0, dom_1.html)('mark', (0, dom_1.defrag)(nodes));
@@ -5545,9 +5547,9 @@ exports.isAlphanumeric = exports.linebreak = exports.txt = exports.text = export
5545
5547
  const combinator_1 = __webpack_require__(3484);
5546
5548
  const str_1 = __webpack_require__(4017);
5547
5549
  const dom_1 = __webpack_require__(394);
5548
- exports.delimiter = /[\s\x00-\x7F()[]{}“”‘’「」『』]|\S[#>]/u;
5550
+ exports.delimiter = /[\s\x00-\x7F()[]{}“”‘’「」『』]|\S#|[0-9A-Za-z]>/u;
5549
5551
  exports.nonWhitespace = /[\S\n]|$/u;
5550
- exports.nonAlphanumeric = /[^0-9A-Za-z]|\S[#>]|$/u;
5552
+ exports.nonAlphanumeric = /[^0-9A-Za-z]|\S#|[0-9A-Za-z]>|$/u;
5551
5553
  const repeat = (0, str_1.str)(/^(.)\1*/);
5552
5554
  const text = ({
5553
5555
  source,
@@ -5922,7 +5924,7 @@ const dom_1 = __webpack_require__(394);
5922
5924
  // https://example/hashtags/a must be a hashtag page or a redirect page going there.
5923
5925
  // https://github.com/tc39/proposal-regexp-unicode-property-escapes#matching-emoji
5924
5926
  exports.emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
5925
- exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp([/^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|')))/u.source, /(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source].join('').replace(/emoji/g, exports.emoji), 'u'))), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, false, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`, link_1.unsafelink), ([el]) => [(0, dom_1.define)(el, {
5927
+ exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp([/^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^'\p{C}\p{S}\p{P}\s]|emoji)))/u.source, /(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^'\p{C}\p{S}\p{P}\s]|emoji))+/u.source].join('').replace(/emoji/g, exports.emoji), 'u'))), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, false, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`, link_1.unsafelink), ([el]) => [(0, dom_1.define)(el, {
5926
5928
  class: 'hashtag'
5927
5929
  })]))), ({
5928
5930
  source
@@ -6207,12 +6209,12 @@ const array_1 = __webpack_require__(6876);
6207
6209
  const dom_1 = __webpack_require__(394);
6208
6210
  // Don't use the symbols already used: !#$%@&*+~=|
6209
6211
  // All syntax surrounded by square brackets shouldn't contain line breaks.
6210
- exports.placeholder = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.surround)((0, source_1.str)(/^\[[:^|]/), (0, combinator_1.precedence)(1, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ']', [[']', 1]]))), (0, source_1.str)(']'), false, ([, bs], rest) => [[(0, dom_1.html)('span', {
6212
+ exports.placeholder = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)(/^\[[:^|]/), (0, combinator_1.precedence)(1, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ']', [[']', 1]])))), (0, source_1.str)(']'), false, ([, bs], rest) => [[(0, dom_1.html)('span', {
6211
6213
  class: 'invalid',
6212
6214
  'data-invalid-syntax': 'extension',
6213
6215
  'data-invalid-type': 'syntax',
6214
6216
  'data-invalid-message': `Invalid start symbol or linebreak`
6215
- }, (0, dom_1.defrag)(bs))], rest], ([as, bs], rest) => [(0, array_1.unshift)(as, bs), rest], 3 | 8 /* Backtrack.bracket */)));
6217
+ }, (0, dom_1.defrag)(bs))], rest], ([as, bs], rest) => [(0, array_1.unshift)(as, bs), rest], 3 | 8 /* Backtrack.bracket */));
6216
6218
 
6217
6219
  /***/ },
6218
6220
 
@@ -6573,7 +6575,7 @@ const source_1 = __webpack_require__(8745);
6573
6575
  const visibility_1 = __webpack_require__(6364);
6574
6576
  const array_1 = __webpack_require__(6876);
6575
6577
  const dom_1 = __webpack_require__(394);
6576
- exports.strong = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.surround)((0, source_1.str)('**', '*'), (0, combinator_1.precedence)(0, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('**')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), (0, combinator_1.union)([emstrong_1.emstrong, exports.strong]))])))), (0, source_1.str)('**'), false, ([, bs], rest) => [[(0, dom_1.html)('strong', (0, dom_1.defrag)(bs))], rest], ([as, bs], rest) => [(0, array_1.unshift)(as, bs), rest])));
6578
+ exports.strong = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)('**', '*'), (0, combinator_1.precedence)(0, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('**')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), (0, combinator_1.union)([emstrong_1.emstrong, exports.strong]))]))))), (0, source_1.str)('**'), false, ([, bs], rest) => [[(0, dom_1.html)('strong', (0, dom_1.defrag)(bs))], rest], ([as, bs], rest) => [(0, array_1.unshift)(as, bs), rest]));
6577
6579
 
6578
6580
  /***/ },
6579
6581
 
@@ -6955,7 +6957,7 @@ const visibility_1 = __webpack_require__(6364);
6955
6957
  const util_1 = __webpack_require__(4992);
6956
6958
  const array_1 = __webpack_require__(6876);
6957
6959
  const dom_1 = __webpack_require__(394);
6958
- exports.deletion = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.validate)('~~', (0, combinator_1.precedence)(0, (0, util_1.repeat)('~~', (0, combinator_1.surround)('', (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', '~~')), (0, combinator_1.open)('\n', (0, combinator_1.some)(exports.deletion, '~'), true)])), '~~', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), nodes => [(0, dom_1.html)('del', (0, dom_1.defrag)(nodes))])))));
6960
+ exports.deletion = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('~~', (0, combinator_1.precedence)(0, (0, util_1.repeat)('~~', (0, combinator_1.surround)('', (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', '~~')), (0, combinator_1.open)('\n', (0, combinator_1.some)(exports.deletion, '~'), true)]))), '~~', false, ([, bs], rest) => [bs, rest], ([, bs], rest) => [(0, array_1.push)(bs, ["\u001B" /* Command.Escape */]), rest]), nodes => [(0, dom_1.html)('del', (0, dom_1.defrag)(nodes))]))));
6959
6961
 
6960
6962
  /***/ },
6961
6963
 
@@ -7149,20 +7151,20 @@ const source_1 = __webpack_require__(8745);
7149
7151
  const visibility_1 = __webpack_require__(6364);
7150
7152
  const array_1 = __webpack_require__(6876);
7151
7153
  const dom_1 = __webpack_require__(394);
7152
- exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.sequence)([(0, combinator_1.bind)((0, combinator_1.surround)('[', (0, source_1.str)(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ']', false, undefined, undefined, 3 | 20 /* Backtrack.ruby */), ([source], rest, context) => {
7154
+ exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.sequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, source_1.str)(/^(?:\\[^\n]|[^\\[\](){}<>"\n])+/u), ']', false, ([, [source]], rest, context) => {
7153
7155
  const ns = (0, parser_1.eval)(text({
7154
7156
  source,
7155
7157
  context
7156
7158
  }), [undefined])[0];
7157
7159
  ns && ns.at(-1) === '' && ns.pop();
7158
- return ns && (0, visibility_1.isTightNodeStart)(ns) ? [[ns], rest] : undefined;
7159
- }), (0, combinator_1.bind)((0, combinator_1.surround)('(', (0, source_1.str)(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ')', false, undefined, undefined, 3 | 20 /* Backtrack.ruby */), ([source], rest, context) => {
7160
+ return ns && (0, visibility_1.isTightNodeStart)(ns) ? [ns, rest] : undefined;
7161
+ }, undefined, 3 | 20 /* Backtrack.ruby */)), (0, combinator_1.dup)((0, combinator_1.surround)('(', (0, source_1.str)(/^(?:\\[^\n]|[^\\[\](){}<>"\n])+/u), ')', false, ([, [source]], rest, context) => {
7160
7162
  const ns = (0, parser_1.eval)(text({
7161
7163
  source,
7162
7164
  context
7163
7165
  }), [undefined])[0];
7164
- return ns && [[ns], rest];
7165
- })]), ([texts, rubies]) => {
7166
+ return ns && [ns, rest];
7167
+ }, undefined, 3 | 20 /* Backtrack.ruby */))]), ([texts, rubies]) => {
7166
7168
  switch (true) {
7167
7169
  case rubies.length <= texts.length:
7168
7170
  return [(0, dom_1.html)('ruby', attributes(texts, rubies), (0, dom_1.defrag)(texts.reduce((acc, _, i) => (0, array_1.push)(acc, (0, array_1.unshift)([texts[i]], i < rubies.length && rubies[i] ? [(0, dom_1.html)('rp', '('), (0, dom_1.html)('rt', rubies[i]), (0, dom_1.html)('rp', ')')] : [(0, dom_1.html)('rt')])), [])))];
@@ -7804,12 +7806,11 @@ function rewrite(scope, parser) {
7804
7806
  context
7805
7807
  } = input;
7806
7808
  if (source === '') return;
7807
- const {
7808
- backtracks
7809
- } = context;
7810
- context.backtracks = {};
7809
+ // 影響する使用はないはず
7810
+ //const { backtracks } = context;
7811
+ //context.backtracks = {};
7811
7812
  const res1 = scope(input);
7812
- context.backtracks = backtracks;
7813
+ //context.backtracks = backtracks;
7813
7814
  if (res1 === undefined || (0, parser_1.exec)(res1).length >= source.length) return;
7814
7815
  const src = source.slice(0, source.length - (0, parser_1.exec)(res1).length);
7815
7816
  const offset = source.length - src.length;
@@ -8153,15 +8154,15 @@ const hashnum_1 = __webpack_require__(8684);
8153
8154
  const anchor_1 = __webpack_require__(8535);
8154
8155
  const source_1 = __webpack_require__(8745);
8155
8156
  const util_1 = __webpack_require__(4992);
8156
- exports.autolink = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/^(?:[@#>0-9a-z]|\S[#>]|[\r\n]!?https?:\/\/)/iu, (0, combinator_1.state)(~1 /* State.autolink */, (0, combinator_1.union)([(0, combinator_1.some)((0, combinator_1.union)([url_1.lineurl])), (0, combinator_1.fmap)((0, combinator_1.some)((0, combinator_1.union)([url_1.url, email_1.email,
8157
+ exports.autolink = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/^(?:[@#>0-9a-z]|\S#|[0-9a-z]>|[\r\n]!?https?:\/\/)/iu, (0, combinator_1.state)(~1 /* State.autolink */, (0, combinator_1.union)([(0, combinator_1.some)((0, combinator_1.union)([url_1.lineurl])), (0, combinator_1.fmap)((0, combinator_1.some)((0, combinator_1.union)([url_1.url, email_1.email,
8157
8158
  // Escape unmatched email-like strings.
8158
- (0, source_1.str)(/^[0-9a-z]+(?:[_.+-][0-9a-z]+)*(?:@(?:[0-9a-z]+(?:[.-][0-9a-z]+)*)?)*/i), channel_1.channel, account_1.account,
8159
+ (0, source_1.str)(/^[0-9a-z]+(?:[_.+-][0-9a-z]+|@(?=@))*/i), channel_1.channel, account_1.account,
8159
8160
  // Escape unmatched account-like strings.
8160
- (0, source_1.str)(/^@+[0-9a-z]*(?:[-.][0-9a-z]+)*/i),
8161
+ (0, source_1.str)(/^@+(?:[0-9a-z]+(?:[_.+-][0-9a-z]+)*)?/i),
8161
8162
  // Escape invalid leading characters.
8162
- (0, source_1.str)(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji|['_])(?=#)/u.source.replace('emoji', hashtag_1.emoji), 'u')), hashtag_1.hashtag, hashnum_1.hashnum,
8163
+ (0, source_1.str)(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji)(?=#)/u.source.replace('emoji', hashtag_1.emoji), 'u')), hashtag_1.hashtag, hashnum_1.hashnum,
8163
8164
  // Escape unmatched hashtag-like strings.
8164
- (0, source_1.str)(new RegExp(/^#+(?:[^\p{C}\p{S}\p{P}\s]|emoji|['_])*/u.source.replace('emoji', hashtag_1.emoji), 'u')),
8165
+ (0, source_1.str)(new RegExp(/^#+(?:(?:[^\p{C}\p{S}\p{P}\s]|emoji)+(?:['_.+-](?:[^\p{C}\p{S}\p{P}\s]|emoji)+)*)?/u.source.replace(/emoji/g, hashtag_1.emoji), 'u')),
8165
8166
  // Escape invalid leading characters.
8166
8167
  (0, source_1.str)(/^[0-9a-z](?=>)/iu), anchor_1.anchor])), ns => ns.length === 1 ? ns : [(0, util_1.stringify)(ns)])]))));
8167
8168
 
@@ -8679,7 +8680,7 @@ const link_1 = __webpack_require__(3628);
8679
8680
  const hashtag_1 = __webpack_require__(5764);
8680
8681
  const source_1 = __webpack_require__(8745);
8681
8682
  const dom_1 = __webpack_require__(394);
8682
- exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, false, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, link_1.unsafelink), ([el]) => [(0, dom_1.define)(el, {
8683
+ exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji)/u.source.replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, false, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, link_1.unsafelink), ([el]) => [(0, dom_1.define)(el, {
8683
8684
  class: 'hashnum',
8684
8685
  href: null
8685
8686
  })]))), ({
@@ -8880,11 +8881,11 @@ const source_1 = __webpack_require__(8745);
8880
8881
  const memoize_1 = __webpack_require__(6925);
8881
8882
  const array_1 = __webpack_require__(6876);
8882
8883
  const dom_1 = __webpack_require__(394);
8883
- exports.remark = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.match)(/^\[(%+)\s/, (0, memoize_1.memoize)(([, fence]) => (0, combinator_1.surround)((0, source_1.str)(`[${fence}`), (0, combinator_1.precedence)(4, (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), new RegExp(String.raw`^\s+${fence}\]`), [[new RegExp(String.raw`^\s+${fence}\]`), 4]])), (0, combinator_1.close)((0, combinator_1.some)(source_1.text, '%'), (0, source_1.str)(`${fence}]`)), true, ([as, bs = [], cs], rest) => [[(0, dom_1.html)('span', {
8884
+ exports.remark = (0, combinator_1.lazy)(() => (0, combinator_1.match)(/^\[(%+)\s/, (0, memoize_1.memoize)(([, fence]) => (0, combinator_1.surround)((0, source_1.str)(`[${fence}`), (0, combinator_1.precedence)(4, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), new RegExp(String.raw`^\s+${fence}\]`), [[new RegExp(String.raw`^\s+${fence}\]`), 4]]))), (0, combinator_1.close)((0, combinator_1.some)(source_1.text, '%'), (0, source_1.str)(`${fence}]`)), true, ([as, bs = [], cs], rest) => [[(0, dom_1.html)('span', {
8884
8885
  class: 'remark'
8885
8886
  }, [(0, dom_1.html)('input', {
8886
8887
  type: 'checkbox'
8887
- }), (0, dom_1.html)('span', (0, dom_1.defrag)((0, array_1.push)((0, array_1.unshift)(as, bs), cs)))])], rest], ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), ([, fence]) => fence.length, {}))));
8888
+ }), (0, dom_1.html)('span', (0, dom_1.defrag)((0, array_1.push)((0, array_1.unshift)(as, bs), cs)))])], rest], ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), ([, fence]) => fence.length, {})));
8888
8889
 
8889
8890
  /***/ },
8890
8891
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.286.0",
3
+ "version": "0.286.2",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -20,10 +20,12 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
20
20
  ([indent]) => indent.length * 2 + +(indent[0] === ' '), {}), false), separation),
21
21
  (lines, rest, context) => {
22
22
  assert(parser = parser as Parser<T>);
23
- const { backtracks } = context;
24
- context.backtracks = {};
23
+ // 影響する使用はないはず
24
+ //const { backtracks } = context;
25
+ //context.backtracks = {};
25
26
  const result = parser({ source: trimBlockEnd(lines.join('')), context });
26
- context.backtracks = backtracks;
27
+ //context.backtracks = backtracks;
28
+ assert(result);
27
29
  return result && exec(result) === ''
28
30
  ? [eval(result), rest]
29
31
  : undefined;
@@ -37,11 +37,12 @@ export function rewrite<T>(scope: Parser<unknown>, parser: Parser<T>): Parser<T>
37
37
  return input => {
38
38
  const { source, context } = input;
39
39
  if (source === '') return;
40
- const { backtracks } = context;
41
- context.backtracks = {};
40
+ // 影響する使用はないはず
41
+ //const { backtracks } = context;
42
+ //context.backtracks = {};
42
43
  const res1 = scope(input);
43
44
  assert(check(source, res1));
44
- context.backtracks = backtracks;
45
+ //context.backtracks = backtracks;
45
46
  if (res1 === undefined || exec(res1).length >= source.length) return;
46
47
  const src = source.slice(0, source.length - exec(res1).length);
47
48
  assert(src !== '');
@@ -332,14 +332,14 @@ describe('Unit: parser/api/parse', () => {
332
332
  if (!navigator.userAgent.includes('Chrome')) return;
333
333
 
334
334
  it('creation', function () {
335
- this.timeout(5000);
335
+ this.timeout(10000);
336
336
  assert.deepStrictEqual(
337
337
  [...parse('.'.repeat(100000)).children].map(el => el.outerHTML),
338
338
  [`<p>${'.'.repeat(100000)}</p>`]);
339
339
  });
340
340
 
341
341
  it.skip('creation error', function () {
342
- this.timeout(5000);
342
+ this.timeout(10000);
343
343
  assert.deepStrictEqual(
344
344
  [...parse('.'.repeat(100001)).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
345
345
  [
@@ -350,18 +350,20 @@ describe('Unit: parser/api/parse', () => {
350
350
 
351
351
  it('backtrack', function () {
352
352
  this.timeout(5000);
353
+ const str = `${'.'.repeat(9)}((${'['.repeat(13)}{{http://[[[${'.'.repeat(7134)}`;
353
354
  assert.deepStrictEqual(
354
- [...parse(`..((${'['.repeat(16)}http://{{${'.'.repeat(5876)}`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
355
- [`<p>..((${'['.repeat(16)}http://{{${'.'.repeat(5876)}</p>`]);
355
+ [...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
356
+ [`<p>${str}</p>`]);
356
357
  });
357
358
 
358
359
  it('backtrack error', function () {
359
360
  this.timeout(5000);
361
+ const str = `${'.'.repeat(9)}((${'['.repeat(13)}{{http://[[[${'.'.repeat(7134 + 1)}`;
360
362
  assert.deepStrictEqual(
361
- [...parse(`..((${'['.repeat(16)}http://{{${'.'.repeat(5877)}`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
363
+ [...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
362
364
  [
363
365
  '<h1 id="error:rnd" class="error">Error: Too many creations</h1>',
364
- `<pre class="error" translate="no">..((${'['.repeat(16)}http://{{${'.'.repeat(1000 - 2 - 2 - 16 - 9 - 3)}...</pre>`,
366
+ `<pre class="error" translate="no">${str.slice(0, 1000 - 3)}...</pre>`,
365
367
  ]);
366
368
  });
367
369
 
@@ -10,7 +10,6 @@ describe('Unit: parser/inline/autolink/hashnum', () => {
10
10
  assert.deepStrictEqual(inspect(parser('')), undefined);
11
11
  assert.deepStrictEqual(inspect(parser('#')), [['#'], '']);
12
12
  assert.deepStrictEqual(inspect(parser('# ')), [['#'], ' ']);
13
- assert.deepStrictEqual(inspect(parser('#1_')), [['#1_'], '']);
14
13
  assert.deepStrictEqual(inspect(parser('#1#')), [['#1#'], '']);
15
14
  assert.deepStrictEqual(inspect(parser('#1#2')), [['#1#2'], '']);
16
15
  assert.deepStrictEqual(inspect(parser('#1#2#3')), [['#1#2#3'], '']);
@@ -44,6 +43,8 @@ describe('Unit: parser/inline/autolink/hashnum', () => {
44
43
  assert.deepStrictEqual(inspect(parser('#1\\')), [['<a class="hashnum">#1</a>'], '\\']);
45
44
  assert.deepStrictEqual(inspect(parser('#1\\ ')), [['<a class="hashnum">#1</a>'], '\\ ']);
46
45
  assert.deepStrictEqual(inspect(parser('#1\\\n')), [['<a class="hashnum">#1</a>'], '\\\n']);
46
+ assert.deepStrictEqual(inspect(parser(`#1'`)), [[`<a class="hashnum">#1</a>`], `'`]);
47
+ assert.deepStrictEqual(inspect(parser(`#1''`)), [[`<a class="hashnum">#1</a>`], `''`]);
47
48
  assert.deepStrictEqual(inspect(parser('#123456789')), [['<a class="hashnum">#123456789</a>'], '']);
48
49
  });
49
50
 
@@ -7,7 +7,7 @@ import { str } from '../../source';
7
7
  import { define } from 'typed-dom/dom';
8
8
 
9
9
  export const hashnum: AutolinkParser.HashnumParser = lazy(() => rewrite(
10
- open('#', str(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, emoji), 'u'))),
10
+ open('#', str(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji)/u.source.replace(/emoji/, emoji), 'u'))),
11
11
  union([
12
12
  constraint(State.autolink, false, state(State.autolink, fmap(convert(
13
13
  source => `[${source}]{ ${source.slice(1)} }`,
@@ -20,11 +20,11 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
20
20
  assert.deepStrictEqual(inspect(parser('##')), [['##'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('##a')), [['##a'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('###a')), [['###a'], '']);
23
- assert.deepStrictEqual(inspect(parser(`#'`)), [[`#'`], '']);
24
- assert.deepStrictEqual(inspect(parser(`#'0`)), [[`#'0`], '']);
25
- assert.deepStrictEqual(inspect(parser(`#'00`)), [[`#'00`], '']);
26
- assert.deepStrictEqual(inspect(parser('#_')), [['#_'], '']);
27
- assert.deepStrictEqual(inspect(parser('#_a')), [['#_a'], '']);
23
+ assert.deepStrictEqual(inspect(parser(`#'`)), [['#'], `'`]);
24
+ assert.deepStrictEqual(inspect(parser(`#'0`)), [[`#`], `'0`]);
25
+ assert.deepStrictEqual(inspect(parser(`#'00`)), [[`#`], `'00`]);
26
+ assert.deepStrictEqual(inspect(parser('#_')), [['#'], '_']);
27
+ assert.deepStrictEqual(inspect(parser('#_a')), [['#'], '_a']);
28
28
  assert.deepStrictEqual(inspect(parser('#(a)')), [['#'], '(a)']);
29
29
  assert.deepStrictEqual(inspect(parser('#{}')), [['#'], '{}']);
30
30
  assert.deepStrictEqual(inspect(parser('#{{}')), [['#'], '{{}']);
@@ -58,13 +58,12 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
58
58
  assert.deepStrictEqual(inspect(parser('#1a')), [['<a class="hashtag" href="/hashtags/1a">#1a</a>'], '']);
59
59
  assert.deepStrictEqual(inspect(parser('#1あ')), [['<a class="hashtag" href="/hashtags/1あ">#1あ</a>'], '']);
60
60
  assert.deepStrictEqual(inspect(parser('#1👩')), [['<a class="hashtag" href="/hashtags/1👩">#1👩</a>'], '']);
61
- assert.deepStrictEqual(inspect(parser(`#1'`)), [[`<a class="hashtag" href="/hashtags/1'">#1'</a>`], '']);
62
- assert.deepStrictEqual(inspect(parser(`#1''`)), [[`<a class="hashtag" href="/hashtags/1''">#1''</a>`], '']);
63
- assert.deepStrictEqual(inspect(parser(`#a'`)), [[`<a class="hashtag" href="/hashtags/a'">#a'</a>`], '']);
64
- assert.deepStrictEqual(inspect(parser(`#a''`)), [[`<a class="hashtag" href="/hashtags/a''">#a''</a>`], '']);
61
+ assert.deepStrictEqual(inspect(parser(`#a'`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `'`]);
62
+ assert.deepStrictEqual(inspect(parser(`#a''`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `''`]);
65
63
  assert.deepStrictEqual(inspect(parser(`#a'b`)), [[`<a class="hashtag" href="/hashtags/a'b">#a'b</a>`], '']);
66
- assert.deepStrictEqual(inspect(parser(`#a'_b`)), [[`<a class="hashtag" href="/hashtags/a'_b">#a'_b</a>`], '']);
67
- assert.deepStrictEqual(inspect(parser(`#a_'b`)), [[`<a class="hashtag" href="/hashtags/a_'b">#a_'b</a>`], '']);
64
+ assert.deepStrictEqual(inspect(parser(`#a'0`)), [[`<a class="hashtag" href="/hashtags/a'0">#a'0</a>`], '']);
65
+ assert.deepStrictEqual(inspect(parser(`#a'_b`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `'_b`]);
66
+ assert.deepStrictEqual(inspect(parser(`#a_'b`)), [[`<a class="hashtag" href="/hashtags/a">#a</a>`], `_'b`]);
68
67
  assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(9)}a`)), [[`<a class="hashtag" href="/hashtags/${'1'.repeat(9)}a">#${'1'.repeat(9)}a</a>`], '']);
69
68
  });
70
69
 
@@ -14,8 +14,8 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => rewrite(
14
14
  open(
15
15
  '#',
16
16
  str(new RegExp([
17
- /^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|')))/u.source,
18
- /(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source,
17
+ /^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^'\p{C}\p{S}\p{P}\s]|emoji)))/u.source,
18
+ /(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^'\p{C}\p{S}\p{P}\s]|emoji))+/u.source,
19
19
  ].join('').replace(/emoji/g, emoji), 'u'))),
20
20
  union([
21
21
  constraint(State.autolink, false, state(State.autolink, fmap(convert(
@@ -34,13 +34,13 @@ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => open(
34
34
  ]),
35
35
  ]))));
36
36
 
37
- const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => recursion(Recursion.terminal, union([
38
- surround(str('('), some(union([bracket, unescsource]), ')'), str(')'), true,
37
+ const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => union([
38
+ surround(str('('), recursion(Recursion.terminal, some(union([bracket, unescsource]), ')')), str(')'), true,
39
39
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
40
- surround(str('['), some(union([bracket, unescsource]), ']'), str(']'), true,
40
+ surround(str('['), recursion(Recursion.terminal, some(union([bracket, unescsource]), ']')), str(']'), true,
41
41
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
42
- surround(str('{'), some(union([bracket, unescsource]), '}'), str('}'), true,
42
+ surround(str('{'), recursion(Recursion.terminal, some(union([bracket, unescsource]), '}')), str('}'), true,
43
43
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
44
- surround(str('"'), precedence(2, some(unescsource, '"')), str('"'), true,
44
+ surround(str('"'), precedence(2, recursion(Recursion.terminal, some(unescsource, '"'))), str('"'), true,
45
45
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.url),
46
- ])));
46
+ ]));
@@ -12,7 +12,7 @@ import { str } from '../source';
12
12
  import { stringify } from '../util';
13
13
 
14
14
  export const autolink: AutolinkParser = lazy(() =>
15
- validate(/^(?:[@#>0-9a-z]|\S[#>]|[\r\n]!?https?:\/\/)/iu,
15
+ validate(/^(?:[@#>0-9a-z]|\S#|[0-9a-z]>|[\r\n]!?https?:\/\/)/iu,
16
16
  state(~State.autolink,
17
17
  union([
18
18
  some(union([lineurl])),
@@ -20,17 +20,17 @@ export const autolink: AutolinkParser = lazy(() =>
20
20
  url,
21
21
  email,
22
22
  // Escape unmatched email-like strings.
23
- str(/^[0-9a-z]+(?:[_.+-][0-9a-z]+)*(?:@(?:[0-9a-z]+(?:[.-][0-9a-z]+)*)?)*/i),
23
+ str(/^[0-9a-z]+(?:[_.+-][0-9a-z]+|@(?=@))*/i),
24
24
  channel,
25
25
  account,
26
26
  // Escape unmatched account-like strings.
27
- str(/^@+[0-9a-z]*(?:[-.][0-9a-z]+)*/i),
27
+ str(/^@+(?:[0-9a-z]+(?:[_.+-][0-9a-z]+)*)?/i),
28
28
  // Escape invalid leading characters.
29
- str(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji|['_])(?=#)/u.source.replace('emoji', emoji), 'u')),
29
+ str(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji)(?=#)/u.source.replace('emoji', emoji), 'u')),
30
30
  hashtag,
31
31
  hashnum,
32
32
  // Escape unmatched hashtag-like strings.
33
- str(new RegExp(/^#+(?:[^\p{C}\p{S}\p{P}\s]|emoji|['_])*/u.source.replace('emoji', emoji), 'u')),
33
+ str(new RegExp(/^#+(?:(?:[^\p{C}\p{S}\p{P}\s]|emoji)+(?:['_.+-](?:[^\p{C}\p{S}\p{P}\s]|emoji)+)*)?/u.source.replace(/emoji/g, emoji), 'u')),
34
34
  // Escape invalid leading characters.
35
35
  str(/^[0-9a-z](?=>)/iu),
36
36
  anchor,
@@ -7,14 +7,15 @@ import { repeat } from '../util';
7
7
  import { push } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
- export const deletion: DeletionParser = lazy(() => recursion(Recursion.inline, validate('~~',
10
+ export const deletion: DeletionParser = lazy(() => validate('~~',
11
11
  precedence(0, repeat('~~', surround(
12
12
  '',
13
+ recursion(Recursion.inline,
13
14
  some(union([
14
15
  some(inline, blankWith('\n', '~~')),
15
16
  open('\n', some(deletion, '~'), true),
16
- ])),
17
+ ]))),
17
18
  '~~', false,
18
19
  ([, bs], rest) => [bs, rest],
19
20
  ([, bs], rest) => [push(bs, [Command.Escape]), rest]),
20
- nodes => [html('del', defrag(nodes))])))));
21
+ nodes => [html('del', defrag(nodes))]))));
@@ -9,9 +9,9 @@ import { tightStart, blankWith } from '../visibility';
9
9
  import { unshift } from 'spica/array';
10
10
  import { html, defrag } from 'typed-dom/dom';
11
11
 
12
- export const emphasis: EmphasisParser = lazy(() => recursion(Recursion.inline, surround(
12
+ export const emphasis: EmphasisParser = lazy(() => surround(
13
13
  str('*', '*'),
14
- precedence(0,
14
+ precedence(0, recursion(Recursion.inline,
15
15
  tightStart(some(union([
16
16
  strong,
17
17
  some(inline, blankWith('*')),
@@ -20,7 +20,7 @@ export const emphasis: EmphasisParser = lazy(() => recursion(Recursion.inline, s
20
20
  strong,
21
21
  emphasis,
22
22
  ])),
23
- ])))),
23
+ ]))))),
24
24
  str('*'), false,
25
25
  ([, bs], rest) => [[html('em', defrag(bs))], rest],
26
- ([as, bs], rest) => [unshift(as, bs), rest])));
26
+ ([as, bs], rest) => [unshift(as, bs), rest]));
@@ -30,13 +30,14 @@ const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
30
30
 
31
31
  // 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず早く閉じるよう解析しなければならない。
32
32
  // このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
33
- export const emstrong: EmStrongParser = lazy(() => recursion(Recursion.inline, validate('***',
33
+ export const emstrong: EmStrongParser = lazy(() => validate('***',
34
34
  precedence(0, repeat('***', surround(
35
35
  '',
36
+ recursion(Recursion.inline,
36
37
  tightStart(some(union([
37
38
  some(inline, blankWith('*')),
38
39
  open(some(inline, '*'), inline),
39
- ]))),
40
+ ])))),
40
41
  str(/^\*{1,3}/), false,
41
42
  ([, bs, cs], rest, context): Result<HTMLElement | string, typeof context> => {
42
43
  assert(cs.length === 1);
@@ -93,4 +94,4 @@ export const emstrong: EmStrongParser = lazy(() => recursion(Recursion.inline, v
93
94
  rest = rest.slice(postfix);
94
95
  }
95
96
  return [nodes, rest];
96
- })))));
97
+ }))));
@@ -40,16 +40,16 @@ export const signature: IndexParser.SignatureParser = lazy(() => validate('|', f
40
40
  html('span', { class: 'indexer', 'data-index': identity('index', undefined, ns.join(''))!.slice(7) }),
41
41
  ])));
42
42
 
43
- const bracket: IndexParser.SignatureParser.BracketParser = lazy(() => recursion(Recursion.terminal, union([
44
- surround(str('('), some(union([bracket, txt]), ')'), str(')'), true,
43
+ const bracket: IndexParser.SignatureParser.BracketParser = lazy(() => union([
44
+ surround(str('('), recursion(Recursion.terminal, some(union([bracket, txt]), ')')), str(')'), true,
45
45
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
46
- surround(str('['), some(union([bracket, txt]), ']'), str(']'), true,
46
+ surround(str('['), recursion(Recursion.terminal, some(union([bracket, txt]), ']')), str(']'), true,
47
47
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
48
- surround(str('{'), some(union([bracket, txt]), '}'), str('}'), true,
48
+ surround(str('{'), recursion(Recursion.terminal, some(union([bracket, txt]), '}')), str('}'), true,
49
49
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
50
- surround(str('"'), precedence(2, some(txt, '"')), str('"'), true,
50
+ surround(str('"'), precedence(2, recursion(Recursion.terminal, some(txt, '"'))), str('"'), true,
51
51
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.index),
52
- ])));
52
+ ]));
53
53
 
54
54
  export function dataindex(ns: readonly (string | HTMLElement)[]): string | undefined {
55
55
  if (ns.length === 0) return;
@@ -11,10 +11,10 @@ import { html, defrag } from 'typed-dom/dom';
11
11
 
12
12
  // All syntax surrounded by square brackets shouldn't contain line breaks.
13
13
 
14
- export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => recursion(Recursion.inline, surround(
14
+ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => surround(
15
15
  str(/^\[[:^|]/),
16
- precedence(1,
17
- tightStart(some(union([inline]), ']', [[']', 1]]))),
16
+ precedence(1, recursion(Recursion.inline,
17
+ tightStart(some(union([inline]), ']', [[']', 1]])))),
18
18
  str(']'), false,
19
19
  ([, bs], rest) => [[
20
20
  html('span', {
@@ -24,4 +24,4 @@ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => recursi
24
24
  'data-invalid-message': `Invalid start symbol or linebreak`,
25
25
  }, defrag(bs)),
26
26
  ], rest],
27
- ([as, bs], rest) => [unshift(as, bs), rest], 3 | Backtrack.bracket)));
27
+ ([as, bs], rest) => [unshift(as, bs), rest], 3 | Backtrack.bracket));
@@ -18,7 +18,7 @@ const attrspecs = {
18
18
  Object.setPrototypeOf(attrspecs, null);
19
19
  Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
20
20
 
21
- export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, recursion(Recursion.inline,
21
+ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i,
22
22
  union([
23
23
  focus(
24
24
  /^<wbr[^\S\n]*>/i,
@@ -36,11 +36,11 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, re
36
36
  ([, tag]) =>
37
37
  surround<HTMLParser.TagParser, string>(
38
38
  surround(str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
39
- precedence(3,
39
+ precedence(3, recursion(Recursion.inline,
40
40
  subsequence([
41
41
  focus(/^[^\S\n]*\n/, some(inline)),
42
42
  some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`), [[blankWith('\n', `</${tag}>`), 3]]), true)),
43
- ])),
43
+ ]))),
44
44
  str(`</${tag}>`), true,
45
45
  ([as, bs = [], cs], rest) =>
46
46
  [[elem(tag, as, bs, cs)], rest],
@@ -54,11 +54,11 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, re
54
54
  ([, tag]) =>
55
55
  surround<HTMLParser.TagParser, string>(surround(
56
56
  str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
57
- precedence(3,
57
+ precedence(3, recursion(Recursion.inline,
58
58
  subsequence([
59
59
  focus(/^[^\S\n]*\n/, some(inline)),
60
60
  some(inline, `</${tag}>`, [[`</${tag}>`, 3]]),
61
- ])),
61
+ ]))),
62
62
  str(`</${tag}>`), true,
63
63
  ([as, bs = [], cs], rest) =>
64
64
  [[elem(tag, as, bs, cs)], rest],
@@ -66,7 +66,7 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i, re
66
66
  [[elem(tag, as, bs, [])], rest]),
67
67
  ([, tag]) => tag,
68
68
  new Clock(10000))),
69
- ]))));
69
+ ])));
70
70
 
71
71
  export const attribute: HTMLParser.AttributeParser = union([
72
72
  str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="[^"\n]*")?(?=[^\S\n]|>)/i),
@@ -7,14 +7,15 @@ import { repeat } from '../util';
7
7
  import { push } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
- export const insertion: InsertionParser = lazy(() => recursion(Recursion.inline, validate('++',
10
+ export const insertion: InsertionParser = lazy(() => validate('++',
11
11
  precedence(0, repeat('++', surround(
12
12
  '',
13
+ recursion(Recursion.inline,
13
14
  some(union([
14
15
  some(inline, blankWith('\n', '++')),
15
16
  open('\n', some(insertion, '+'), true),
16
- ])),
17
+ ]))),
17
18
  '++', false,
18
19
  ([, bs], rest) => [bs, rest],
19
20
  ([, bs], rest) => [push(bs, [Command.Escape]), rest]),
20
- nodes => [html('ins', defrag(nodes))])))));
21
+ nodes => [html('ins', defrag(nodes))]))));
@@ -7,16 +7,18 @@ import { repeat } from '../util';
7
7
  import { push } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
+ // 可読性のため実際にはオブリーク体を指定する。
10
11
  // 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
11
12
  // ある程度の長さのある文に使うのが望ましい。
12
- export const italic: ItalicParser = lazy(() => recursion(Recursion.inline, validate('///',
13
+ export const italic: ItalicParser = lazy(() => validate('///',
13
14
  precedence(0, repeat('///', surround(
14
15
  '',
16
+ recursion(Recursion.inline,
15
17
  tightStart(some(union([
16
18
  some(inline, blankWith('///')),
17
19
  open(some(inline, '/'), italic),
18
- ]))),
20
+ ])))),
19
21
  '///', false,
20
22
  ([, bs], rest) => [bs, rest],
21
23
  ([, bs], rest) => [push(bs, [Command.Escape]), rest]),
22
- nodes => [html('i', defrag(nodes))])))));
24
+ nodes => [html('i', defrag(nodes))]))));
@@ -180,6 +180,7 @@ describe('Unit: parser/inline/link', () => {
180
180
  assert.deepStrictEqual(inspect(parser('[@a]{b}')), [['<a class="link" href="b">@a</a>'], '']);
181
181
  assert.deepStrictEqual(inspect(parser('[@a@b]{c}')), [['<a class="link" href="c">@a@b</a>'], '']);
182
182
  assert.deepStrictEqual(inspect(parser('[a@b]{c}')), [['<a class="link" href="c">a@b</a>'], '']);
183
+ assert.deepStrictEqual(inspect(parser('[==a==]{b}')), [['<a class="link" href="b">==a==</a>'], '']);
183
184
  assert.deepStrictEqual(inspect(parser('[*a*]{b}')), [['<a class="link" href="b"><em>a</em></a>'], '']);
184
185
  });
185
186
 
@@ -1,6 +1,6 @@
1
1
  import { MarkParser } from '../inline';
2
2
  import { State, Recursion, Command } from '../context';
3
- import { union, some, recursion, precedence, constraint, validate, surround, open, lazy } from '../../combinator';
3
+ import { union, some, recursion, precedence, state, constraint, validate, surround, open, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { identity, signature } from './extension/indexee';
6
6
  import { tightStart, blankWith } from '../visibility';
@@ -8,13 +8,14 @@ import { repeat } from '../util';
8
8
  import { push } from 'spica/array';
9
9
  import { html, define, defrag } from 'typed-dom/dom';
10
10
 
11
- export const mark: MarkParser = lazy(() => constraint(State.mark, false, recursion(Recursion.inline, validate('==',
12
- precedence(0, repeat('==', surround(
11
+ export const mark: MarkParser = lazy(() => constraint(State.linkers & ~State.mark, false, validate('==',
12
+ precedence(0, state(State.mark, repeat('==', surround(
13
13
  '',
14
+ recursion(Recursion.inline,
14
15
  tightStart(some(union([
15
16
  some(inline, blankWith('==')),
16
17
  open(some(inline, '='), mark),
17
- ]))),
18
+ ])))),
18
19
  '==', false,
19
20
  ([, bs], rest) => [bs, rest],
20
21
  ([, bs], rest) => [push(bs, [Command.Escape]), rest]),
@@ -32,11 +32,12 @@ export const math: MathParser = lazy(() => validate('$', rewrite(
32
32
  source)
33
33
  ], ''])));
34
34
 
35
- const bracket: MathParser.BracketParser = lazy(() => recursion(Recursion.terminal, surround(
35
+ const bracket: MathParser.BracketParser = lazy(() => surround(
36
36
  str('{'),
37
+ recursion(Recursion.terminal,
37
38
  some(union([
38
39
  bracket,
39
40
  some(escsource, /^[{}$\n]/),
40
- ])),
41
+ ]))),
41
42
  str('}'),
42
- true)));
43
+ true));
@@ -7,13 +7,14 @@ import { memoize } from 'spica/memoize';
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(() => recursion(Recursion.inline, match(
10
+ export const remark: RemarkParser = lazy(() => match(
11
11
  /^\[(%+)\s/,
12
12
  memoize(
13
13
  ([, fence]) =>
14
14
  surround(
15
15
  str(`[${fence}`),
16
- precedence(4, some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`), [[new RegExp(String.raw`^\s+${fence}\]`), 4]])),
16
+ precedence(4, recursion(Recursion.inline,
17
+ some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`), [[new RegExp(String.raw`^\s+${fence}\]`), 4]]))),
17
18
  close(some(text, '%'), str(`${fence}]`)), true,
18
19
  ([as, bs = [], cs], rest) => [[
19
20
  html('span', { class: 'remark' }, [
@@ -22,4 +23,4 @@ export const remark: RemarkParser = lazy(() => recursion(Recursion.inline, match
22
23
  ]),
23
24
  ], rest],
24
25
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
25
- ([, fence]) => fence.length, {}))));
26
+ ([, fence]) => fence.length, {})));
@@ -17,6 +17,8 @@ describe('Unit: parser/inline/ruby', () => {
17
17
  assert.deepStrictEqual(inspect(parser('[&Tab; a](b)')), undefined);
18
18
  assert.deepStrictEqual(inspect(parser('[&a;](b)')), [['<ruby class="invalid">&amp;a;<rp>(</rp><rt>b</rt><rp>)</rp></ruby>'], '']);
19
19
  assert.deepStrictEqual(inspect(parser('[a](&a;)')), [['<ruby class="invalid">a<rp>(</rp><rt>&amp;a;</rt><rp>)</rp></ruby>'], '']);
20
+ assert.deepStrictEqual(inspect(parser('[<wbr>](a)')), undefined);
21
+ assert.deepStrictEqual(inspect(parser('[a](<wbr>)')), undefined);
20
22
  assert.deepStrictEqual(inspect(parser('[a]()')), undefined);
21
23
  assert.deepStrictEqual(inspect(parser('[a]( )')), undefined);
22
24
  assert.deepStrictEqual(inspect(parser('[a\nb](c)')), undefined);
@@ -51,7 +53,6 @@ describe('Unit: parser/inline/ruby', () => {
51
53
  assert.deepStrictEqual(inspect(parser('[&copy;](&copy;)')), [['<ruby>©<rp>(</rp><rt>©</rt><rp>)</rp></ruby>'], '']);
52
54
  assert.deepStrictEqual(inspect(parser('[&amp;copy;](&amp;copy;)')), [['<ruby>&amp;copy;<rp>(</rp><rt>&amp;copy;</rt><rp>)</rp></ruby>'], '']);
53
55
  assert.deepStrictEqual(inspect(parser('[*A*](*a*)')), [['<ruby>*A*<rp>(</rp><rt>*a*</rt><rp>)</rp></ruby>'], '']);
54
- assert.deepStrictEqual(inspect(parser('[<wbr>](<wbr>)')), [['<ruby>&lt;wbr&gt;<rp>(</rp><rt>&lt;wbr&gt;</rt><rp>)</rp></ruby>'], '']);
55
56
  });
56
57
 
57
58
  });
@@ -1,7 +1,7 @@
1
1
  import { RubyParser } from '../inline';
2
2
  import { Backtrack, Command, CmdRegExp } from '../context';
3
3
  import { eval, exec } from '../../combinator/data/parser';
4
- import { sequence, surround, lazy, fmap, bind } from '../../combinator';
4
+ import { sequence, surround, dup, lazy, fmap } from '../../combinator';
5
5
  import { unsafehtmlentity } from './htmlentity';
6
6
  import { text as txt, str } from '../source';
7
7
  import { isTightNodeStart } from '../visibility';
@@ -10,15 +10,23 @@ import { html, defrag } from 'typed-dom/dom';
10
10
 
11
11
  export const ruby: RubyParser = lazy(() => fmap(
12
12
  sequence([
13
- bind(surround('[', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ']', false, undefined, undefined, 3 | Backtrack.ruby), ([source], rest, context) => {
14
- const ns = eval(text({ source, context }), [undefined])[0];
15
- ns && ns.at(-1) === '' && ns.pop();
16
- return ns && isTightNodeStart(ns) ? [[ns], rest] : undefined;
17
- }),
18
- bind(surround('(', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ')', false, undefined, undefined, 3 | Backtrack.ruby), ([source], rest, context) => {
19
- const ns = eval(text({ source, context }), [undefined])[0];
20
- return ns && [[ns], rest];
21
- }),
13
+ dup(surround(
14
+ '[', str(/^(?:\\[^\n]|[^\\[\](){}<>"\n])+/u), ']',
15
+ false,
16
+ ([, [source]], rest, context) => {
17
+ const ns = eval(text({ source, context }), [undefined])[0];
18
+ ns && ns.at(-1) === '' && ns.pop();
19
+ return ns && isTightNodeStart(ns) ? [ns, rest] : undefined;
20
+ },
21
+ undefined, 3 | Backtrack.ruby)),
22
+ dup(surround(
23
+ '(', str(/^(?:\\[^\n]|[^\\[\](){}<>"\n])+/u), ')',
24
+ false,
25
+ ([, [source]], rest, context) => {
26
+ const ns = eval(text({ source, context }), [undefined])[0];
27
+ return ns && [ns, rest];
28
+ },
29
+ undefined, 3 | Backtrack.ruby)),
22
30
  ]),
23
31
  ([texts, rubies]) => {
24
32
  switch (true) {
@@ -8,16 +8,16 @@ import { tightStart, blankWith } from '../visibility';
8
8
  import { unshift } from 'spica/array';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
11
- export const strong: StrongParser = lazy(() => recursion(Recursion.inline, surround(
11
+ export const strong: StrongParser = lazy(() => surround(
12
12
  str('**', '*'),
13
- precedence(0,
13
+ precedence(0, recursion(Recursion.inline,
14
14
  tightStart(some(union([
15
15
  some(inline, blankWith('**')),
16
16
  open(some(inline, '*'), union([
17
17
  emstrong,
18
18
  strong,
19
19
  ])),
20
- ])))),
20
+ ]))))),
21
21
  str('**'), false,
22
22
  ([, bs], rest) => [[html('strong', defrag(bs))], rest],
23
- ([as, bs], rest) => [unshift(as, bs), rest])));
23
+ ([as, bs], rest) => [unshift(as, bs), rest]));
@@ -12,13 +12,13 @@ export const template: TemplateParser = lazy(() => surround(
12
12
  ([, ns = []], rest) => [[html('span', { class: 'template' }, `{{${ns.join('')}}}`)], rest],
13
13
  undefined, 3 | Backtrack.template));
14
14
 
15
- const bracket: TemplateParser.BracketParser = lazy(() => recursion(Recursion.terminal, union([
16
- surround(str('('), some(union([bracket, escsource]), ')'), str(')'), true,
15
+ const bracket: TemplateParser.BracketParser = lazy(() => union([
16
+ surround(str('('), recursion(Recursion.terminal, some(union([bracket, escsource]), ')')), str(')'), true,
17
17
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
18
- surround(str('['), some(union([bracket, escsource]), ']'), str(']'), true,
18
+ surround(str('['), recursion(Recursion.terminal, some(union([bracket, escsource]), ']')), str(']'), true,
19
19
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
20
- surround(str('{'), some(union([bracket, escsource]), '}'), str('}'), true,
20
+ surround(str('{'), recursion(Recursion.terminal, some(union([bracket, escsource]), '}')), str('}'), true,
21
21
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
22
- surround(str('"'), precedence(2, some(escsource, /^["\n]/)), str('"'), true,
22
+ surround(str('"'), precedence(2, recursion(Recursion.terminal, some(escsource, /^["\n]/))), str('"'), true,
23
23
  undefined, () => [[Command.Escape], ''], 3 | Backtrack.template),
24
- ])));
24
+ ]));
@@ -204,9 +204,10 @@ describe('Unit: parser/inline', () => {
204
204
  assert.deepStrictEqual(inspect(parser('#a')), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
205
205
  assert.deepStrictEqual(inspect(parser('#a\nb\n#c\n[#d]')), [['<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>'], '']);
206
206
  assert.deepStrictEqual(inspect(parser('##a')), [['##a'], '']);
207
- assert.deepStrictEqual(inspect(parser('_#a')), [['_#a'], '']);
208
- assert.deepStrictEqual(inspect(parser('_#a_')), [['_#a', '_'], '']);
209
- assert.deepStrictEqual(inspect(parser('_#a_b')), [['_#a_b'], '']);
207
+ assert.deepStrictEqual(inspect(parser('_#a')), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
208
+ assert.deepStrictEqual(inspect(parser('_#a_')), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>', '_'], '']);
209
+ assert.deepStrictEqual(inspect(parser('_#a_b')), [['_', '<a class="hashtag" href="/hashtags/a_b">#a_b</a>'], '']);
210
+ assert.deepStrictEqual(inspect(parser('_#a_b_')), [['_', '<a class="hashtag" href="/hashtags/a_b">#a_b</a>', '_'], '']);
210
211
  assert.deepStrictEqual(inspect(parser('a#b')), [['a#b'], '']);
211
212
  assert.deepStrictEqual(inspect(parser('0a#b')), [['0a#b'], '']);
212
213
  assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
@@ -216,8 +217,6 @@ describe('Unit: parser/inline', () => {
216
217
  assert.deepStrictEqual(inspect(parser('「#あ」')), [['「', '<a class="hashtag" href="/hashtags/あ">#あ</a>', '」'], '']);
217
218
  assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
218
219
  assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
219
- assert.deepStrictEqual(inspect(parser('_a_#b')), [['_', 'a_#b'], '']);
220
- assert.deepStrictEqual(inspect(parser('*a*#b')), [['<em>a</em>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
221
220
  assert.deepStrictEqual(inspect(parser('((a))#b')), [['<sup class="annotation"><span>a</span></sup>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
222
221
  assert.deepStrictEqual(inspect(parser('[[a]]#b')), [['<sup class="reference"><span>a</span></sup>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
223
222
  assert.deepStrictEqual(inspect(parser('[#a')), [['[', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
@@ -227,10 +226,12 @@ describe('Unit: parser/inline', () => {
227
226
 
228
227
  it('hashnum', () => {
229
228
  assert.deepStrictEqual(inspect(parser('#1')), [['<a class="hashnum">#1</a>'], '']);
229
+ assert.deepStrictEqual(inspect(parser(`#1'`)), [[`<a class="hashnum">#1</a>`, `'`], '']);
230
230
  assert.deepStrictEqual(inspect(parser('#1234567890@a')), [['#1234567890@a'], '']);
231
- assert.deepStrictEqual(inspect(parser('_#1')), [['_#1'], '']);
232
- assert.deepStrictEqual(inspect(parser('_#1_')), [['_#1_'], '']);
233
- assert.deepStrictEqual(inspect(parser('_#1_0')), [['_#1_0'], '']);
231
+ assert.deepStrictEqual(inspect(parser('_#1')), [['_', '<a class="hashnum">#1</a>'], '']);
232
+ assert.deepStrictEqual(inspect(parser('_#1_')), [['_', '<a class="hashnum">#1</a>', '_'], '']);
233
+ assert.deepStrictEqual(inspect(parser('_#1_0')), [['_', '<a class="hashtag" href="/hashtags/1_0">#1_0</a>'], '']);
234
+ assert.deepStrictEqual(inspect(parser('_#1_0_')), [['_', '<a class="hashtag" href="/hashtags/1_0">#1_0</a>', '_'], '']);
234
235
  assert.deepStrictEqual(inspect(parser('「#1」')), [['「', '<a class="hashnum">#1</a>', '」'], '']);
235
236
  });
236
237
 
@@ -5,6 +5,9 @@ import { consume } from '../../combinator';
5
5
  export function str(pattern: string | RegExp, not?: string): StrParser;
6
6
  export function str(pattern: string | RegExp, not?: string): Parser<string, Context<StrParser>, []> {
7
7
  assert(pattern);
8
+ const count = typeof pattern === 'object'
9
+ ? /[^^\\*+][*+]/.test(pattern.source)
10
+ : false;
8
11
  return typeof pattern === 'string'
9
12
  ? ({ source }) => {
10
13
  if (source === '') return;
@@ -16,7 +19,7 @@ export function str(pattern: string | RegExp, not?: string): Parser<string, Cont
16
19
  : ({ source, context }) => {
17
20
  if (source === '') return;
18
21
  const m = source.match(pattern);
19
- m && consume(m[0].length, context);
22
+ count && m && consume(m[0].length, context);
20
23
  if (m && not && source.slice(m[0].length, m[0].length + not.length) === not) return;
21
24
  return m && m[0].length > 0
22
25
  ? [[m[0]], source.slice(m[0].length)]
@@ -4,9 +4,9 @@ import { union, consume, focus } from '../../combinator';
4
4
  import { str } from './str';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
- export const delimiter = /[\s\x00-\x7F()[]{}“”‘’「」『』]|\S[#>]/u;
7
+ export const delimiter = /[\s\x00-\x7F()[]{}“”‘’「」『』]|\S#|[0-9A-Za-z]>/u;
8
8
  export const nonWhitespace = /[\S\n]|$/u;
9
- export const nonAlphanumeric = /[^0-9A-Za-z]|\S[#>]|$/u;
9
+ export const nonAlphanumeric = /[^0-9A-Za-z]|\S#|[0-9A-Za-z]>|$/u;
10
10
  const repeat = str(/^(.)\1*/);
11
11
 
12
12
  export const text: TextParser = ({ source, context }) => {