rapier-html 1.2.51 → 1.2.55

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.
@@ -2,6 +2,57 @@
2
2
 
3
3
  const NEWLINES = /\r\n?/g, NULLS = /\0/g;
4
4
 
5
+ const BLOCK_STARTER = new Uint8Array(128);
6
+ for (const c of [35, 36, 42, 43, 45, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 62, 91, 92, 95, 96, 126]) BLOCK_STARTER[c] = 1;
7
+
8
+ const INLINE_TERM = new Uint8Array(128);
9
+ for (const c of [10, 33, 36, 38, 42, 43, 58, 60, 61, 91, 92, 93, 94, 95, 96, 126]) INLINE_TERM[c] = 1;
10
+
11
+ const LABEL_SPECIAL = new Uint8Array(128);
12
+ for (const c of [10, 33, 36, 60, 91, 92, 93, 94, 96]) LABEL_SPECIAL[c] = 1;
13
+
14
+ const ASCII_PUNCT = new Uint8Array(128);
15
+ for (const c of [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126]) ASCII_PUNCT[c] = 1;
16
+
17
+ const RULE_STARTERS = {
18
+ text: null,
19
+ linkify: [58],
20
+ newline: [10],
21
+ rapier_semantic_probe: [33, 91],
22
+ temml_math_block: [36, 92],
23
+ escape: [92],
24
+ backticks: [96],
25
+ strikethrough: [126],
26
+ mark: [61],
27
+ ins: [43],
28
+ emphasis: [42, 95],
29
+ sup: [94],
30
+ link: [91],
31
+ rapier_wiki: [33, 91],
32
+ image: [33],
33
+ footnote_inline: [94],
34
+ footnote_ref: [91],
35
+ autolink: [60],
36
+ html_inline: [60],
37
+ entity: [38]
38
+ };
39
+
40
+ function isAsciiTrimmable(c) {
41
+ return c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d;
42
+ }
43
+
44
+ function asciiTrim(str) {
45
+ const len = str.length;
46
+ if (len === 0) return '';
47
+ const first = str.charCodeAt(0), last = str.charCodeAt(len - 1);
48
+ if (!isAsciiTrimmable(first) && !isAsciiTrimmable(last)) return str;
49
+ let start = 0;
50
+ while (start < len && isAsciiTrimmable(str.charCodeAt(start))) start++;
51
+ let end = len - 1;
52
+ while (end >= start && isAsciiTrimmable(str.charCodeAt(end))) end--;
53
+ return str.slice(start, end + 1);
54
+ }
55
+
5
56
  function lineScanner(Base, Token) {
6
57
  function StateBlock(src, md, env, tokens) {
7
58
  this.bMarks = []; this.eMarks = []; this.tShift = []; this.sCount = []; this.bsCount = [];
@@ -19,7 +70,7 @@ function lineScanner(Base, Token) {
19
70
  indent++;
20
71
  }
21
72
  if (at === length) break;
22
- let end = src.indexOf('\n', at);
73
+ let end = src.charCodeAt(at) === 0x0a ? at : src.indexOf('\n', at + 1);
23
74
  if (end < 0) end = length;
24
75
  this.bMarks.push(start); this.eMarks.push(end); this.tShift.push(indent); this.sCount.push(offset); this.bsCount.push(0);
25
76
  start = end + 1;
@@ -28,16 +79,533 @@ function lineScanner(Base, Token) {
28
79
  this.lineMax = this.bMarks.length - 1;
29
80
  }
30
81
  StateBlock.prototype = Base.prototype;
82
+ Base.prototype.getLines = function getLines(begin, end, indent, keepLastLF) {
83
+ if (begin >= end) return '';
84
+ const src = this.src, bMarks = this.bMarks, eMarks = this.eMarks, tShift = this.tShift, bsCount = this.bsCount;
85
+ if (begin + 1 === end) {
86
+ const lineStart = bMarks[begin], last = keepLastLF ? eMarks[begin] + 1 : eMarks[begin];
87
+ if (indent === 0) return src.slice(lineStart, last);
88
+ const shift = tShift[begin];
89
+ if (shift === 0) return src.slice(lineStart, last);
90
+ let lineIndent = 0, first = lineStart;
91
+ const bs = bsCount[begin];
92
+ while (first < last && lineIndent < indent) {
93
+ const ch = src.charCodeAt(first);
94
+ if (ch === 0x20) lineIndent++;
95
+ else if (ch === 0x09) lineIndent += 4 - (lineIndent + bs) % 4;
96
+ else if (first - lineStart < shift) lineIndent++;
97
+ else break;
98
+ first++;
99
+ }
100
+ if (lineIndent > indent) return ' '.repeat(lineIndent - indent) + src.slice(first, last);
101
+ return src.slice(first, last);
102
+ }
103
+ const queue = new Array(end - begin);
104
+ for (let i = 0, line = begin; line < end; line++, i++) {
105
+ const lineStart = bMarks[line];
106
+ const last = line + 1 < end || keepLastLF ? eMarks[line] + 1 : eMarks[line];
107
+ if (indent === 0 || tShift[line] === 0 && bsCount[line] === 0 && src.charCodeAt(lineStart) !== 0x20 && src.charCodeAt(lineStart) !== 0x09) {
108
+ queue[i] = src.slice(lineStart, last);
109
+ continue;
110
+ }
111
+ let lineIndent = 0, first = lineStart;
112
+ const shift = tShift[line], bs = bsCount[line];
113
+ while (first < last && lineIndent < indent) {
114
+ const ch = src.charCodeAt(first);
115
+ if (ch === 0x20) lineIndent++;
116
+ else if (ch === 0x09) lineIndent += 4 - (lineIndent + bs) % 4;
117
+ else if (first - lineStart < shift) lineIndent++;
118
+ else break;
119
+ first++;
120
+ }
121
+ queue[i] = lineIndent > indent ? ' '.repeat(lineIndent - indent) + src.slice(first, last) : src.slice(first, last);
122
+ }
123
+ return queue.join('');
124
+ };
31
125
  return StateBlock;
32
126
  }
33
127
 
128
+ function lineCanTerminateParagraph(state, nextLine, endLine) {
129
+ const pos = state.bMarks[nextLine] + state.tShift[nextLine];
130
+ if (pos >= state.eMarks[nextLine]) return false;
131
+ const ch = state.src.charCodeAt(pos);
132
+ if (ch < 128 && BLOCK_STARTER[ch] === 1) return true;
133
+ const n2 = nextLine + 1;
134
+ if (n2 < endLine) {
135
+ const p2 = state.bMarks[n2] + state.tShift[n2];
136
+ if (p2 < state.eMarks[n2]) {
137
+ const c2 = state.src.charCodeAt(p2);
138
+ if (c2 === 0x7c || c2 === 0x2d || c2 === 0x3a) return true;
139
+ }
140
+ }
141
+ return false;
142
+ }
143
+
144
+ function lheading(state, startLine, endLine) {
145
+ const blkIndent = state.blkIndent;
146
+ if (state.sCount[startLine] - blkIndent >= 4) return false;
147
+ let nextLine = startLine + 1;
148
+ if (nextLine >= endLine || state.isEmpty(nextLine)) {
149
+ state._pLine = startLine;
150
+ state._pEnd = endLine;
151
+ state._pNext = nextLine;
152
+ return false;
153
+ }
154
+ const terminatorRules = state.md.block.ruler.getRules('paragraph'), len = terminatorRules.length;
155
+ const oldParentType = state.parentType;
156
+ state.parentType = 'paragraph';
157
+ const src = state.src, bMarks = state.bMarks, eMarks = state.eMarks, tShift = state.tShift, sCount = state.sCount;
158
+ let level = 0, marker = 0;
159
+ for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
160
+ const sc = sCount[nextLine];
161
+ if (sc - blkIndent > 3) continue;
162
+ if (sc >= blkIndent) {
163
+ let pos = bMarks[nextLine] + tShift[nextLine];
164
+ const max = eMarks[nextLine];
165
+ if (pos < max) {
166
+ marker = src.charCodeAt(pos);
167
+ if (marker === 0x2d || marker === 0x3d) {
168
+ pos = state.skipChars(pos, marker);
169
+ pos = state.skipSpaces(pos);
170
+ if (pos >= max) {
171
+ level = marker === 0x3d ? 1 : 2;
172
+ break;
173
+ }
174
+ }
175
+ }
176
+ }
177
+ if (sc < 0) continue;
178
+ if (!lineCanTerminateParagraph(state, nextLine, endLine)) continue;
179
+ let terminate = false;
180
+ for (let i = 0; i < len; i++) {
181
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
182
+ terminate = true;
183
+ break;
184
+ }
185
+ }
186
+ if (terminate) break;
187
+ }
188
+ if (!level) {
189
+ state.parentType = oldParentType;
190
+ state._pLine = startLine;
191
+ state._pEnd = endLine;
192
+ state._pNext = nextLine;
193
+ return false;
194
+ }
195
+ const content = asciiTrim(state.getLines(startLine, nextLine, blkIndent, false));
196
+ state.line = nextLine + 1;
197
+ const token_o = state.push('heading_open', `h${level}`, 1);
198
+ token_o.markup = marker === 0x3d ? '=' : '-';
199
+ token_o.map = [startLine, state.line];
200
+ const token_i = state.push('inline', '', 0);
201
+ token_i.content = content;
202
+ token_i.map = [startLine, state.line - 1];
203
+ token_i.children = [];
204
+ const token_c = state.push('heading_close', `h${level}`, -1);
205
+ token_c.markup = marker === 0x3d ? '=' : '-';
206
+ state.parentType = oldParentType;
207
+ return true;
208
+ }
209
+
210
+ function paragraph(state, startLine, endLine) {
211
+ let nextLine = startLine + 1;
212
+ if (state._pLine === startLine && state._pEnd === endLine) {
213
+ nextLine = state._pNext;
214
+ } else if (nextLine < endLine && !state.isEmpty(nextLine)) {
215
+ const terminatorRules = state.md.block.ruler.getRules('paragraph'), len = terminatorRules.length;
216
+ const oldParentType = state.parentType, blkIndent = state.blkIndent, sCount = state.sCount;
217
+ state.parentType = 'paragraph';
218
+ for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
219
+ const sc = sCount[nextLine];
220
+ if (sc - blkIndent > 3 || sc < 0) continue;
221
+ if (!lineCanTerminateParagraph(state, nextLine, endLine)) continue;
222
+ let terminate = false;
223
+ for (let i = 0; i < len; i++) {
224
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
225
+ terminate = true;
226
+ break;
227
+ }
228
+ }
229
+ if (terminate) break;
230
+ }
231
+ state.parentType = oldParentType;
232
+ }
233
+ const content = asciiTrim(state.getLines(startLine, nextLine, state.blkIndent, false));
234
+ state.line = nextLine;
235
+ const token_o = state.push('paragraph_open', 'p', 1);
236
+ token_o.map = [startLine, nextLine];
237
+ const token_i = state.push('inline', '', 0);
238
+ token_i.content = content;
239
+ token_i.map = [startLine, nextLine];
240
+ token_i.children = [];
241
+ state.push('paragraph_close', 'p', -1);
242
+ return true;
243
+ }
244
+
245
+ function textRule(state, silent) {
246
+ let pos = state.pos;
247
+ const src = state.src, max = state.posMax;
248
+ while (pos < max) {
249
+ const ch = src.charCodeAt(pos);
250
+ if (ch < 128 && INLINE_TERM[ch] === 1) break;
251
+ pos++;
252
+ }
253
+ if (pos === state.pos) return false;
254
+ if (!silent) state.pending += src.slice(state.pos, pos);
255
+ state.pos = pos;
256
+ return true;
257
+ }
258
+
259
+ function compileInlineDispatch(ruler) {
260
+ const rules = ruler.getRules('');
261
+ if (ruler._compiledRules === rules && ruler._dispatchTable) return ruler._dispatchTable;
262
+ const entries = ruler.__rules__;
263
+ for (let i = 0; i < entries.length; i++) {
264
+ const entry = entries[i];
265
+ if (!entry.enabled) continue;
266
+ if (!(entry.name in RULE_STARTERS)) {
267
+ ruler._compiledRules = rules;
268
+ ruler._dispatchTable = null;
269
+ return null;
270
+ }
271
+ }
272
+ const table = new Array(128);
273
+ for (let c = 0; c < 128; c++) table[c] = [];
274
+ for (let i = 0; i < entries.length; i++) {
275
+ const entry = entries[i];
276
+ if (!entry.enabled) continue;
277
+ const starters = RULE_STARTERS[entry.name];
278
+ if (starters === null) {
279
+ for (let c = 0; c < 128; c++) {
280
+ if (INLINE_TERM[c] === 0) table[c].push(entry.fn);
281
+ }
282
+ } else {
283
+ for (let j = 0; j < starters.length; j++) table[starters[j]].push(entry.fn);
284
+ }
285
+ }
286
+ ruler._compiledRules = rules;
287
+ ruler._dispatchTable = table;
288
+ return table;
289
+ }
290
+
291
+ function tuneInlineTokenizer(md) {
292
+ const inline = md.inline;
293
+ inline.tokenize = function tokenize(state) {
294
+ const rules = this.ruler.getRules('');
295
+ const end = state.posMax;
296
+ const maxNesting = state.md.options.maxNesting;
297
+ const src = state.src;
298
+ const table = this.ruler.__cache__?.[''] === rules ? compileInlineDispatch(this.ruler) : null;
299
+ if (table) {
300
+ while (state.pos < end) {
301
+ const prevPos = state.pos;
302
+ const ch = src.charCodeAt(prevPos);
303
+ if (ch >= 128 || INLINE_TERM[ch] === 0) {
304
+ let pos = prevPos + 1;
305
+ while (pos < end) {
306
+ const c = src.charCodeAt(pos);
307
+ if (c < 128 && INLINE_TERM[c] === 1) break;
308
+ pos++;
309
+ }
310
+ state.pending += src.slice(prevPos, pos);
311
+ state.pos = pos;
312
+ if (pos >= end) break;
313
+ continue;
314
+ }
315
+ let ok = false;
316
+ if (state.level < maxNesting) {
317
+ const bucket = table[ch], bLen = bucket.length;
318
+ for (let i = 0; i < bLen; i++) {
319
+ ok = bucket[i](state, false);
320
+ if (ok) {
321
+ if (prevPos >= state.pos) throw new Error("inline rule didn't increment state.pos");
322
+ break;
323
+ }
324
+ }
325
+ }
326
+ if (ok) {
327
+ if (state.pos >= end) break;
328
+ continue;
329
+ }
330
+ state.pending += src[state.pos++];
331
+ }
332
+ if (state.pending) state.pushPending();
333
+ return;
334
+ }
335
+ const len = rules.length;
336
+ while (state.pos < end) {
337
+ const prevPos = state.pos;
338
+ let ok = false;
339
+ if (state.level < maxNesting) {
340
+ for (let i = 0; i < len; i++) {
341
+ ok = rules[i](state, false);
342
+ if (ok) {
343
+ if (prevPos >= state.pos) throw new Error("inline rule didn't increment state.pos");
344
+ break;
345
+ }
346
+ }
347
+ }
348
+ if (ok) {
349
+ if (state.pos >= end) break;
350
+ continue;
351
+ }
352
+ state.pending += src[state.pos++];
353
+ }
354
+ if (state.pending) state.pushPending();
355
+ };
356
+
357
+ inline.skipToken = function skipToken(state) {
358
+ const pos = state.pos;
359
+ const cache = state.cache;
360
+ if (cache[pos] !== undefined) {
361
+ state.pos = cache[pos];
362
+ return;
363
+ }
364
+ const rules = this.ruler.getRules('');
365
+ const maxNesting = state.md.options.maxNesting;
366
+ let ok = false;
367
+ if (state.level < maxNesting) {
368
+ const table = this.ruler.__cache__?.[''] === rules ? compileInlineDispatch(this.ruler) : null;
369
+ if (table) {
370
+ const ch = state.src.charCodeAt(pos);
371
+ if (ch >= 128 || INLINE_TERM[ch] === 0) {
372
+ let end = pos + 1;
373
+ const max = state.posMax, src = state.src;
374
+ while (end < max) {
375
+ const c = src.charCodeAt(end);
376
+ if (c < 128 && INLINE_TERM[c] === 1) break;
377
+ end++;
378
+ }
379
+ state.pos = end;
380
+ cache[pos] = end;
381
+ return;
382
+ }
383
+ const bucket = table[ch], bLen = bucket.length;
384
+ for (let i = 0; i < bLen; i++) {
385
+ state.level++;
386
+ ok = bucket[i](state, true);
387
+ state.level--;
388
+ if (ok) {
389
+ if (pos >= state.pos) throw new Error("inline rule didn't increment state.pos");
390
+ break;
391
+ }
392
+ }
393
+ } else {
394
+ const len = rules.length;
395
+ for (let i = 0; i < len; i++) {
396
+ state.level++;
397
+ ok = rules[i](state, true);
398
+ state.level--;
399
+ if (ok) {
400
+ if (pos >= state.pos) throw new Error("inline rule didn't increment state.pos");
401
+ break;
402
+ }
403
+ }
404
+ }
405
+ } else {
406
+ state.pos = state.posMax;
407
+ }
408
+ if (!ok) state.pos++;
409
+ cache[pos] = state.pos;
410
+ };
411
+ }
412
+
413
+ function fastParseLinkLabel(state, start, disableNested) {
414
+ const max = state.posMax, src = state.src;
415
+ if (state._labelPos === start && state._labelMax === max) {
416
+ if (disableNested && state._labelNested) return -1;
417
+ return state._labelEnd;
418
+ }
419
+ let pos = start + 1;
420
+ let simple = true;
421
+ while (pos < max) {
422
+ const ch = src.charCodeAt(pos);
423
+ if (ch === 0x5d) {
424
+ state._labelPos = start;
425
+ state._labelMax = max;
426
+ state._labelEnd = pos;
427
+ state._labelNested = false;
428
+ return pos;
429
+ }
430
+ if (ch < 128 && LABEL_SPECIAL[ch] === 1) {
431
+ simple = false;
432
+ break;
433
+ }
434
+ pos++;
435
+ }
436
+ if (simple) {
437
+ state._labelPos = start;
438
+ state._labelMax = max;
439
+ state._labelEnd = -1;
440
+ state._labelNested = false;
441
+ return -1;
442
+ }
443
+ const oldPos = state.pos;
444
+ state.pos = start + 1;
445
+ let level = 1, found = false, hasNested = false;
446
+ while (state.pos < max) {
447
+ const marker = src.charCodeAt(state.pos);
448
+ if (marker === 0x5d) {
449
+ level--;
450
+ if (level === 0) {
451
+ found = true;
452
+ break;
453
+ }
454
+ }
455
+ const prevPos = state.pos;
456
+ state.md.inline.skipToken(state);
457
+ if (marker === 0x5b) {
458
+ if (prevPos === state.pos - 1) {
459
+ level++;
460
+ } else {
461
+ hasNested = true;
462
+ if (disableNested) {
463
+ state.pos = oldPos;
464
+ state._labelPos = start;
465
+ state._labelMax = max;
466
+ state._labelEnd = -2;
467
+ state._labelNested = true;
468
+ return -1;
469
+ }
470
+ }
471
+ }
472
+ }
473
+ const labelEnd = found ? state.pos : -1;
474
+ state.pos = oldPos;
475
+ if (state._labelPos !== start || state._labelMax !== max || state._labelEnd !== -2) {
476
+ state._labelPos = start;
477
+ state._labelMax = max;
478
+ state._labelEnd = labelEnd;
479
+ state._labelNested = hasNested;
480
+ }
481
+ return disableNested && hasNested ? -1 : labelEnd;
482
+ }
483
+
484
+ const DELIM_RESULT = {can_open: false, can_close: false, length: 0};
485
+
486
+ function tuneScanDelims(md) {
487
+ const isPunctCharCode = md.utils.isPunctCharCode;
488
+ const isWhiteSpace = md.utils.isWhiteSpace;
489
+ md.inline.State.prototype.scanDelims = function scanDelims(start, canSplitWord) {
490
+ const max = this.posMax, src = this.src;
491
+ const marker = src.charCodeAt(start);
492
+ let lastChar;
493
+ if (start === 0) {
494
+ lastChar = 0x20;
495
+ } else if (start === 1) {
496
+ lastChar = src.charCodeAt(0);
497
+ if ((lastChar & 0xf800) === 0xd800) lastChar = 0xfffd;
498
+ } else {
499
+ lastChar = src.charCodeAt(start - 1);
500
+ if ((lastChar & 0xfc00) === 0xdc00) {
501
+ const highSurr = src.charCodeAt(start - 2);
502
+ lastChar = (highSurr & 0xfc00) === 0xd800 ? 0x10000 + ((highSurr - 0xd800) << 10) + (lastChar - 0xdc00) : 0xfffd;
503
+ } else if ((lastChar & 0xfc00) === 0xd800) {
504
+ lastChar = 0xfffd;
505
+ }
506
+ }
507
+ let pos = start + 1;
508
+ while (pos < max && src.charCodeAt(pos) === marker) pos++;
509
+ const count = pos - start;
510
+ let nextChar = pos < max ? src.charCodeAt(pos) : 0x20;
511
+ if ((nextChar & 0xfc00) === 0xd800) {
512
+ const lowSurr = src.charCodeAt(pos + 1);
513
+ nextChar = (lowSurr & 0xfc00) === 0xdc00 ? 0x10000 + ((nextChar - 0xd800) << 10) + (lowSurr - 0xdc00) : 0xfffd;
514
+ } else if ((nextChar & 0xfc00) === 0xdc00) {
515
+ nextChar = 0xfffd;
516
+ }
517
+ const isLastWhiteSpace = lastChar < 128 ? (lastChar === 0x20 || (lastChar >= 0x09 && lastChar <= 0x0d)) : isWhiteSpace(lastChar);
518
+ const isNextWhiteSpace = nextChar < 128 ? (nextChar === 0x20 || (nextChar >= 0x09 && nextChar <= 0x0d)) : isWhiteSpace(nextChar);
519
+ const isLastPunctChar = !isLastWhiteSpace && (lastChar < 128 ? ASCII_PUNCT[lastChar] === 1 : isPunctCharCode(lastChar));
520
+ const isNextPunctChar = !isNextWhiteSpace && (nextChar < 128 ? ASCII_PUNCT[nextChar] === 1 : isPunctCharCode(nextChar));
521
+ const left_flanking = !isNextWhiteSpace && (!isNextPunctChar || isLastWhiteSpace || isLastPunctChar);
522
+ const right_flanking = !isLastWhiteSpace && (!isLastPunctChar || isNextWhiteSpace || isNextPunctChar);
523
+ DELIM_RESULT.can_open = left_flanking && (canSplitWord || !right_flanking || isLastPunctChar);
524
+ DELIM_RESULT.can_close = right_flanking && (canSplitWord || !left_flanking || isNextPunctChar);
525
+ DELIM_RESULT.length = count;
526
+ return DELIM_RESULT;
527
+ };
528
+ }
529
+
530
+ function fragmentsJoin(state) {
531
+ const tokens = state.tokens, max = tokens.length;
532
+ if (max === 0) return;
533
+ let level = 0, last = 0;
534
+ for (let curr = 0; curr < max; curr++) {
535
+ const tok = tokens[curr], nesting = tok.nesting;
536
+ if (nesting < 0) level--;
537
+ tok.level = level;
538
+ if (nesting > 0) level++;
539
+ if (tok.type === 'text' && curr + 1 < max && tokens[curr + 1].type === 'text') {
540
+ tokens[curr + 1].content = tok.content + tokens[curr + 1].content;
541
+ } else {
542
+ if (curr !== last) tokens[last] = tok;
543
+ last++;
544
+ }
545
+ }
546
+ if (last !== max) tokens.length = last;
547
+ }
548
+
549
+ function joinAlt(tokens) {
550
+ const max = tokens.length;
551
+ if (max === 0) return;
552
+ let last = 0;
553
+ for (let curr = 0; curr < max; curr++) {
554
+ const tok = tokens[curr];
555
+ if (tok.type === 'text_special') tok.type = 'text';
556
+ }
557
+ for (let curr = 0; curr < max; curr++) {
558
+ const tok = tokens[curr];
559
+ if (tok.type === 'text' && curr + 1 < max && tokens[curr + 1].type === 'text') {
560
+ tokens[curr + 1].content = tok.content + tokens[curr + 1].content;
561
+ } else {
562
+ if (curr !== last) tokens[last] = tok;
563
+ last++;
564
+ }
565
+ }
566
+ if (last !== max) tokens.length = last;
567
+ }
568
+
569
+ function textJoin(state) {
570
+ const blockTokens = state.tokens, l = blockTokens.length;
571
+ for (let j = 0; j < l; j++) {
572
+ if (blockTokens[j].type !== 'inline') continue;
573
+ const tokens = blockTokens[j].children, max = tokens.length;
574
+ if (max === 0) continue;
575
+ let hasSpecialOrMerge = false;
576
+ for (let curr = 0; curr < max; curr++) {
577
+ const tok = tokens[curr];
578
+ if (tok.type === 'text_special') {
579
+ tok.type = 'text';
580
+ hasSpecialOrMerge = true;
581
+ }
582
+ if (tok.children) joinAlt(tok.children);
583
+ if (!hasSpecialOrMerge && tok.type === 'text' && curr + 1 < max && (tokens[curr + 1].type === 'text' || tokens[curr + 1].type === 'text_special')) {
584
+ hasSpecialOrMerge = true;
585
+ }
586
+ }
587
+ if (!hasSpecialOrMerge) continue;
588
+ let last = 0;
589
+ for (let curr = 0; curr < max; curr++) {
590
+ const tok = tokens[curr];
591
+ if (tok.type === 'text' && curr + 1 < max && tokens[curr + 1].type === 'text') {
592
+ tokens[curr + 1].content = tok.content + tokens[curr + 1].content;
593
+ } else {
594
+ if (curr !== last) tokens[last] = tok;
595
+ last++;
596
+ }
597
+ }
598
+ if (last !== max) tokens.length = last;
599
+ }
600
+ }
601
+
34
602
  function backticks(state, silent) {
35
603
  const start = state.pos, src = state.src;
36
604
  if (src.charCodeAt(start) !== 0x60) return false;
37
605
  const max = state.posMax;
38
606
  let pos = start + 1;
39
607
  while (pos < max && src.charCodeAt(pos) === 0x60) pos++;
40
- const marker = src.slice(start, pos), length = marker.length, runs = state.backticks;
608
+ const marker = pos - start === 1 ? '`' : src.slice(start, pos), length = marker.length, runs = state.backticks;
41
609
  const scanned = state.backticksScanned;
42
610
  if (!scanned || pos < scanned || (runs[length] ?? -1) >= pos) {
43
611
  let matchStart, matchEnd = pos;
@@ -50,8 +618,11 @@ function backticks(state, silent) {
50
618
  if (!silent) {
51
619
  const token = state.push('code_inline', 'code', 0);
52
620
  token.markup = marker;
53
- let content = src.slice(pos, matchStart).replace(/\n/g, ' ');
54
- if (content.startsWith(' ') && content.endsWith(' ') && /[^ ]/.test(content)) content = content.slice(1, -1);
621
+ let content = src.slice(pos, matchStart);
622
+ if (content.indexOf('\n') >= 0) content = content.replace(/\n/g, ' ');
623
+ if (content.length >= 2 && content.charCodeAt(0) === 0x20 && content.charCodeAt(content.length - 1) === 0x20 && /[^ ]/.test(content)) {
624
+ content = content.slice(1, -1);
625
+ }
55
626
  token.content = content;
56
627
  }
57
628
  state.pos = matchEnd;
@@ -72,37 +643,78 @@ function cleanWebLink(url) {
72
643
  return match !== null && match[1].length <= 255;
73
644
  }
74
645
 
75
- function releasedState(state, md) {
76
- for (const key of Object.keys(state)) {
77
- const value = state[key];
78
- if (typeof value === 'string') state[key] = '';
79
- else if (typeof value === 'function') state[key] = released;
80
- else if (value !== null && typeof value === 'object' && value !== md) state[key] = Array.isArray(value) ? [] : {};
81
- }
646
+ function releaseCoreState(state) {
647
+ state.src = '';
648
+ state.env = {};
649
+ state.tokens = [];
650
+ return state;
651
+ }
652
+
653
+ function releaseBlockState(state) {
654
+ state.src = '';
655
+ state.env = {};
656
+ state.tokens = [];
657
+ state.bMarks = [];
658
+ state.eMarks = [];
659
+ state.tShift = [];
660
+ state.sCount = [];
661
+ state.bsCount = [];
662
+ return state;
663
+ }
664
+
665
+ function releaseInlineState(state) {
666
+ state.src = '';
667
+ state.env = {};
668
+ state.tokens = [];
669
+ state.tokens_meta = [];
670
+ state.pending = '';
671
+ state.delimiters = [];
672
+ state._prev_delimiters = [];
673
+ state.backticks = {};
82
674
  return state;
83
675
  }
84
- function released() {}
85
- function sampleTokens(tokens) {
86
- const kept = new Map();
87
- for (let index = 0; index < tokens.length; index++) {
676
+
677
+ function detachTokenSample(token) {
678
+ const copy = Object.assign(Object.create(Object.getPrototypeOf(token)), token);
679
+ copy.content = '';
680
+ copy.markup = '';
681
+ copy.info = '';
682
+ copy.children = token.children ? [] : null;
683
+ copy.meta = token.meta ? {} : null;
684
+ copy.attrs = token.attrs ? [] : null;
685
+ return copy;
686
+ }
687
+
688
+ function sampleTokensOnce(kept, tokens) {
689
+ const limit = Math.min(tokens.length, 256);
690
+ for (let index = 0; index < limit; index++) {
88
691
  const token = tokens[index];
89
- if (!kept.has(token.type)) kept.set(token.type, token);
692
+ if (!kept.has(token.type)) kept.set(token.type, detachTokenSample(token));
90
693
  const children = token.children;
91
694
  if (children) for (let at = 0; at < children.length; at++) {
92
695
  const child = children[at];
93
- if (!kept.has(child.type)) kept.set(child.type, child);
696
+ if (!kept.has(child.type)) kept.set(child.type, detachTokenSample(child));
94
697
  }
95
698
  }
96
- return kept;
699
+ }
700
+
701
+ function hasAnyDelimiters(state) {
702
+ if (state.delimiters.length > 0) return true;
703
+ const meta = state.tokens_meta, len = meta.length;
704
+ for (let i = 0; i < len; i++) {
705
+ const m = meta[i];
706
+ if (m && m.delimiters && m.delimiters.length > 0) return true;
707
+ }
708
+ return false;
97
709
  }
98
710
 
99
711
  function keepClasses(md) {
100
- const anchors = {core: null, block: null, inline: null, tokens: null};
712
+ const anchors = {core: null, block: null, inline: null, tokens: new Map()};
101
713
  const finish = (state, tokens) => {
102
- anchors.core = releasedState(state, md);
103
- if (anchors.block) releasedState(anchors.block, md);
104
- if (anchors.inline) releasedState(anchors.inline, md);
105
- anchors.tokens = sampleTokens(tokens);
714
+ anchors.core = releaseCoreState(state);
715
+ if (anchors.block) releaseBlockState(anchors.block);
716
+ if (anchors.inline) releaseInlineState(anchors.inline);
717
+ if (anchors.tokens.size < 24 && tokens.length > 0) sampleTokensOnce(anchors.tokens, tokens);
106
718
  };
107
719
  const release = (owner, key) => Object.getPrototypeOf(owner)[key] === owner[key];
108
720
  if (release(md, 'parse') && release(md, 'parseInline')) {
@@ -132,8 +744,12 @@ function keepClasses(md) {
132
744
  if (release(md.inline, 'parse')) md.inline.parse = function (str, md, env, outTokens) {
133
745
  const state = new this.State(str, md, env, outTokens);
134
746
  this.tokenize(state);
135
- const rules = this.ruler2.getRules(''), length = rules.length;
136
- for (let index = 0; index < length; index++) rules[index](state);
747
+ if (hasAnyDelimiters(state)) {
748
+ const rules = this.ruler2.getRules(''), length = rules.length;
749
+ for (let index = 0; index < length; index++) rules[index](state);
750
+ } else {
751
+ fragmentsJoin(state);
752
+ }
137
753
  anchors.inline = state;
138
754
  };
139
755
  }
@@ -144,12 +760,20 @@ export function tuneMarkdownEngine(md) {
144
760
  md.core.ruler.at('normalize', state => {
145
761
  let src = state.src;
146
762
  if (src.indexOf('\r') >= 0) src = src.replace(NEWLINES, '\n');
147
- if (src.indexOf('\0') >= 0) src = src.replace(NULLS, '�');
763
+ if (src.indexOf('\0') >= 0) src = src.replace(NULLS, '\ufffd');
148
764
  state.src = src;
149
765
  });
766
+ md.core.ruler.at('text_join', textJoin);
150
767
  const BaseBlock = md.block.State;
151
768
  md.block.State = lineScanner(BaseBlock, new BaseBlock('', md, {}, []).Token);
769
+ md.block.ruler.at('lheading', lheading);
770
+ md.block.ruler.at('paragraph', paragraph);
771
+ md.inline.ruler.at('text', textRule);
152
772
  md.inline.ruler.at('backticks', backticks);
773
+ md.inline.ruler2.at('fragments_join', fragmentsJoin);
774
+ md.helpers.parseLinkLabel = fastParseLinkLabel;
775
+ tuneScanDelims(md);
776
+ tuneInlineTokenizer(md);
153
777
  const normalizeLink = md.normalizeLink;
154
778
  md.normalizeLink = url => cleanWebLink(url) ? url : normalizeLink.call(md, url);
155
779
  keepClasses(md);