securemark 0.299.0 → 0.299.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 +8 -0
- package/dist/index.js +145 -50
- package/markdown.d.ts +2 -2
- package/package.json +1 -1
- package/src/combinator/data/delimiter.ts +2 -0
- package/src/combinator/data/parser/some.ts +13 -5
- package/src/parser/api/header.ts +5 -1
- package/src/parser/api/normalize.ts +1 -2
- package/src/parser/api/parse.test.ts +9 -9
- package/src/parser/context.ts +1 -1
- package/src/parser/header.test.ts +5 -5
- package/src/parser/header.ts +2 -3
- package/src/parser/inline/annotation.ts +8 -3
- package/src/parser/inline/autolink/url.ts +3 -4
- package/src/parser/inline/math.test.ts +2 -2
- package/src/parser/inline/math.ts +3 -3
- package/src/parser/inline/ruby.ts +2 -3
- package/src/parser/inline.test.ts +1 -1
- package/src/parser/repeat.ts +4 -4
- package/src/parser/source/escapable.ts +28 -5
- package/src/parser/source/text.ts +8 -16
- package/src/parser/source/unescapable.test.ts +1 -1
- package/src/parser/source/unescapable.ts +86 -5
- package/src/parser/visibility.ts +10 -13
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.299.
|
|
1
|
+
/*! securemark v0.299.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"));
|
|
@@ -3745,16 +3745,21 @@ Object.defineProperty(exports, "__esModule", ({
|
|
|
3745
3745
|
}));
|
|
3746
3746
|
exports.some = void 0;
|
|
3747
3747
|
const delimiter_1 = __webpack_require__(385);
|
|
3748
|
-
function some(parser, delimiter, after, delimiters, limit =
|
|
3748
|
+
function some(parser, delimiter, after, delimiters, limit = 0) {
|
|
3749
3749
|
if (typeof delimiter === 'number') {
|
|
3750
3750
|
limit = delimiter;
|
|
3751
|
+
delimiters = undefined;
|
|
3751
3752
|
delimiter = undefined;
|
|
3752
3753
|
} else if (Array.isArray(delimiter)) {
|
|
3754
|
+
limit = after;
|
|
3753
3755
|
delimiters = delimiter;
|
|
3754
3756
|
delimiter = undefined;
|
|
3755
3757
|
} else if (after === undefined || Array.isArray(after)) {
|
|
3758
|
+
limit = delimiters;
|
|
3756
3759
|
delimiters = after;
|
|
3757
3760
|
after = undefined;
|
|
3761
|
+
} else {
|
|
3762
|
+
delimiters = delimiters;
|
|
3758
3763
|
}
|
|
3759
3764
|
const match = delimiter_1.Delimiters.tester(delimiter, after);
|
|
3760
3765
|
const delims = delimiters?.map(([delimiter, precedence]) => ({
|
|
@@ -3780,7 +3785,8 @@ function some(parser, delimiter, after, delimiters, limit = -1) {
|
|
|
3780
3785
|
if (result === undefined) break;
|
|
3781
3786
|
if (context.position === pos) break;
|
|
3782
3787
|
nodes = nodes?.import(result) ?? result;
|
|
3783
|
-
|
|
3788
|
+
// 次にパースに成功すれば確実に制限値を超えるので制限値ちょうどでも中止する
|
|
3789
|
+
if (limit > 0 && context.position - position >= limit) break;
|
|
3784
3790
|
}
|
|
3785
3791
|
delims && context.delimiters.pop(delims.length);
|
|
3786
3792
|
return context.position > position ? nodes : undefined;
|
|
@@ -4201,7 +4207,11 @@ function header(source) {
|
|
|
4201
4207
|
exports.header = header;
|
|
4202
4208
|
function headers(source) {
|
|
4203
4209
|
const [el] = parse(source);
|
|
4204
|
-
|
|
4210
|
+
const acc = [];
|
|
4211
|
+
for (let field = el?.firstChild?.firstChild; field = field?.nextSibling;) {
|
|
4212
|
+
acc.push(field.textContent);
|
|
4213
|
+
}
|
|
4214
|
+
return acc;
|
|
4205
4215
|
}
|
|
4206
4216
|
exports.headers = headers;
|
|
4207
4217
|
function parse(source) {
|
|
@@ -4224,7 +4234,7 @@ function parse(source) {
|
|
|
4224
4234
|
Object.defineProperty(exports, "__esModule", ({
|
|
4225
4235
|
value: true
|
|
4226
4236
|
}));
|
|
4227
|
-
exports.escape = exports.invisibleGraphHTMLEntityNames = exports.
|
|
4237
|
+
exports.escape = exports.invisibleGraphHTMLEntityNames = exports.invisibleBlankHTMLEntityNames = exports.normalize = void 0;
|
|
4228
4238
|
const dom_1 = __webpack_require__(394);
|
|
4229
4239
|
const UNICODE_REPLACEMENT_CHARACTER = '\uFFFD';
|
|
4230
4240
|
function normalize(source) {
|
|
@@ -4251,7 +4261,6 @@ const parser = (el => entity => {
|
|
|
4251
4261
|
return el.textContent;
|
|
4252
4262
|
})((0, dom_1.html)('span'));
|
|
4253
4263
|
exports.invisibleBlankHTMLEntityNames = invisibleHTMLEntityNames.filter(name => parser(`&${name};`).trimStart() === '');
|
|
4254
|
-
exports.invisibleBlankCharacters = exports.invisibleBlankHTMLEntityNames.map(name => parser(`&${name};`));
|
|
4255
4264
|
exports.invisibleGraphHTMLEntityNames = invisibleHTMLEntityNames.filter(name => parser(`&${name};`).trimStart() !== '');
|
|
4256
4265
|
const unreadableEscapeHTMLEntityNames = invisibleHTMLEntityNames.filter(name => !['Tab', 'NewLine', 'NonBreakingSpace', 'nbsp', 'zwj', 'zwnj'].includes(name));
|
|
4257
4266
|
const unreadableEscapeCharacters = unreadableEscapeHTMLEntityNames.map(name => parser(`&${name};`));
|
|
@@ -5828,7 +5837,7 @@ class Context extends parser_1.Context {
|
|
|
5828
5837
|
} = options;
|
|
5829
5838
|
this.resources ??= {
|
|
5830
5839
|
// バックトラックのせいで文字数制限を受けないようにする。
|
|
5831
|
-
clock: exports.MAX_SEGMENT_SIZE * (
|
|
5840
|
+
clock: exports.MAX_SEGMENT_SIZE * (5 + 1),
|
|
5832
5841
|
recursions: [5 || 0 /* Recursion.block */, 20 || 0 /* Recursion.blockquote */, 40 || 0 /* Recursion.listitem */, 20 || 0 /* Recursion.inline */, 20 || 0 /* Recursion.bracket */, 20 || 0 /* Recursion.terminal */]
|
|
5833
5842
|
};
|
|
5834
5843
|
this.segment = segment ?? 0 /* Segment.unknown */;
|
|
@@ -5882,7 +5891,7 @@ const combinator_1 = __webpack_require__(3484);
|
|
|
5882
5891
|
const source_1 = __webpack_require__(8745);
|
|
5883
5892
|
const util_1 = __webpack_require__(4992);
|
|
5884
5893
|
const dom_1 = __webpack_require__(394);
|
|
5885
|
-
exports.header = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/---+[^\S\r\n]*\r?\n(?=\S)/y, (0, combinator_1.inits)([(0, combinator_1.block)((0, combinator_1.union)([(0, combinator_1.validate)(context => context.header, (0, combinator_1.focus)(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,
|
|
5894
|
+
exports.header = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/---+[^\S\r\n]*\r?\n(?=\S)/y, (0, combinator_1.inits)([(0, combinator_1.block)((0, combinator_1.union)([(0, combinator_1.validate)(context => context.header, (0, combinator_1.focus)(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,32}\1[^\S\r\n]*(?:$|\r?\n)/yi, (0, combinator_1.convert)(source => source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n')), (0, combinator_1.fmap)((0, combinator_1.some)((0, combinator_1.union)([field])), ns => new parser_1.List([new parser_1.Node((0, dom_1.html)('aside', {
|
|
5886
5895
|
class: 'header'
|
|
5887
5896
|
}, [(0, dom_1.html)('details', {
|
|
5888
5897
|
open: ''
|
|
@@ -5904,7 +5913,7 @@ const field = (0, combinator_1.line)(({
|
|
|
5904
5913
|
}) => {
|
|
5905
5914
|
const name = source.slice(position, source.indexOf(':', position));
|
|
5906
5915
|
const value = source.slice(position + name.length + 1).trim();
|
|
5907
|
-
return new parser_1.List([new parser_1.Node((0, dom_1.html)('
|
|
5916
|
+
return new parser_1.List([new parser_1.Node((0, dom_1.html)('div', {
|
|
5908
5917
|
class: 'field',
|
|
5909
5918
|
'data-name': name.toLowerCase(),
|
|
5910
5919
|
'data-value': value
|
|
@@ -5912,7 +5921,7 @@ const field = (0, combinator_1.line)(({
|
|
|
5912
5921
|
class: 'field-name'
|
|
5913
5922
|
}, name), ': ', (0, dom_1.html)('span', {
|
|
5914
5923
|
class: 'field-value'
|
|
5915
|
-
}, value)
|
|
5924
|
+
}, value)]))]);
|
|
5916
5925
|
});
|
|
5917
5926
|
|
|
5918
5927
|
/***/ },
|
|
@@ -6360,10 +6369,9 @@ Object.defineProperty(exports, "__esModule", ({
|
|
|
6360
6369
|
exports.lineurl = exports.url = void 0;
|
|
6361
6370
|
const parser_1 = __webpack_require__(605);
|
|
6362
6371
|
const combinator_1 = __webpack_require__(3484);
|
|
6363
|
-
const inline_1 = __webpack_require__(7973);
|
|
6364
6372
|
const link_1 = __webpack_require__(3628);
|
|
6365
6373
|
const source_1 = __webpack_require__(8745);
|
|
6366
|
-
exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[
|
|
6374
|
+
exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[[0-9A-Za-z])/y, (0, combinator_1.precedence)(0, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.unescsource, /(?<![-+*=~^_,.;:!?]|\/{3})(?:[-+*=~^_,.;:!?]|\/{3,}(?!\/))*(?=[\\$"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y), (0, combinator_1.precedence)(1, bracket)]), [[/[^\x21-\x7E]|\$/y, 9]])), false, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, context => new parser_1.List([new parser_1.Node((0, link_1.parse)(new parser_1.List(), new parser_1.List([new parser_1.Node(context.source)]), context))]))), context => new parser_1.List([new parser_1.Node(context.source)])])));
|
|
6367
6375
|
exports.lineurl = (0, combinator_1.lazy)(() => (0, combinator_1.focus)(/(?<=^|[\r\n])!?https?:\/\/\S+(?=[^\S\r\n]*(?=$|\r?\n))/y, (0, combinator_1.tails)([(0, source_1.str)('!'), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, context => {
|
|
6368
6376
|
const {
|
|
6369
6377
|
source,
|
|
@@ -6371,7 +6379,7 @@ exports.lineurl = (0, combinator_1.lazy)(() => (0, combinator_1.focus)(/(?<=^|[\
|
|
|
6371
6379
|
} = context;
|
|
6372
6380
|
context.position -= source[0] === '!' ? 1 : 0;
|
|
6373
6381
|
return new parser_1.List([new parser_1.Node((0, link_1.parse)(new parser_1.List(), new parser_1.List([new parser_1.Node(source.slice(position))]), context))]);
|
|
6374
|
-
})), (
|
|
6382
|
+
})), context => new parser_1.List([new parser_1.Node(context.source)])])])));
|
|
6375
6383
|
const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ')')), (0, source_1.str)(')'), true, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ']')), (0, source_1.str)(']'), true, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), '}')), (0, source_1.str)('}'), true, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)(source_1.unescsource, '"'))), (0, source_1.str)('"'), true, [3 | 8 /* Backtrack.unescapable */])]));
|
|
6376
6384
|
|
|
6377
6385
|
/***/ },
|
|
@@ -7390,8 +7398,8 @@ const combinator_1 = __webpack_require__(3484);
|
|
|
7390
7398
|
const source_1 = __webpack_require__(8745);
|
|
7391
7399
|
const util_1 = __webpack_require__(4992);
|
|
7392
7400
|
const dom_1 = __webpack_require__(394);
|
|
7393
|
-
const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])
|
|
7394
|
-
exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.union)([(0, combinator_1.surround)(/\$(?={)/y, (0, combinator_1.precedence)(4, bracket), '$', false, [3 | 16 /* Backtrack.escapable */]), (0, combinator_1.surround)(/\$(?![\s{}])/y, (0, combinator_1.precedence)(2, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.escsource,
|
|
7401
|
+
const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])/i;
|
|
7402
|
+
exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.union)([(0, combinator_1.surround)(/\$(?={)/y, (0, combinator_1.precedence)(4, bracket), '$', false, [3 | 16 /* Backtrack.escapable */]), (0, combinator_1.surround)(/\$(?![\s{}])/y, (0, combinator_1.precedence)(2, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.escsource, /[`"{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y), (0, combinator_1.precedence)(4, bracket)]))), /(?<!\s)\$(?![-0-9A-Za-z])/y, false, [3 | 16 /* Backtrack.escapable */])]), ({
|
|
7395
7403
|
source,
|
|
7396
7404
|
caches: {
|
|
7397
7405
|
math: cache
|
|
@@ -7405,7 +7413,7 @@ exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combin
|
|
|
7405
7413
|
translate: 'no',
|
|
7406
7414
|
...(0, util_1.invalid)('math', 'content', `"${source.match(forbiddenCommand)[0]}" command is forbidden`)
|
|
7407
7415
|
}, source))])));
|
|
7408
|
-
const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.escsource, /[{}$\r\n]/y)]))), (0, source_1.str)('}'), true));
|
|
7416
|
+
const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.escsource, /[{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y)]))), (0, source_1.str)('}'), true));
|
|
7409
7417
|
|
|
7410
7418
|
/***/ },
|
|
7411
7419
|
|
|
@@ -7667,14 +7675,14 @@ const dom_1 = __webpack_require__(394);
|
|
|
7667
7675
|
exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.bind)((0, combinator_1.inits)([(0, combinator_1.dup)((0, combinator_1.surround)('[', text, ']', false, [1 | 4 /* Backtrack.common */, 3 | 32 /* Backtrack.ruby */], ([, ns]) => {
|
|
7668
7676
|
ns && ns.last?.value === '' && ns.pop();
|
|
7669
7677
|
return (0, visibility_1.isNonblankNodeStart)(ns) ? ns : undefined;
|
|
7670
|
-
})), (0, combinator_1.dup)((0, combinator_1.surround)('(', text, ')', false
|
|
7678
|
+
})), (0, combinator_1.dup)((0, combinator_1.surround)('(', text, ')', false))]), ([{
|
|
7671
7679
|
value: texts
|
|
7672
7680
|
}, {
|
|
7673
7681
|
value: rubies = undefined
|
|
7674
7682
|
} = {}], context) => {
|
|
7675
7683
|
if (rubies === undefined) {
|
|
7676
7684
|
const head = context.position - context.range;
|
|
7677
|
-
return void (0, combinator_1.setBacktrack)(context, 2 | 32 /* Backtrack.ruby */, head);
|
|
7685
|
+
return void (0, combinator_1.setBacktrack)(context, 2 | 64 /* Backtrack.link */ | 32 /* Backtrack.ruby */, head);
|
|
7678
7686
|
}
|
|
7679
7687
|
switch (true) {
|
|
7680
7688
|
case texts.length >= rubies.length:
|
|
@@ -8210,10 +8218,10 @@ function repeat(opener, after, closer, rs, parser, cons, termination = (nodes, c
|
|
|
8210
8218
|
follow = follow > 0 ? follow : countFollows(source, pos, closer, lead / opener.length | 0);
|
|
8211
8219
|
nodes = cons(nodes, context, lead, follow);
|
|
8212
8220
|
if (context.position > pos) {
|
|
8213
|
-
const advance =
|
|
8221
|
+
const advance = context.position - pos;
|
|
8214
8222
|
i -= advance;
|
|
8215
8223
|
follow -= advance;
|
|
8216
|
-
depth -= advance;
|
|
8224
|
+
depth -= advance / closer.length | 0;
|
|
8217
8225
|
}
|
|
8218
8226
|
continue;
|
|
8219
8227
|
}
|
|
@@ -8240,10 +8248,10 @@ function repeat(opener, after, closer, rs, parser, cons, termination = (nodes, c
|
|
|
8240
8248
|
nodes = cons(nodes, context, lead, follow);
|
|
8241
8249
|
state = true;
|
|
8242
8250
|
if (context.position > pos) {
|
|
8243
|
-
const advance =
|
|
8251
|
+
const advance = context.position - pos;
|
|
8244
8252
|
i -= advance;
|
|
8245
8253
|
follow -= advance;
|
|
8246
|
-
depth -= advance;
|
|
8254
|
+
depth -= advance / closer.length | 0;
|
|
8247
8255
|
}
|
|
8248
8256
|
continue;
|
|
8249
8257
|
}
|
|
@@ -8432,14 +8440,11 @@ Object.defineProperty(exports, "__esModule", ({
|
|
|
8432
8440
|
exports.escsource = void 0;
|
|
8433
8441
|
const parser_1 = __webpack_require__(605);
|
|
8434
8442
|
const combinator_1 = __webpack_require__(3484);
|
|
8435
|
-
const text_1 = __webpack_require__(5655);
|
|
8436
8443
|
const dom_1 = __webpack_require__(394);
|
|
8437
|
-
const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
|
|
8438
8444
|
const escsource = context => {
|
|
8439
8445
|
const {
|
|
8440
8446
|
source,
|
|
8441
|
-
position
|
|
8442
|
-
state
|
|
8447
|
+
position
|
|
8443
8448
|
} = context;
|
|
8444
8449
|
if (position === source.length) return;
|
|
8445
8450
|
const char = source[position];
|
|
@@ -8468,7 +8473,7 @@ const escsource = context => {
|
|
|
8468
8473
|
return new parser_1.List([new parser_1.Node((0, dom_1.html)('br'), 1 /* Flag.blank */)]);
|
|
8469
8474
|
default:
|
|
8470
8475
|
if (context.sequential) return new parser_1.List([new parser_1.Node(char)]);
|
|
8471
|
-
let i = (
|
|
8476
|
+
let i = seek(source, position);
|
|
8472
8477
|
i -= position;
|
|
8473
8478
|
(0, combinator_1.consume)(i - 1, context);
|
|
8474
8479
|
context.position += i - 1;
|
|
@@ -8476,6 +8481,30 @@ const escsource = context => {
|
|
|
8476
8481
|
}
|
|
8477
8482
|
};
|
|
8478
8483
|
exports.escsource = escsource;
|
|
8484
|
+
function seek(source, position) {
|
|
8485
|
+
for (let i = position + 1; i < source.length; ++i) {
|
|
8486
|
+
const char = source[i];
|
|
8487
|
+
switch (char) {
|
|
8488
|
+
case '\\':
|
|
8489
|
+
case '$':
|
|
8490
|
+
case '"':
|
|
8491
|
+
case '`':
|
|
8492
|
+
case ':':
|
|
8493
|
+
case '[':
|
|
8494
|
+
case ']':
|
|
8495
|
+
case '(':
|
|
8496
|
+
case ')':
|
|
8497
|
+
case '{':
|
|
8498
|
+
case '}':
|
|
8499
|
+
case '\r':
|
|
8500
|
+
case '\n':
|
|
8501
|
+
return i;
|
|
8502
|
+
default:
|
|
8503
|
+
continue;
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
return source.length;
|
|
8507
|
+
}
|
|
8479
8508
|
|
|
8480
8509
|
/***/ },
|
|
8481
8510
|
|
|
@@ -8605,7 +8634,7 @@ exports.strs = strs;
|
|
|
8605
8634
|
Object.defineProperty(exports, "__esModule", ({
|
|
8606
8635
|
value: true
|
|
8607
8636
|
}));
|
|
8608
|
-
exports.isAlphanumeric = exports.
|
|
8637
|
+
exports.isAlphanumeric = exports.backToEmailHead = exports.backToUrlHead = exports.canSkip = exports.txt = exports.text = exports.nonWhitespace = void 0;
|
|
8609
8638
|
const parser_1 = __webpack_require__(605);
|
|
8610
8639
|
const combinator_1 = __webpack_require__(3484);
|
|
8611
8640
|
const dom_1 = __webpack_require__(394);
|
|
@@ -8676,23 +8705,16 @@ function isWhitespace(char, linebreak) {
|
|
|
8676
8705
|
return false;
|
|
8677
8706
|
}
|
|
8678
8707
|
}
|
|
8679
|
-
function next(source, position, state
|
|
8680
|
-
let index;
|
|
8681
|
-
if (
|
|
8682
|
-
delimiter.lastIndex = position + 1;
|
|
8683
|
-
delimiter.test(source);
|
|
8684
|
-
index = delimiter.lastIndex || position;
|
|
8685
|
-
} else {
|
|
8686
|
-
index = seek(source, position, state);
|
|
8687
|
-
}
|
|
8688
|
-
if (index === position || index === source.length) return source.length;
|
|
8708
|
+
function next(source, position, state) {
|
|
8709
|
+
let index = seek(source, position, state);
|
|
8710
|
+
if (index === source.length) return source.length;
|
|
8689
8711
|
const char = source[index];
|
|
8690
8712
|
switch (char) {
|
|
8691
8713
|
case '%':
|
|
8692
|
-
index +=
|
|
8714
|
+
index += index - 1 > position ? -1 : 0;
|
|
8693
8715
|
break;
|
|
8694
8716
|
case '[':
|
|
8695
|
-
index +=
|
|
8717
|
+
index += index - 1 > position && source.startsWith(' [|', index - 1) ? -1 : 0;
|
|
8696
8718
|
break;
|
|
8697
8719
|
case ':':
|
|
8698
8720
|
index = source.startsWith('//', index + 1) ? backToUrlHead(source, position, index) : index;
|
|
@@ -8703,7 +8725,6 @@ function next(source, position, state, delimiter) {
|
|
|
8703
8725
|
}
|
|
8704
8726
|
return index;
|
|
8705
8727
|
}
|
|
8706
|
-
exports.next = next;
|
|
8707
8728
|
function backToUrlHead(source, position, index) {
|
|
8708
8729
|
const delim = index;
|
|
8709
8730
|
let state = false;
|
|
@@ -8725,6 +8746,7 @@ function backToUrlHead(source, position, index) {
|
|
|
8725
8746
|
}
|
|
8726
8747
|
return index === position || source[index] !== 'h' ? delim : index;
|
|
8727
8748
|
}
|
|
8749
|
+
exports.backToUrlHead = backToUrlHead;
|
|
8728
8750
|
function backToEmailHead(source, position, index) {
|
|
8729
8751
|
const delim = index;
|
|
8730
8752
|
let state = false;
|
|
@@ -8747,6 +8769,7 @@ function backToEmailHead(source, position, index) {
|
|
|
8747
8769
|
}
|
|
8748
8770
|
return index === position ? delim : index;
|
|
8749
8771
|
}
|
|
8772
|
+
exports.backToEmailHead = backToEmailHead;
|
|
8750
8773
|
function isAlphanumeric(char) {
|
|
8751
8774
|
if (char < '0' || '\x7F' < char) return false;
|
|
8752
8775
|
return '0' <= char && char <= '9' || 'A' <= char && char <= 'Z' || 'a' <= char && char <= 'z';
|
|
@@ -8842,12 +8865,11 @@ function seek(source, position, state) {
|
|
|
8842
8865
|
Object.defineProperty(exports, "__esModule", ({
|
|
8843
8866
|
value: true
|
|
8844
8867
|
}));
|
|
8845
|
-
exports.unescsource =
|
|
8868
|
+
exports.unescsource = void 0;
|
|
8846
8869
|
const parser_1 = __webpack_require__(605);
|
|
8847
8870
|
const combinator_1 = __webpack_require__(3484);
|
|
8848
8871
|
const text_1 = __webpack_require__(5655);
|
|
8849
8872
|
const dom_1 = __webpack_require__(394);
|
|
8850
|
-
exports.delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
|
|
8851
8873
|
const unescsource = context => {
|
|
8852
8874
|
const {
|
|
8853
8875
|
source,
|
|
@@ -8871,7 +8893,7 @@ const unescsource = context => {
|
|
|
8871
8893
|
default:
|
|
8872
8894
|
if (context.sequential) return new parser_1.List([new parser_1.Node(char)]);
|
|
8873
8895
|
text_1.nonWhitespace.lastIndex = position + 1;
|
|
8874
|
-
let i = (0, text_1.canSkip)(source, position) ? text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length :
|
|
8896
|
+
let i = (0, text_1.canSkip)(source, position) ? text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : next(source, position, state);
|
|
8875
8897
|
i -= position;
|
|
8876
8898
|
(0, combinator_1.consume)(i - 1, context);
|
|
8877
8899
|
context.position += i - 1;
|
|
@@ -8879,6 +8901,79 @@ const unescsource = context => {
|
|
|
8879
8901
|
}
|
|
8880
8902
|
};
|
|
8881
8903
|
exports.unescsource = unescsource;
|
|
8904
|
+
function next(source, position, state) {
|
|
8905
|
+
let index = seek(source, position, state);
|
|
8906
|
+
if (index === source.length) return source.length;
|
|
8907
|
+
const char = source[index];
|
|
8908
|
+
switch (char) {
|
|
8909
|
+
case ':':
|
|
8910
|
+
index = source.startsWith('//', index + 1) ? (0, text_1.backToUrlHead)(source, position, index) : index;
|
|
8911
|
+
break;
|
|
8912
|
+
case '@':
|
|
8913
|
+
index = ~state & 1 /* State.autolink */ ? (0, text_1.backToEmailHead)(source, position, index) : index;
|
|
8914
|
+
break;
|
|
8915
|
+
}
|
|
8916
|
+
return index;
|
|
8917
|
+
}
|
|
8918
|
+
function seek(source, position, state) {
|
|
8919
|
+
const cat = category(source[position]);
|
|
8920
|
+
for (let i = position + 1; i < source.length; ++i) {
|
|
8921
|
+
const char = source[i];
|
|
8922
|
+
switch (char) {
|
|
8923
|
+
case '\\':
|
|
8924
|
+
case '!':
|
|
8925
|
+
case '$':
|
|
8926
|
+
case '"':
|
|
8927
|
+
case '`':
|
|
8928
|
+
case '[':
|
|
8929
|
+
case ']':
|
|
8930
|
+
case '(':
|
|
8931
|
+
case ')':
|
|
8932
|
+
case '{':
|
|
8933
|
+
case '}':
|
|
8934
|
+
case '<':
|
|
8935
|
+
case '>':
|
|
8936
|
+
case '(':
|
|
8937
|
+
case ')':
|
|
8938
|
+
case '[':
|
|
8939
|
+
case ']':
|
|
8940
|
+
case '{':
|
|
8941
|
+
case '}':
|
|
8942
|
+
case '-':
|
|
8943
|
+
case '+':
|
|
8944
|
+
case '*':
|
|
8945
|
+
case '=':
|
|
8946
|
+
case '~':
|
|
8947
|
+
case '^':
|
|
8948
|
+
case '_':
|
|
8949
|
+
case ',':
|
|
8950
|
+
case '.':
|
|
8951
|
+
case ';':
|
|
8952
|
+
case ':':
|
|
8953
|
+
case '!':
|
|
8954
|
+
case '?':
|
|
8955
|
+
case '/':
|
|
8956
|
+
case '|':
|
|
8957
|
+
case '\r':
|
|
8958
|
+
case '\n':
|
|
8959
|
+
return i;
|
|
8960
|
+
case '@':
|
|
8961
|
+
case '#':
|
|
8962
|
+
if (~state & 1 /* State.autolink */) return i;
|
|
8963
|
+
continue;
|
|
8964
|
+
case ':':
|
|
8965
|
+
if (source[i + 1] === '/' && source[i + 2] === '/') return i;
|
|
8966
|
+
continue;
|
|
8967
|
+
default:
|
|
8968
|
+
if (cat && !category(char)) return i;
|
|
8969
|
+
continue;
|
|
8970
|
+
}
|
|
8971
|
+
}
|
|
8972
|
+
return source.length;
|
|
8973
|
+
}
|
|
8974
|
+
function category(char) {
|
|
8975
|
+
return '\x21' <= char && char <= '\x7E';
|
|
8976
|
+
}
|
|
8882
8977
|
|
|
8883
8978
|
/***/ },
|
|
8884
8979
|
|
|
@@ -8962,9 +9057,9 @@ const combinator_1 = __webpack_require__(3484);
|
|
|
8962
9057
|
const normalize_1 = __webpack_require__(4490);
|
|
8963
9058
|
var blank;
|
|
8964
9059
|
(function (blank) {
|
|
8965
|
-
blank.line = new RegExp(/((?:^|\n)[^\S\r\n]*(?=\S))((?:[^\S\r\n]|\\(?=$|\s)|&IBHN
|
|
8966
|
-
blank.start = new RegExp(/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN
|
|
8967
|
-
blank.unit = new RegExp(/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN
|
|
9060
|
+
blank.line = new RegExp(/((?:^|\n)[^\S\r\n]*(?=\S))((?:[^\S\r\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)+(?=$|\r?\n))/g.source.replace('IBHN', `(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')})`), 'g');
|
|
9061
|
+
blank.start = new RegExp(/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)+/y.source.replace('IBHN', `(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')})`), 'y');
|
|
9062
|
+
blank.unit = new RegExp(/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)/y.source.replace('IBHN', `(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')})`), 'y');
|
|
8968
9063
|
})(blank || (blank = {}));
|
|
8969
9064
|
function visualize(parser) {
|
|
8970
9065
|
return (0, combinator_1.convert)(source => source.replace(blank.line, `$1${"\u001B" /* Command.Escape */}$2`), parser);
|
|
@@ -8976,15 +9071,15 @@ function blankWith(starts, delimiter) {
|
|
|
8976
9071
|
return new RegExp([
|
|
8977
9072
|
// 空行除去
|
|
8978
9073
|
// 完全な空行はエスケープ済みなので再帰的バックトラックにはならない。
|
|
8979
|
-
String.raw`(?:${starts}(?:\\?\s|&(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')})
|
|
9074
|
+
String.raw`(?:${starts}(?:\\?\s|&(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)*)?`, typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source].join(''), 'y');
|
|
8980
9075
|
}
|
|
8981
9076
|
exports.blankWith = blankWith;
|
|
8982
9077
|
function beforeNonblankWith(delimiter) {
|
|
8983
|
-
return new RegExp([typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source, String.raw`(?!\\?\s|&(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')})
|
|
9078
|
+
return new RegExp([typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source, String.raw`(?!\\?\s|&(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)`].join(''), 'y');
|
|
8984
9079
|
}
|
|
8985
9080
|
exports.beforeNonblankWith = beforeNonblankWith;
|
|
8986
9081
|
function afterNonblankWith(delimiter) {
|
|
8987
|
-
return new RegExp([String.raw`(?<!\s|&(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')})
|
|
9082
|
+
return new RegExp([String.raw`(?<!\s|&(?:${normalize_1.invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)`, typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source].join(''), 'y');
|
|
8988
9083
|
}
|
|
8989
9084
|
function isNonblankFirstLine(nodes) {
|
|
8990
9085
|
if (nodes.length === 0) return true;
|
package/markdown.d.ts
CHANGED
|
@@ -1082,7 +1082,7 @@ export namespace MarkdownParser {
|
|
|
1082
1082
|
Inline<'url'>,
|
|
1083
1083
|
Parser<string | HTMLElement, Context, [
|
|
1084
1084
|
Parser<HTMLAnchorElement, Context, []>,
|
|
1085
|
-
|
|
1085
|
+
Parser<string, Context, []>,
|
|
1086
1086
|
]> {
|
|
1087
1087
|
}
|
|
1088
1088
|
export namespace UrlParser {
|
|
@@ -1092,7 +1092,7 @@ export namespace MarkdownParser {
|
|
|
1092
1092
|
SourceParser.StrParser,
|
|
1093
1093
|
Parser<string | HTMLElement, Context, [
|
|
1094
1094
|
Parser<HTMLAnchorElement, Context, []>,
|
|
1095
|
-
|
|
1095
|
+
Parser<string, Context, []>,
|
|
1096
1096
|
]>,
|
|
1097
1097
|
]> {
|
|
1098
1098
|
}
|
package/package.json
CHANGED
|
@@ -4,24 +4,31 @@ import { Delimiters } from '../delimiter';
|
|
|
4
4
|
type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
|
|
5
5
|
|
|
6
6
|
export function some<P extends Parser>(parser: P, limit?: number): P;
|
|
7
|
-
export function some<P extends Parser>(parser: P, delimiters?: readonly DelimiterOption[]): P;
|
|
8
|
-
export function some<P extends Parser>(parser: P, delimiter: string | RegExp, delimiters?: readonly DelimiterOption[]): P;
|
|
9
|
-
export function some<P extends Parser>(parser: P, delimiter: string | RegExp, after: string | RegExp, delimiters?: readonly DelimiterOption[]): P;
|
|
10
|
-
export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp | readonly DelimiterOption[], after?: string | RegExp | readonly DelimiterOption[], delimiters?: readonly DelimiterOption[], limit =
|
|
7
|
+
export function some<P extends Parser>(parser: P, delimiters?: readonly DelimiterOption[], limit?: number): P;
|
|
8
|
+
export function some<P extends Parser>(parser: P, delimiter: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
|
|
9
|
+
export function some<P extends Parser>(parser: P, delimiter: string | RegExp, after: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
|
|
10
|
+
export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp | readonly DelimiterOption[], after?: number | string | RegExp | readonly DelimiterOption[], delimiters?: number | readonly DelimiterOption[], limit = 0): Parser<N> {
|
|
11
11
|
if (typeof delimiter === 'number') {
|
|
12
12
|
limit = delimiter;
|
|
13
|
+
delimiters = undefined;
|
|
13
14
|
delimiter = undefined;
|
|
14
15
|
}
|
|
15
16
|
else if (Array.isArray(delimiter)) {
|
|
17
|
+
limit = after as number;
|
|
16
18
|
delimiters = delimiter;
|
|
17
19
|
delimiter = undefined;
|
|
18
20
|
}
|
|
19
21
|
else if (after === undefined || Array.isArray(after)) {
|
|
22
|
+
limit = delimiters as number;
|
|
20
23
|
delimiters = after;
|
|
21
24
|
after = undefined;
|
|
22
25
|
}
|
|
26
|
+
else {
|
|
27
|
+
delimiters = delimiters as readonly DelimiterOption[];
|
|
28
|
+
}
|
|
23
29
|
assert(parser);
|
|
24
30
|
assert(delimiter !== '');
|
|
31
|
+
assert(delimiters === undefined || Array.isArray(delimiters));
|
|
25
32
|
const match = Delimiters.tester(delimiter as string, after as string);
|
|
26
33
|
const delims = delimiters?.map(([delimiter, precedence]) => ({
|
|
27
34
|
signature: Delimiters.signature(delimiter),
|
|
@@ -43,7 +50,8 @@ export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp
|
|
|
43
50
|
if (result === undefined) break;
|
|
44
51
|
if (context.position === pos) break;
|
|
45
52
|
nodes = nodes?.import(result) ?? result;
|
|
46
|
-
|
|
53
|
+
// 次にパースに成功すれば確実に制限値を超えるので制限値ちょうどでも中止する
|
|
54
|
+
if (limit > 0 && context.position - position >= limit) break;
|
|
47
55
|
}
|
|
48
56
|
delims && context.delimiters.pop(delims.length);
|
|
49
57
|
assert(context.position >= position);
|
package/src/parser/api/header.ts
CHANGED
|
@@ -8,7 +8,11 @@ export function header(source: string): string {
|
|
|
8
8
|
|
|
9
9
|
export function headers(source: string): string[] {
|
|
10
10
|
const [el] = parse(source);
|
|
11
|
-
|
|
11
|
+
const acc = [];
|
|
12
|
+
for (let field = el?.firstChild?.firstChild; field = field?.nextSibling;) {
|
|
13
|
+
acc.push(field.textContent!);
|
|
14
|
+
}
|
|
15
|
+
return acc;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
function parse(source: string): [HTMLElement, number] | [] {
|
|
@@ -65,8 +65,7 @@ const parser = (el => (entity: string): string => {
|
|
|
65
65
|
})(html('span'));
|
|
66
66
|
export const invisibleBlankHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames
|
|
67
67
|
.filter(name => parser(`&${name};`).trimStart() === '');
|
|
68
|
-
|
|
69
|
-
.map(name => parser(`&${name};`));
|
|
68
|
+
assert(invisibleBlankHTMLEntityNames.every(name => /^\s$/.test(parser(`&${name};`))));
|
|
70
69
|
export const invisibleGraphHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames
|
|
71
70
|
.filter(name => parser(`&${name};`).trimStart() !== '');
|
|
72
71
|
const unreadableEscapeHTMLEntityNames = invisibleHTMLEntityNames.filter(name => ![
|
|
@@ -125,7 +125,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
125
125
|
'!{../../a}',
|
|
126
126
|
].join('\n\n'), { host: new URL(`${location.origin}/z`) }).children].map(el => el.outerHTML),
|
|
127
127
|
[
|
|
128
|
-
'<aside class="header"><details open=""><summary>Header</summary><
|
|
128
|
+
'<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://source/x/y"><span class="field-name">URL</span>: <span class="field-value">https://source/x/y</span></div></details></aside>',
|
|
129
129
|
'<p><a class="account" href="https://source/@a" target="_blank">@a</a></p>',
|
|
130
130
|
'<p><a class="account" href="https://domain/@a" target="_blank">@domain/a</a></p>',
|
|
131
131
|
'<p><a class="channel" href="https://source/@a?ch=b" target="_blank">@a#b</a></p>',
|
|
@@ -158,7 +158,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
158
158
|
'{./a}',
|
|
159
159
|
].join('\n\n'), { host: new URL(`${location.origin}/index.md`) }).children].map(el => el.outerHTML),
|
|
160
160
|
[
|
|
161
|
-
'<aside class="header"><details open=""><summary>Header</summary><
|
|
161
|
+
'<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://source/x/y"><span class="field-name">URL</span>: <span class="field-value">https://source/x/y</span></div></details></aside>',
|
|
162
162
|
'<p><a class="url" href="/a">^/a</a></p>',
|
|
163
163
|
'<p><a class="url" href="https://source/x/a" target="_blank">./a</a></p>',
|
|
164
164
|
]);
|
|
@@ -173,7 +173,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
173
173
|
'{./a}',
|
|
174
174
|
].join('\n\n'), { host: new URL(`${location.origin}/z`) }).children].map(el => el.outerHTML),
|
|
175
175
|
[
|
|
176
|
-
`<aside class="header"><details open=""><summary>Header</summary><
|
|
176
|
+
`<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="${location.origin}/x/y"><span class="field-name">URL</span>: <span class="field-value">${location.origin}/x/y</span></div></details></aside>`,
|
|
177
177
|
'<p><a class="url" href="/z/a">^/a</a></p>',
|
|
178
178
|
'<p><a class="url" href="/x/a">./a</a></p>',
|
|
179
179
|
]);
|
|
@@ -204,9 +204,9 @@ describe('Unit: parser/api/parse', () => {
|
|
|
204
204
|
'{#}',
|
|
205
205
|
].join('\n\n'), { host: new URL(`${location.origin}/z`) }).children].map(el => normalize(el.outerHTML)),
|
|
206
206
|
[
|
|
207
|
-
`<aside class="header"><details open=""><summary>Header</summary><
|
|
207
|
+
`<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://example/x"><span class="field-name">URL</span>: <span class="field-value">https://example/x</span></div></details></aside>`,
|
|
208
208
|
'<pre class="invalid" translate="no">---\nURL: https://example/y\n---\n</pre>',
|
|
209
|
-
'<aside class="example" data-type="markdown"><pre translate="no">---\nURL: https://example/y\n---\n\n{#}</pre><hr><section><aside class="header"><details open=""><summary>Header</summary><
|
|
209
|
+
'<aside class="example" data-type="markdown"><pre translate="no">---\nURL: https://example/y\n---\n\n{#}</pre><hr><section><aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://example/y"><span class="field-name">URL</span>: <span class="field-value">https://example/y</span></div></details></aside><p><a class="url" href="https://example/y#" target="_blank">#</a></p><h2>References</h2><ol class="references"></ol></section></aside>',
|
|
210
210
|
'<p><a class="url" href="https://example/x#" target="_blank">#</a></p>',
|
|
211
211
|
]);
|
|
212
212
|
});
|
|
@@ -380,28 +380,28 @@ describe('Unit: parser/api/parse', () => {
|
|
|
380
380
|
// 最悪計算量での実行速度はCommonMarkの公式JS実装の32nに対して1-4倍程度。
|
|
381
381
|
// 5n = reference + link + url/math + ruby + text
|
|
382
382
|
assert.deepStrictEqual(
|
|
383
|
-
[...parse(`((([[[[#$[${'.'.repeat(
|
|
383
|
+
[...parse(`((([[[[#$http://[${'.'.repeat(19992)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
384
384
|
.map(el => el.tagName),
|
|
385
385
|
['P']);
|
|
386
386
|
});
|
|
387
387
|
|
|
388
388
|
it('backtrack 1 error', () => {
|
|
389
389
|
assert.deepStrictEqual(
|
|
390
|
-
[...parse(`((([[[[#$[${'.'.repeat(
|
|
390
|
+
[...parse(`((([[[[#$http://[${'.'.repeat(19992 + 1)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
391
391
|
.map(el => el.tagName),
|
|
392
392
|
['H1', 'PRE']);
|
|
393
393
|
});
|
|
394
394
|
|
|
395
395
|
it('backtrack 2', () => {
|
|
396
396
|
assert.deepStrictEqual(
|
|
397
|
-
[...parse(`((([[[[#$[${'.'.repeat(
|
|
397
|
+
[...parse(`((([[[[#$http://[${'.'.repeat(33324)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
398
398
|
.map(el => el.tagName),
|
|
399
399
|
['P', 'OL']);
|
|
400
400
|
});
|
|
401
401
|
|
|
402
402
|
it('backtrack 2 error', () => {
|
|
403
403
|
assert.deepStrictEqual(
|
|
404
|
-
[...parse(`((([[[[#$[${'.'.repeat(
|
|
404
|
+
[...parse(`((([[[[#$http://[${'.'.repeat(33324 + 1)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
405
405
|
.map(el => el.tagName),
|
|
406
406
|
['H1', 'PRE']);
|
|
407
407
|
});
|
package/src/parser/context.ts
CHANGED
|
@@ -24,11 +24,11 @@ describe('Unit: parser/header', () => {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it('basic', () => {
|
|
27
|
-
assert.deepStrictEqual(inspect(parser, input('---\na: b\n---', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><
|
|
28
|
-
assert.deepStrictEqual(inspect(parser, input('---\na: b\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><
|
|
29
|
-
assert.deepStrictEqual(inspect(parser, input('---\na: b\nC: D e\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><
|
|
30
|
-
assert.deepStrictEqual(inspect(parser, input('---\r\na: b\r\nC: D e\r\n---\r\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><
|
|
31
|
-
assert.deepStrictEqual(inspect(parser, input('----\na: b\n----', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><
|
|
27
|
+
assert.deepStrictEqual(inspect(parser, input('---\na: b\n---', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div></details></aside>'], '']);
|
|
28
|
+
assert.deepStrictEqual(inspect(parser, input('---\na: b\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div></details></aside>'], '']);
|
|
29
|
+
assert.deepStrictEqual(inspect(parser, input('---\na: b\nC: D e\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div><div class="field" data-name="c" data-value="D e"><span class="field-name">C</span>: <span class="field-value">D e</span></div></details></aside>'], '']);
|
|
30
|
+
assert.deepStrictEqual(inspect(parser, input('---\r\na: b\r\nC: D e\r\n---\r\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div><div class="field" data-name="c" data-value="D e"><span class="field-name">C</span>: <span class="field-value">D e</span></div></details></aside>'], '']);
|
|
31
|
+
assert.deepStrictEqual(inspect(parser, input('----\na: b\n----', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div></details></aside>'], '']);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
});
|
package/src/parser/header.ts
CHANGED
|
@@ -11,7 +11,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
|
|
|
11
11
|
block(
|
|
12
12
|
union([
|
|
13
13
|
validate(context => context.header,
|
|
14
|
-
focus(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,
|
|
14
|
+
focus(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,32}\1[^\S\r\n]*(?:$|\r?\n)/yi,
|
|
15
15
|
convert(source =>
|
|
16
16
|
source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n')),
|
|
17
17
|
fmap(
|
|
@@ -42,11 +42,10 @@ const field: MarkdownParser.HeaderParser.FieldParser = line(({ source, position
|
|
|
42
42
|
const name = source.slice(position, source.indexOf(':', position));
|
|
43
43
|
const value = source.slice(position + name.length + 1).trim();
|
|
44
44
|
return new List([
|
|
45
|
-
new Node(html('
|
|
45
|
+
new Node(html('div', { class: 'field', 'data-name': name.toLowerCase(), 'data-value': value }, [
|
|
46
46
|
html('span', { class: 'field-name' }, name),
|
|
47
47
|
': ',
|
|
48
48
|
html('span', { class: 'field-value' }, value),
|
|
49
|
-
'\n',
|
|
50
49
|
])),
|
|
51
50
|
]);
|
|
52
51
|
});
|
|
@@ -41,17 +41,22 @@ export const annotation: AnnotationParser = lazy(() => constraint(State.annotati
|
|
|
41
41
|
new Node(html('span', { class: bracketname(context, 1, 1) }, defrag(unwrap(nodes))))
|
|
42
42
|
]);
|
|
43
43
|
}
|
|
44
|
-
recursion.add(
|
|
44
|
+
recursion.add(
|
|
45
|
+
MAX_DEPTH - (resources?.recursions[Recursion.bracket] ?? resources?.recursions.at(-1) ?? MAX_DEPTH));
|
|
45
46
|
context.position += 1;
|
|
46
47
|
return new List([
|
|
47
|
-
new Node(html('sup', { class: 'annotation' }, [
|
|
48
|
+
new Node(html('sup', { class: 'annotation' }, [
|
|
49
|
+
html('span', defrag(unwrap(trimBlankNodeEnd(nodes))))
|
|
50
|
+
]))
|
|
48
51
|
]);
|
|
49
52
|
},
|
|
50
53
|
(nodes, context, prefix, postfix) => {
|
|
51
54
|
assert(postfix === 0);
|
|
52
55
|
for (let i = 0; i < prefix; ++i) {
|
|
53
56
|
nodes.unshift(new Node('('));
|
|
54
|
-
nodes = new List([
|
|
57
|
+
nodes = new List([
|
|
58
|
+
new Node(html('span', { class: bracketname(context, 0, 0) }, defrag(unwrap(nodes))))
|
|
59
|
+
]);
|
|
55
60
|
context.range += 1;
|
|
56
61
|
}
|
|
57
62
|
return nodes;
|
|
@@ -2,13 +2,12 @@ import { AutolinkParser } from '../../inline';
|
|
|
2
2
|
import { State, Recursion, Backtrack } from '../../context';
|
|
3
3
|
import { List, Node } from '../../../combinator/data/parser';
|
|
4
4
|
import { union, tails, some, recursion, precedence, state, constraint, focus, rewrite, surround, open, lazy } from '../../../combinator';
|
|
5
|
-
import { inline } from '../../inline';
|
|
6
5
|
import { parse } from '../link';
|
|
7
6
|
import { unescsource, str } from '../../source';
|
|
8
7
|
|
|
9
8
|
export const url: AutolinkParser.UrlParser = lazy(() => rewrite(
|
|
10
9
|
open(
|
|
11
|
-
/(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[
|
|
10
|
+
/(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[[0-9A-Za-z])/y,
|
|
12
11
|
precedence(0, some(union([
|
|
13
12
|
some(unescsource, /(?<![-+*=~^_,.;:!?]|\/{3})(?:[-+*=~^_,.;:!?]|\/{3,}(?!\/))*(?=[\\$"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y),
|
|
14
13
|
precedence(1, bracket),
|
|
@@ -18,7 +17,7 @@ export const url: AutolinkParser.UrlParser = lazy(() => rewrite(
|
|
|
18
17
|
union([
|
|
19
18
|
constraint(State.autolink, state(State.autolink, context =>
|
|
20
19
|
new List([new Node(parse(new List(), new List([new Node(context.source)]), context))]))),
|
|
21
|
-
|
|
20
|
+
context => new List([new Node(context.source)]),
|
|
22
21
|
])));
|
|
23
22
|
|
|
24
23
|
export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => focus(
|
|
@@ -36,7 +35,7 @@ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => focus(
|
|
|
36
35
|
context))
|
|
37
36
|
]);
|
|
38
37
|
})),
|
|
39
|
-
|
|
38
|
+
context => new List([new Node(context.source)]),
|
|
40
39
|
]),
|
|
41
40
|
])));
|
|
42
41
|
|
|
@@ -93,8 +93,8 @@ describe('Unit: parser/inline/math', () => {
|
|
|
93
93
|
assert.deepStrictEqual(inspect(parser, input('$\\Begin$', new Context())), [['<span class="invalid" translate="no">$\\Begin$</span>'], '']);
|
|
94
94
|
assert.deepStrictEqual(inspect(parser, input('$\\begin{}$', new Context())), [['<span class="invalid" translate="no">$\\begin{}$</span>'], '']);
|
|
95
95
|
assert.deepStrictEqual(inspect(parser, input('${\\begin}$', new Context())), [['<span class="invalid" translate="no">${\\begin}$</span>'], '']);
|
|
96
|
-
assert.deepStrictEqual(inspect(parser, input('$http://host$', new Context())),
|
|
97
|
-
assert.deepStrictEqual(inspect(parser, input('${http://host}$', new Context())),
|
|
96
|
+
assert.deepStrictEqual(inspect(parser, input('$http://host$', new Context())), undefined);
|
|
97
|
+
assert.deepStrictEqual(inspect(parser, input('${http://host}$', new Context())), undefined);
|
|
98
98
|
assert.deepStrictEqual(inspect(parser, input(' ${a}$', new Context())), undefined);
|
|
99
99
|
});
|
|
100
100
|
|
|
@@ -6,7 +6,7 @@ import { escsource, str } from '../source';
|
|
|
6
6
|
import { invalid } from '../util';
|
|
7
7
|
import { html } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
|
-
const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])
|
|
9
|
+
const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])/i;
|
|
10
10
|
|
|
11
11
|
export const math: MathParser = lazy(() => rewrite(
|
|
12
12
|
union([
|
|
@@ -19,7 +19,7 @@ export const math: MathParser = lazy(() => rewrite(
|
|
|
19
19
|
surround(
|
|
20
20
|
/\$(?![\s{}])/y,
|
|
21
21
|
precedence(2, some(union([
|
|
22
|
-
some(escsource,
|
|
22
|
+
some(escsource, /[`"{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y),
|
|
23
23
|
precedence(4, bracket),
|
|
24
24
|
]))),
|
|
25
25
|
/(?<!\s)\$(?![-0-9A-Za-z])/y,
|
|
@@ -45,7 +45,7 @@ const bracket: MathParser.BracketParser = lazy(() => surround(
|
|
|
45
45
|
recursion(Recursion.terminal,
|
|
46
46
|
some(union([
|
|
47
47
|
bracket,
|
|
48
|
-
some(escsource, /[{}$\r\n]/y),
|
|
48
|
+
some(escsource, /[{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y),
|
|
49
49
|
]))),
|
|
50
50
|
str('}'),
|
|
51
51
|
true));
|
|
@@ -20,13 +20,12 @@ export const ruby: RubyParser = lazy(() => bind(
|
|
|
20
20
|
})),
|
|
21
21
|
dup(surround(
|
|
22
22
|
'(', text, ')',
|
|
23
|
-
false,
|
|
24
|
-
[3 | Backtrack.ruby])),
|
|
23
|
+
false)),
|
|
25
24
|
]),
|
|
26
25
|
([{ value: texts }, { value: rubies = undefined } = {}], context) => {
|
|
27
26
|
if (rubies === undefined) {
|
|
28
27
|
const head = context.position - context.range;
|
|
29
|
-
return void setBacktrack(context, 2 | Backtrack.ruby, head);
|
|
28
|
+
return void setBacktrack(context, 2 | Backtrack.link | Backtrack.ruby, head);
|
|
30
29
|
}
|
|
31
30
|
switch (true) {
|
|
32
31
|
case texts.length >= rubies.length:
|
|
@@ -167,7 +167,7 @@ describe('Unit: parser/inline', () => {
|
|
|
167
167
|
assert.deepStrictEqual(inspect(parser, input('"[% "*"* %]', new Context())), [['"', '<span class="remark"><input type="checkbox"><span>[% "*"* %]</span></span>'], '']);
|
|
168
168
|
assert.deepStrictEqual(inspect(parser, input('"{{""}}', new Context())), [['"', '{', '<a class="url" href="""">""</a>', '}'], '']);
|
|
169
169
|
assert.deepStrictEqual(inspect(parser, input('[#http://host/(<bdi>)]</bdi>', new Context())), [['<a class="index" href="#index::http://host/(<bdi>)">http://host/<span class="paren">(<span class="invalid"><bdi></span>)</span></a>', '</bdi', '>'], '']);
|
|
170
|
-
assert.deepStrictEqual(inspect(parser, input('[#@a/http://host/(<bdi>)]</bdi>', new Context())), [['<a class="index" href="#index::@a/http://host/(<bdi>)">@a/http://host
|
|
170
|
+
assert.deepStrictEqual(inspect(parser, input('[#@a/http://host/(<bdi>)]</bdi>', new Context())), [['<a class="index" href="#index::@a/http://host/(<bdi>)">@a/http://host/(<bdi>)</a>', '</bdi', '>'], '']);
|
|
171
171
|
assert.deepStrictEqual(inspect(parser, input('[#a|<bdi>]</bdi>', new Context())), [['<a class="index" href="#index::a|<bdi>">a|<span class="invalid"><bdi></span></a>', '</bdi', '>'], '']);
|
|
172
172
|
assert.deepStrictEqual(inspect(parser, input('[[#a|<bdi>]</bdi>', new Context())), [['[', '<a class="index" href="#index::a|<bdi>">a|<span class="invalid"><bdi></span></a>', '</bdi', '>'], '']);
|
|
173
173
|
assert.deepStrictEqual(inspect(parser, input('[*==*]{a}', new Context())), [['<a class="link" href="a">*==*</a>'], '']);
|
package/src/parser/repeat.ts
CHANGED
|
@@ -68,10 +68,10 @@ export function repeat<N extends HTMLElement | string>(
|
|
|
68
68
|
follow = follow > 0 ? follow : countFollows(source, pos, closer, lead / opener.length | 0);
|
|
69
69
|
nodes = cons(nodes, context, lead, follow);
|
|
70
70
|
if (context.position > pos) {
|
|
71
|
-
const advance =
|
|
71
|
+
const advance = context.position - pos;
|
|
72
72
|
i -= advance;
|
|
73
73
|
follow -= advance;
|
|
74
|
-
depth -= advance;
|
|
74
|
+
depth -= advance / closer.length | 0;
|
|
75
75
|
}
|
|
76
76
|
continue;
|
|
77
77
|
}
|
|
@@ -100,10 +100,10 @@ export function repeat<N extends HTMLElement | string>(
|
|
|
100
100
|
nodes = cons(nodes, context, lead, follow);
|
|
101
101
|
state = true;
|
|
102
102
|
if (context.position > pos) {
|
|
103
|
-
const advance =
|
|
103
|
+
const advance = context.position - pos;
|
|
104
104
|
i -= advance;
|
|
105
105
|
follow -= advance;
|
|
106
|
-
depth -= advance;
|
|
106
|
+
depth -= advance / closer.length | 0;
|
|
107
107
|
}
|
|
108
108
|
continue;
|
|
109
109
|
}
|
|
@@ -3,13 +3,10 @@ import { Command } from '../context';
|
|
|
3
3
|
import { Flag } from '../node';
|
|
4
4
|
import { List, Node } from '../../combinator/data/parser';
|
|
5
5
|
import { consume } from '../../combinator';
|
|
6
|
-
import { next } from './text';
|
|
7
6
|
import { html } from 'typed-dom/dom';
|
|
8
7
|
|
|
9
|
-
const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
|
|
10
|
-
|
|
11
8
|
export const escsource: EscapableSourceParser = context => {
|
|
12
|
-
const { source, position
|
|
9
|
+
const { source, position } = context;
|
|
13
10
|
if (position === source.length) return;
|
|
14
11
|
const char = source[position];
|
|
15
12
|
consume(1, context);
|
|
@@ -38,7 +35,7 @@ export const escsource: EscapableSourceParser = context => {
|
|
|
38
35
|
default:
|
|
39
36
|
assert(char !== '\n');
|
|
40
37
|
if (context.sequential) return new List([new Node(char)]);
|
|
41
|
-
let i =
|
|
38
|
+
let i = seek(source, position);
|
|
42
39
|
assert(i > position);
|
|
43
40
|
i -= position;
|
|
44
41
|
consume(i - 1, context);
|
|
@@ -46,3 +43,29 @@ export const escsource: EscapableSourceParser = context => {
|
|
|
46
43
|
return new List([new Node(source.slice(position, context.position))]);
|
|
47
44
|
}
|
|
48
45
|
};
|
|
46
|
+
|
|
47
|
+
function seek(source: string, position: number): number {
|
|
48
|
+
for (let i = position + 1; i < source.length; ++i) {
|
|
49
|
+
const char = source[i];
|
|
50
|
+
switch (char) {
|
|
51
|
+
case '\\':
|
|
52
|
+
case '$':
|
|
53
|
+
case '"':
|
|
54
|
+
case '`':
|
|
55
|
+
case ':':
|
|
56
|
+
case '[':
|
|
57
|
+
case ']':
|
|
58
|
+
case '(':
|
|
59
|
+
case ')':
|
|
60
|
+
case '{':
|
|
61
|
+
case '}':
|
|
62
|
+
case '\r':
|
|
63
|
+
case '\n':
|
|
64
|
+
return i;
|
|
65
|
+
default:
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
assert(false);
|
|
69
|
+
}
|
|
70
|
+
return source.length;
|
|
71
|
+
}
|
|
@@ -83,28 +83,20 @@ function isWhitespace(char: string, linebreak: boolean): boolean {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
let index
|
|
88
|
-
if (delimiter) {
|
|
89
|
-
delimiter.lastIndex = position + 1;
|
|
90
|
-
delimiter.test(source);
|
|
91
|
-
index = delimiter.lastIndex || position;
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
index = seek(source, position, state);
|
|
95
|
-
}
|
|
96
|
-
if (index === position || index === source.length) return source.length;
|
|
86
|
+
function next(source: string, position: number, state: number): number {
|
|
87
|
+
let index= seek(source, position, state);
|
|
97
88
|
assert(index > position);
|
|
89
|
+
if (index === source.length) return source.length;
|
|
98
90
|
const char = source[index];
|
|
99
91
|
switch (char) {
|
|
100
92
|
case '%':
|
|
101
|
-
assert(source.startsWith('%]', index) && isWhitespace(source[index - 1], true)
|
|
102
|
-
index +=
|
|
93
|
+
assert(source.startsWith('%]', index) && isWhitespace(source[index - 1], true));
|
|
94
|
+
index += index - 1 > position
|
|
103
95
|
? -1
|
|
104
96
|
: 0;
|
|
105
97
|
break;
|
|
106
98
|
case '[':
|
|
107
|
-
index +=
|
|
99
|
+
index += index - 1 > position && source.startsWith(' [|', index - 1)
|
|
108
100
|
? -1
|
|
109
101
|
: 0;
|
|
110
102
|
break;
|
|
@@ -122,7 +114,7 @@ export function next(source: string, position: number, state: number, delimiter?
|
|
|
122
114
|
assert(index > position);
|
|
123
115
|
return index;
|
|
124
116
|
}
|
|
125
|
-
function backToUrlHead(source: string, position: number, index: number): number {
|
|
117
|
+
export function backToUrlHead(source: string, position: number, index: number): number {
|
|
126
118
|
const delim = index;
|
|
127
119
|
let state = false;
|
|
128
120
|
for (let i = index - 1; i >= position; --i) {
|
|
@@ -145,7 +137,7 @@ function backToUrlHead(source: string, position: number, index: number): number
|
|
|
145
137
|
? delim
|
|
146
138
|
: index;
|
|
147
139
|
}
|
|
148
|
-
function backToEmailHead(source: string, position: number, index: number): number {
|
|
140
|
+
export function backToEmailHead(source: string, position: number, index: number): number {
|
|
149
141
|
const delim = index;
|
|
150
142
|
let state = false;
|
|
151
143
|
for (let i = index - 1; i >= position; --i) {
|
|
@@ -15,7 +15,7 @@ describe('Unit: parser/source/unescapable', () => {
|
|
|
15
15
|
it('basic', () => {
|
|
16
16
|
assert.deepStrictEqual(inspect(parser, input('a', new Context())), [['a'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser, input('ab', new Context())), [['ab'], '']);
|
|
18
|
-
assert.deepStrictEqual(inspect(parser, input('a b c', new Context())), [['a', ' b
|
|
18
|
+
assert.deepStrictEqual(inspect(parser, input('a b c', new Context())), [['a', ' b c'], '']);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser, input('09あいAZaz', new Context())), [['09', 'あいAZaz'], '']);
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { UnescapableSourceParser } from '../source';
|
|
2
|
-
import { Command } from '../context';
|
|
2
|
+
import { State, Command } from '../context';
|
|
3
3
|
import { Flag } from '../node';
|
|
4
4
|
import { List, Node } from '../../combinator/data/parser';
|
|
5
5
|
import { consume } from '../../combinator';
|
|
6
|
-
import { nonWhitespace, canSkip,
|
|
6
|
+
import { nonWhitespace, canSkip, backToUrlHead, backToEmailHead } from './text';
|
|
7
7
|
import { html } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
|
-
export const delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
|
|
10
|
-
|
|
11
9
|
export const unescsource: UnescapableSourceParser = context => {
|
|
12
10
|
const { source, position, state } = context;
|
|
13
11
|
if (position === source.length) return;
|
|
@@ -32,7 +30,7 @@ export const unescsource: UnescapableSourceParser = context => {
|
|
|
32
30
|
? nonWhitespace.test(source)
|
|
33
31
|
? nonWhitespace.lastIndex - 1
|
|
34
32
|
: source.length
|
|
35
|
-
: next(source, position, state
|
|
33
|
+
: next(source, position, state);
|
|
36
34
|
assert(i > position);
|
|
37
35
|
i -= position;
|
|
38
36
|
consume(i - 1, context);
|
|
@@ -40,3 +38,86 @@ export const unescsource: UnescapableSourceParser = context => {
|
|
|
40
38
|
return new List([new Node(source.slice(position, context.position))]);
|
|
41
39
|
}
|
|
42
40
|
};
|
|
41
|
+
|
|
42
|
+
function next(source: string, position: number, state: number): number {
|
|
43
|
+
let index= seek(source, position, state);
|
|
44
|
+
assert(index > position);
|
|
45
|
+
if (index === source.length) return source.length;
|
|
46
|
+
const char = source[index];
|
|
47
|
+
switch (char) {
|
|
48
|
+
case ':':
|
|
49
|
+
index = source.startsWith('//', index + 1)
|
|
50
|
+
? backToUrlHead(source, position, index)
|
|
51
|
+
: index;
|
|
52
|
+
break;
|
|
53
|
+
case '@':
|
|
54
|
+
index = ~state & State.autolink
|
|
55
|
+
? backToEmailHead(source, position, index)
|
|
56
|
+
: index;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
assert(index > position);
|
|
60
|
+
return index;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function seek(source: string, position: number, state: number): number {
|
|
64
|
+
const cat = category(source[position]);
|
|
65
|
+
for (let i = position + 1; i < source.length; ++i) {
|
|
66
|
+
const char = source[i];
|
|
67
|
+
switch (char) {
|
|
68
|
+
case '\\':
|
|
69
|
+
case '!':
|
|
70
|
+
case '$':
|
|
71
|
+
case '"':
|
|
72
|
+
case '`':
|
|
73
|
+
case '[':
|
|
74
|
+
case ']':
|
|
75
|
+
case '(':
|
|
76
|
+
case ')':
|
|
77
|
+
case '{':
|
|
78
|
+
case '}':
|
|
79
|
+
case '<':
|
|
80
|
+
case '>':
|
|
81
|
+
case '(':
|
|
82
|
+
case ')':
|
|
83
|
+
case '[':
|
|
84
|
+
case ']':
|
|
85
|
+
case '{':
|
|
86
|
+
case '}':
|
|
87
|
+
case '-':
|
|
88
|
+
case '+':
|
|
89
|
+
case '*':
|
|
90
|
+
case '=':
|
|
91
|
+
case '~':
|
|
92
|
+
case '^':
|
|
93
|
+
case '_':
|
|
94
|
+
case ',':
|
|
95
|
+
case '.':
|
|
96
|
+
case ';':
|
|
97
|
+
case ':':
|
|
98
|
+
case '!':
|
|
99
|
+
case '?':
|
|
100
|
+
case '/':
|
|
101
|
+
case '|':
|
|
102
|
+
case '\r':
|
|
103
|
+
case '\n':
|
|
104
|
+
return i;
|
|
105
|
+
case '@':
|
|
106
|
+
case '#':
|
|
107
|
+
if (~state & State.autolink) return i;
|
|
108
|
+
continue;
|
|
109
|
+
case ':':
|
|
110
|
+
if (source[i + 1] === '/' && source[i + 2] === '/') return i;
|
|
111
|
+
continue;
|
|
112
|
+
default:
|
|
113
|
+
if (cat && !category(char)) return i;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
assert(false);
|
|
117
|
+
}
|
|
118
|
+
return source.length;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function category(char: string): boolean {
|
|
122
|
+
return '\x21' <= char && char <= '\x7E';
|
|
123
|
+
}
|
package/src/parser/visibility.ts
CHANGED
|
@@ -2,23 +2,20 @@ import { Parser, List, Node } from '../combinator/data/parser';
|
|
|
2
2
|
import { Command } from './context';
|
|
3
3
|
import { Flag } from './node';
|
|
4
4
|
import { convert, fmap } from '../combinator';
|
|
5
|
-
import { invisibleBlankHTMLEntityNames
|
|
5
|
+
import { invisibleBlankHTMLEntityNames } from './api/normalize';
|
|
6
6
|
|
|
7
7
|
namespace blank {
|
|
8
8
|
export const line = new RegExp(
|
|
9
|
-
/((?:^|\n)[^\S\r\n]*(?=\S))((?:[^\S\r\n]|\\(?=$|\s)|&IBHN
|
|
10
|
-
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`)
|
|
11
|
-
.replace('IBC', `${invisibleBlankCharacters.join('')}`),
|
|
9
|
+
/((?:^|\n)[^\S\r\n]*(?=\S))((?:[^\S\r\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)+(?=$|\r?\n))/g.source
|
|
10
|
+
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`),
|
|
12
11
|
'g');
|
|
13
12
|
export const start = new RegExp(
|
|
14
|
-
/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN
|
|
15
|
-
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`)
|
|
16
|
-
.replace('IBC', `${invisibleBlankCharacters.join('')}`),
|
|
13
|
+
/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)+/y.source
|
|
14
|
+
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`),
|
|
17
15
|
'y');
|
|
18
16
|
export const unit = new RegExp(
|
|
19
|
-
/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN
|
|
20
|
-
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`)
|
|
21
|
-
.replace('IBC', `${invisibleBlankCharacters.join('')}`),
|
|
17
|
+
/(?:[^\S\r\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)/y.source
|
|
18
|
+
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`),
|
|
22
19
|
'y');
|
|
23
20
|
}
|
|
24
21
|
|
|
@@ -34,7 +31,7 @@ export function blankWith(starts: '\n', delimiter: string | RegExp): RegExp {
|
|
|
34
31
|
return new RegExp([
|
|
35
32
|
// 空行除去
|
|
36
33
|
// 完全な空行はエスケープ済みなので再帰的バックトラックにはならない。
|
|
37
|
-
String.raw`(?:${starts}(?:\\?\s|&(?:${invisibleBlankHTMLEntityNames.join('|')})
|
|
34
|
+
String.raw`(?:${starts}(?:\\?\s|&(?:${invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)*)?`,
|
|
38
35
|
typeof delimiter === 'string'
|
|
39
36
|
? delimiter.replace(/[*+()\[\]]/g, '\\$&')
|
|
40
37
|
: delimiter.source,
|
|
@@ -45,12 +42,12 @@ export function beforeNonblankWith(delimiter: string | RegExp): RegExp {
|
|
|
45
42
|
typeof delimiter === 'string'
|
|
46
43
|
? delimiter.replace(/[*+()\[\]]/g, '\\$&')
|
|
47
44
|
: delimiter.source,
|
|
48
|
-
String.raw`(?!\\?\s|&(?:${invisibleBlankHTMLEntityNames.join('|')})
|
|
45
|
+
String.raw`(?!\\?\s|&(?:${invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)`,
|
|
49
46
|
].join(''), 'y');
|
|
50
47
|
}
|
|
51
48
|
function afterNonblankWith(delimiter: string | RegExp): RegExp {
|
|
52
49
|
return new RegExp([
|
|
53
|
-
String.raw`(?<!\s|&(?:${invisibleBlankHTMLEntityNames.join('|')})
|
|
50
|
+
String.raw`(?<!\s|&(?:${invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)`,
|
|
54
51
|
typeof delimiter === 'string'
|
|
55
52
|
? delimiter.replace(/[*+()\[\]]/g, '\\$&')
|
|
56
53
|
: delimiter.source,
|