securemark 0.286.1 → 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 +4 -0
- package/design.md +1 -1
- package/dist/index.js +36 -34
- package/package.json +1 -1
- package/src/parser/api/parse.test.ts +8 -6
- package/src/parser/inline/autolink/hashnum.test.ts +2 -1
- package/src/parser/inline/autolink/hashnum.ts +1 -1
- package/src/parser/inline/autolink/hashtag.test.ts +10 -11
- package/src/parser/inline/autolink/hashtag.ts +2 -2
- package/src/parser/inline/autolink/url.ts +6 -6
- package/src/parser/inline/autolink.ts +5 -5
- package/src/parser/inline/deletion.ts +4 -3
- package/src/parser/inline/emphasis.ts +4 -4
- package/src/parser/inline/emstrong.ts +4 -3
- package/src/parser/inline/extension/index.ts +6 -6
- package/src/parser/inline/extension/placeholder.ts +4 -4
- package/src/parser/inline/html.ts +6 -6
- package/src/parser/inline/insertion.ts +4 -3
- package/src/parser/inline/italic.ts +5 -3
- package/src/parser/inline/link.test.ts +1 -0
- package/src/parser/inline/mark.ts +5 -4
- package/src/parser/inline/math.ts +4 -3
- package/src/parser/inline/remark.ts +4 -3
- package/src/parser/inline/ruby.test.ts +2 -1
- package/src/parser/inline/ruby.ts +18 -10
- package/src/parser/inline/strong.ts +4 -4
- package/src/parser/inline/template.ts +6 -6
- package/src/parser/inline.test.ts +9 -8
- package/src/parser/source/str.ts +4 -1
- package/src/parser/source/text.ts +2 -2
package/CHANGELOG.md
CHANGED
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は線形時間で解析不能な文脈依存言語をおおよそ
|
|
291
|
+
Securemarkは線形時間で解析不能な文脈依存言語をおおよそ14nの最悪時間計算量に改善しさらに解析時間と解析範囲の局限により一定時間内で解析不能な入力の影響を局限することでこれらの問題を解決している。この解析方法はほとんどの自然な入力に対して1nに近い時間で効率的に動作し、最悪計算量で低速に動作させる入力に対してもこの開発効率と安全性優先の低速な実装においてはサーバーで多数のユーザーのリクエストに応じるには低速で脆弱性となる可能性があるがクライアントで単一のユーザーの操作に応じるには十分高速であるためクライアントで解析する限り解析の効率または速度が実用上問題となることはなく仕様が固まり実行効率優先の高速な実装に移れば速度面の懸念もないだろう。
|
|
292
292
|
|
|
293
293
|
### 最適化
|
|
294
294
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.286.
|
|
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.
|
|
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
|
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)(
|
|
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[
|
|
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[
|
|
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|'|_(?=[
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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) ? [
|
|
7159
|
-
}), (0, combinator_1.
|
|
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 && [
|
|
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')])), [])))];
|
|
@@ -8152,15 +8154,15 @@ const hashnum_1 = __webpack_require__(8684);
|
|
|
8152
8154
|
const anchor_1 = __webpack_require__(8535);
|
|
8153
8155
|
const source_1 = __webpack_require__(8745);
|
|
8154
8156
|
const util_1 = __webpack_require__(4992);
|
|
8155
|
-
exports.autolink = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/^(?:[@#>0-9a-z]|\S[
|
|
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,
|
|
8156
8158
|
// Escape unmatched email-like strings.
|
|
8157
|
-
(0, source_1.str)(/^[0-9a-z]+(?:[_.+-][0-9a-z]
|
|
8159
|
+
(0, source_1.str)(/^[0-9a-z]+(?:[_.+-][0-9a-z]+|@(?=@))*/i), channel_1.channel, account_1.account,
|
|
8158
8160
|
// Escape unmatched account-like strings.
|
|
8159
|
-
(0, source_1.str)(/^@+[0-9a-z]
|
|
8161
|
+
(0, source_1.str)(/^@+(?:[0-9a-z]+(?:[_.+-][0-9a-z]+)*)?/i),
|
|
8160
8162
|
// Escape invalid leading characters.
|
|
8161
|
-
(0, source_1.str)(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji
|
|
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,
|
|
8162
8164
|
// Escape unmatched hashtag-like strings.
|
|
8163
|
-
(0, source_1.str)(new RegExp(/^#+(?:[^\p{C}\p{S}\p{P}\s]|emoji
|
|
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')),
|
|
8164
8166
|
// Escape invalid leading characters.
|
|
8165
8167
|
(0, source_1.str)(/^[0-9a-z](?=>)/iu), anchor_1.anchor])), ns => ns.length === 1 ? ns : [(0, util_1.stringify)(ns)])]))));
|
|
8166
8168
|
|
|
@@ -8678,7 +8680,7 @@ const link_1 = __webpack_require__(3628);
|
|
|
8678
8680
|
const hashtag_1 = __webpack_require__(5764);
|
|
8679
8681
|
const source_1 = __webpack_require__(8745);
|
|
8680
8682
|
const dom_1 = __webpack_require__(394);
|
|
8681
|
-
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
|
|
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, {
|
|
8682
8684
|
class: 'hashnum',
|
|
8683
8685
|
href: null
|
|
8684
8686
|
})]))), ({
|
|
@@ -8879,11 +8881,11 @@ const source_1 = __webpack_require__(8745);
|
|
|
8879
8881
|
const memoize_1 = __webpack_require__(6925);
|
|
8880
8882
|
const array_1 = __webpack_require__(6876);
|
|
8881
8883
|
const dom_1 = __webpack_require__(394);
|
|
8882
|
-
exports.remark = (0, combinator_1.lazy)(() => (0, combinator_1.
|
|
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', {
|
|
8883
8885
|
class: 'remark'
|
|
8884
8886
|
}, [(0, dom_1.html)('input', {
|
|
8885
8887
|
type: 'checkbox'
|
|
8886
|
-
}), (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, {})));
|
|
8887
8889
|
|
|
8888
8890
|
/***/ },
|
|
8889
8891
|
|
package/package.json
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
355
|
-
[`<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(
|
|
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">${
|
|
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
|
|
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`)), [[
|
|
25
|
-
assert.deepStrictEqual(inspect(parser(`#'00`)), [[
|
|
26
|
-
assert.deepStrictEqual(inspect(parser('#_')), [['#
|
|
27
|
-
assert.deepStrictEqual(inspect(parser('#_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(`#
|
|
62
|
-
assert.deepStrictEqual(inspect(parser(`#
|
|
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'
|
|
67
|
-
assert.deepStrictEqual(inspect(parser(`#
|
|
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|'|_(?=[
|
|
18
|
-
/(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[
|
|
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(() =>
|
|
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[
|
|
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]
|
|
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]
|
|
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
|
|
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
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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,
|
|
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(() =>
|
|
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(() =>
|
|
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,
|
|
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(() =>
|
|
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(() =>
|
|
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,
|
|
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('[	 a](b)')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('[&a;](b)')), [['<ruby class="invalid">&a;<rp>(</rp><rt>b</rt><rp>)</rp></ruby>'], '']);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('[a](&a;)')), [['<ruby class="invalid">a<rp>(</rp><rt>&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('[©](©)')), [['<ruby>©<rp>(</rp><rt>©</rt><rp>)</rp></ruby>'], '']);
|
|
52
54
|
assert.deepStrictEqual(inspect(parser('[&copy;](&copy;)')), [['<ruby>&copy;<rp>(</rp><rt>&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><wbr><rp>(</rp><rt><wbr></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
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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(() =>
|
|
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(() =>
|
|
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')), [['_
|
|
208
|
-
assert.deepStrictEqual(inspect(parser('_#a_')), [['_
|
|
209
|
-
assert.deepStrictEqual(inspect(parser('_#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')), [['_
|
|
232
|
-
assert.deepStrictEqual(inspect(parser('_#1_')), [['_
|
|
233
|
-
assert.deepStrictEqual(inspect(parser('_#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
|
|
package/src/parser/source/str.ts
CHANGED
|
@@ -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[
|
|
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[
|
|
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 }) => {
|