Browse Source

Various performance improvements

pull/748/head
Fabio Spampinato 4 years ago
parent
commit
c888839574
  1. 37
      benchmark/benchloop.js
  2. 4
      benchmark/profile.js
  3. 11
      lib/common/utils.js
  4. 48
      lib/renderer.js
  5. 6
      lib/rules_block/blockquote.js
  6. 4
      lib/rules_block/code.js
  7. 6
      lib/rules_block/fence.js
  8. 6
      lib/rules_block/heading.js
  9. 9
      lib/rules_block/hr.js
  10. 8
      lib/rules_block/html_block.js
  11. 6
      lib/rules_block/lheading.js
  12. 40
      lib/rules_block/list.js
  13. 6
      lib/rules_block/reference.js
  14. 14
      lib/rules_block/state_block.js
  15. 10
      lib/rules_block/table.js
  16. 4
      lib/rules_core/linkify.js
  17. 17
      lib/rules_core/normalize.js
  18. 2
      lib/rules_core/smartquotes.js
  19. 4
      lib/rules_inline/autolink.js
  20. 7
      lib/rules_inline/backticks.js
  21. 8
      lib/rules_inline/emphasis.js
  22. 4
      lib/rules_inline/entity.js
  23. 4
      lib/rules_inline/escape.js
  24. 12
      lib/rules_inline/html_inline.js
  25. 6
      lib/rules_inline/image.js
  26. 4
      lib/rules_inline/link.js
  27. 16
      lib/rules_inline/newline.js
  28. 8
      lib/rules_inline/strikethrough.js
  29. 17
      lib/rules_inline/text_collapse.js
  30. 1
      package.json

37
benchmark/benchloop.js

@ -0,0 +1,37 @@
'use strict';
/* IMPORT */
var fs = require ('fs'),
path = require ('path'),
benchmark = require ('benchloop'),
mit = require ('..');
/* HELPERS */
var SAMPLES_PATH = path.join (__dirname, 'samples');
var md = mit ('commonmark');
/* BENCHMARK */
benchmark.defaultOptions = Object.assign (benchmark.defaultOptions, {
iterations: 50,
log: 'compact'
});
fs.readdirSync (SAMPLES_PATH).forEach (function (sample) {
var samplePath = path.join (SAMPLES_PATH, sample),
content = fs.readFileSync (samplePath, 'utf-8');
benchmark ({
name: sample,
fn: function () {
md.render (content);
}
});
});
benchmark.summary ();

4
benchmark/profile.js

@ -14,6 +14,8 @@ var md = require('../')({
// var data = fs.readFileSync(path.join(__dirname, '/samples/lorem1.txt'), 'utf8');
var data = fs.readFileSync(path.join(__dirname, '../test/fixtures/commonmark/spec.txt'), 'utf8');
for (var i = 0; i < 20; i++) {
console.time('profile');
for (var i = 0; i < 200; i++) {
md.render(data);
}
console.timeEnd('profile');

11
lib/common/utils.js

@ -13,6 +13,14 @@ function has(object, key) {
return _hasOwnProperty.call(object, key);
}
var _repeat = String.prototype.repeat || function (count) {
return new Array(count + 1).join (this);
};
function repeat(str, count) {
return _repeat.call (str, count);
}
// Merge objects
//
function assign(obj /*from1, from2, from3, ...*/) {
@ -237,7 +245,7 @@ function isMdAsciiPunct(ch) {
}
}
// Hepler to unify [reference labels].
// Helper to unify [reference labels].
//
function normalizeReference(str) {
// Trim and collapse whitespace
@ -302,6 +310,7 @@ exports.lib.ucmicro = require('uc.micro');
exports.assign = assign;
exports.isString = isString;
exports.has = has;
exports.repeat = repeat;
exports.unescapeMd = unescapeMd;
exports.unescapeAll = unescapeAll;
exports.isValidEntityCode = isValidEntityCode;

48
lib/renderer.js

@ -44,9 +44,14 @@ default_rules.fence = function (tokens, idx, options, env, slf) {
highlighted, i, arr, tmpAttrs, tmpToken;
if (info) {
arr = info.split(/(\s+)/g);
langName = arr[0];
langAttrs = arr.slice(2).join('');
if (!/\s/.test (info)) {
langName = info;
langAttrs = '';
} else {
arr = info.split(/(\s+)/g);
langName = arr[0];
langAttrs = arr.slice(2).join('');
}
}
if (options.highlight) {
@ -55,7 +60,7 @@ default_rules.fence = function (tokens, idx, options, env, slf) {
highlighted = escapeHtml(token.content);
}
if (highlighted.indexOf('<pre') === 0) {
if (highlighted.slice(0, 4) === '<pre') {
return highlighted + '\n';
}
@ -63,12 +68,12 @@ default_rules.fence = function (tokens, idx, options, env, slf) {
// May be, one day we will add .deepClone() for token and simplify this part, but
// now we prefer to keep things local.
if (info) {
i = token.attrIndex('class');
tmpAttrs = token.attrs ? token.attrs.slice() : [];
i = token.attrIndex('class');
if (i < 0) {
tmpAttrs.push([ 'class', options.langPrefix + langName ]);
tmpAttrs = [ [ 'class', options.langPrefix + langName ] ];
} else {
tmpAttrs = token.attrs.slice();
tmpAttrs[i] = tmpAttrs[i].slice();
tmpAttrs[i][1] += ' ' + options.langPrefix + langName;
}
@ -78,6 +83,12 @@ default_rules.fence = function (tokens, idx, options, env, slf) {
attrs: tmpAttrs
};
if (i < 0) {
return '<pre><code' + slf.renderAttrs(token) + slf.renderAttrs(tmpToken) + '>'
+ highlighted
+ '</code></pre>\n';
}
return '<pre><code' + slf.renderAttrs(tmpToken) + '>'
+ highlighted
+ '</code></pre>\n';
@ -293,13 +304,17 @@ Renderer.prototype.renderInline = function (tokens, options, env) {
* instead of simple escaping.
**/
Renderer.prototype.renderInlineAsText = function (tokens, options, env) {
var result = '';
var type, token,
result = '';
for (var i = 0, len = tokens.length; i < len; i++) {
if (tokens[i].type === 'text') {
result += tokens[i].content;
} else if (tokens[i].type === 'image') {
result += this.renderInlineAsText(tokens[i].children, options, env);
token = tokens[i];
type = token.type;
if (type === 'text') {
result += token.content;
} else if (type === 'image') {
result += this.renderInlineAsText(token.children, options, env);
}
}
@ -317,17 +332,18 @@ Renderer.prototype.renderInlineAsText = function (tokens, options, env) {
* this method directly.
**/
Renderer.prototype.render = function (tokens, options, env) {
var i, len, type,
var i, len, type, token,
result = '',
rules = this.rules;
for (i = 0, len = tokens.length; i < len; i++) {
type = tokens[i].type;
token = tokens[i];
type = token.type;
if (type === 'inline') {
result += this.renderInline(tokens[i].children, options, env);
result += this.renderInline(token.children, options, env);
} else if (typeof rules[type] !== 'undefined') {
result += rules[tokens[i].type](tokens, i, options, env, this);
result += rules[type](tokens, i, options, env, this);
} else {
result += this.renderToken(tokens, i, options, env);
}

6
lib/rules_block/blockquote.js

@ -6,6 +6,9 @@ var isSpace = require('../common/utils').isSpace;
module.exports = function blockquote(state, startLine, endLine, silent) {
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
var adjustTab,
ch,
i,
@ -30,9 +33,6 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
// check the block quote marker
if (state.src.charCodeAt(pos++) !== 0x3E/* > */) { return false; }

4
lib/rules_block/code.js

@ -4,10 +4,10 @@
module.exports = function code(state, startLine, endLine/*, silent*/) {
var nextLine, last, token;
if (state.sCount[startLine] - state.blkIndent < 4) { return false; }
var nextLine, last, token;
last = nextLine = startLine + 1;
while (nextLine < endLine) {

6
lib/rules_block/fence.js

@ -4,14 +4,14 @@
module.exports = function fence(state, startLine, endLine, silent) {
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
var marker, len, params, nextLine, mem, token, markup,
haveEndMarker = false,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
if (pos + 3 > max) { return false; }
marker = state.src.charCodeAt(pos);

6
lib/rules_block/heading.js

@ -6,13 +6,13 @@ var isSpace = require('../common/utils').isSpace;
module.exports = function heading(state, startLine, endLine, silent) {
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
var ch, level, tmp, token,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
ch = state.src.charCodeAt(pos);
if (ch !== 0x23/* # */ || pos >= max) { return false; }

9
lib/rules_block/hr.js

@ -3,16 +3,17 @@
'use strict';
var isSpace = require('../common/utils').isSpace;
var repeat = require('../common/utils').repeat;
module.exports = function hr(state, startLine, endLine, silent) {
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
var marker, cnt, ch, token,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
marker = state.src.charCodeAt(pos++);
// Check hr marker
@ -39,7 +40,7 @@ module.exports = function hr(state, startLine, endLine, silent) {
token = state.push('hr', 'hr', 0);
token.map = [ startLine, state.line ];
token.markup = Array(cnt + 1).join(String.fromCharCode(marker));
token.markup = repeat (String.fromCharCode(marker), cnt);
return true;
};

8
lib/rules_block/html_block.js

@ -21,14 +21,14 @@ var HTML_SEQUENCES = [
module.exports = function html_block(state, startLine, endLine, silent) {
var i, nextLine, token, lineText,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
if (!state.md.options.html) { return false; }
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
if (!state.md.options.html) { return false; }
var i, nextLine, token, lineText,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }

6
lib/rules_block/lheading.js

@ -4,13 +4,13 @@
module.exports = function lheading(state, startLine, endLine/*, silent*/) {
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
var content, terminate, i, l, token, pos, max, level, marker,
nextLine = startLine + 1, oldParentType,
terminatorRules = state.md.block.ruler.getRules('paragraph');
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
oldParentType = state.parentType;
state.parentType = 'paragraph'; // use paragraph to match terminatorRules

40
lib/rules_block/list.js

@ -11,7 +11,6 @@ function skipBulletListMarker(state, startLine) {
var marker, pos, max, ch;
pos = state.bMarks[startLine] + state.tShift[startLine];
max = state.eMarks[startLine];
marker = state.src.charCodeAt(pos++);
// Check bullet
@ -21,6 +20,8 @@ function skipBulletListMarker(state, startLine) {
return -1;
}
max = state.eMarks[startLine];
if (pos < max) {
ch = state.src.charCodeAt(pos);
@ -84,13 +85,14 @@ function skipOrderedListMarker(state, startLine) {
}
function markTightParagraphs(state, idx) {
var i, l,
var i, l, token,
level = state.level + 2;
for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {
token = state.tokens[i];
if (token.level === level && token.type === 'paragraph_open') {
state.tokens[i + 2].hidden = true;
state.tokens[i].hidden = true;
token.hidden = true;
i += 2;
}
}
@ -98,6 +100,21 @@ function markTightParagraphs(state, idx) {
module.exports = function list(state, startLine, endLine, silent) {
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
// Special case:
// - item 1
// - item 2
// - item 3
// - item 4
// - this one is a paragraph continuation
if (state.listIndent >= 0 &&
state.sCount[startLine] - state.listIndent >= 4 &&
state.sCount[startLine] < state.blkIndent) {
return false;
}
var ch,
contentStart,
i,
@ -129,21 +146,6 @@ module.exports = function list(state, startLine, endLine, silent) {
isTerminatingParagraph = false,
tight = true;
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
// Special case:
// - item 1
// - item 2
// - item 3
// - item 4
// - this one is a paragraph continuation
if (state.listIndent >= 0 &&
state.sCount[startLine] - state.listIndent >= 4 &&
state.sCount[startLine] < state.blkIndent) {
return false;
}
// limit conditions when list can interrupt
// a paragraph (validation mode only)
if (silent && state.parentType === 'paragraph') {

6
lib/rules_block/reference.js

@ -6,6 +6,9 @@ var isSpace = require('../common/utils').isSpace;
module.exports = function reference(state, startLine, _endLine, silent) {
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
var ch,
destEndPos,
destEndLineNo,
@ -27,9 +30,6 @@ module.exports = function reference(state, startLine, _endLine, silent) {
max = state.eMarks[startLine],
nextLine = startLine + 1;
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false; }
// Simple check to quickly interrupt scan on [link](url) at the start of line.

14
lib/rules_block/state_block.js

@ -4,6 +4,7 @@
var Token = require('../token');
var isSpace = require('../common/utils').isSpace;
var repeat = require('../common/utils').repeat;
function StateBlock(src, md, env, tokens) {
@ -173,15 +174,14 @@ StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) {
// cut lines range from source.
StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) {
var i, lineIndent, ch, first, last, queue, lineStart,
var i, lineIndent, ch, first, last, lineStart,
lines = '',
line = begin;
if (begin >= end) {
return '';
return lines;
}
queue = new Array(end - begin);
for (i = 0; line < end; line++, i++) {
lineIndent = 0;
lineStart = first = this.bMarks[line];
@ -215,13 +215,13 @@ StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF
if (lineIndent > indent) {
// partially expanding tabs in code blocks, e.g '\t\tfoobar'
// with indent=2 becomes ' \tfoobar'
queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last);
lines += repeat (' ', lineIndent - indent) + this.src.slice(first, last);
} else {
queue[i] = this.src.slice(first, last);
lines += this.src.slice(first, last);
}
}
return queue.join('');
return lines;
};
// re-export Token class to use in block rules

10
lib/rules_block/table.js

@ -50,20 +50,20 @@ function escapedSplit(str) {
module.exports = function table(state, startLine, endLine, silent) {
var ch, lineText, pos, i, l, nextLine, columns, columnCount, token,
aligns, t, tableLines, tbodyLines, oldParentType, terminate,
terminatorRules;
// should have at least two lines
if (startLine + 2 > endLine) { return false; }
nextLine = startLine + 1;
var nextLine = startLine + 1;
if (state.sCount[nextLine] < state.blkIndent) { return false; }
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[nextLine] - state.blkIndent >= 4) { return false; }
var ch, lineText, pos, i, l, columns, columnCount, token,
aligns, t, tableLines, tbodyLines, oldParentType, terminate,
terminatorRules;
// first character of the second line should be '|', '-', ':',
// and no other characters are allowed but spaces;
// basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp

4
lib/rules_core/linkify.js

@ -17,13 +17,13 @@ function isLinkClose(str) {
module.exports = function linkify(state) {
if (!state.md.options.linkify) { return; }
var i, j, l, tokens, token, currentToken, nodes, ln, text, pos, lastPos,
level, htmlLinkLevel, url, fullUrl, urlText,
blockTokens = state.tokens,
links;
if (!state.md.options.linkify) { return; }
for (j = 0, l = blockTokens.length; j < l; j++) {
if (blockTokens[j].type !== 'inline' ||
!state.md.linkify.pretest(blockTokens[j].content)) {

17
lib/rules_core/normalize.js

@ -4,18 +4,21 @@
// https://spec.commonmark.org/0.29/#line-ending
var NEWLINES_RE = /\r\n?|\n/g;
var NULL_RE = /\0/g;
var CRLF_RE = /\r\n?/g;
module.exports = function normalize(state) {
var str;
var src = state.src;
// Normalize newlines
str = state.src.replace(NEWLINES_RE, '\n');
// Normalize CRLF newlines
src = src.replace(CRLF_RE, '\n');
// Replace NULL characters
str = str.replace(NULL_RE, '\uFFFD');
for (var i = 0, l = src.length; i < l; i++) {
if (!src.charCodeAt(i)) {
src = src.slice (0, i) + '\uFFFD' + src.slice (i + 1);
}
}
state.src = str;
state.src = src;
};

2
lib/rules_core/smartquotes.js

@ -26,7 +26,7 @@ function process_inlines(tokens, state) {
for (i = 0; i < tokens.length; i++) {
token = tokens[i];
thisLevel = tokens[i].level;
thisLevel = token.level;
for (j = stack.length - 1; j >= 0; j--) {
if (stack[j].level <= thisLevel) { break; }

4
lib/rules_inline/autolink.js

@ -9,11 +9,11 @@ var AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$/;
module.exports = function autolink(state, silent) {
if (state.src.charCodeAt(state.pos) !== 0x3C/* < */) { return false; }
var url, fullUrl, token, ch, start, max,
pos = state.pos;
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }
start = state.pos;
max = state.posMax;

7
lib/rules_inline/backticks.js

@ -4,12 +4,13 @@
module.exports = function backtick(state, silent) {
var start, max, marker, token, matchStart, matchEnd, openerLength, closerLength,
pos = state.pos,
ch = state.src.charCodeAt(pos);
var ch = state.src.charCodeAt(state.pos);
if (ch !== 0x60/* ` */) { return false; }
var start, max, marker, token, matchStart, matchEnd, openerLength, closerLength,
pos = state.pos;
start = pos;
pos++;
max = state.posMax;

8
lib/rules_inline/emphasis.js

@ -6,14 +6,14 @@
// Insert each marker as a separate text token, and add it to delimiter list
//
module.exports.tokenize = function emphasis(state, silent) {
var i, scanned, token,
start = state.pos,
marker = state.src.charCodeAt(start);
if (silent) { return false; }
var marker = state.src.charCodeAt(state.pos);
if (marker !== 0x5F /* _ */ && marker !== 0x2A /* * */) { return false; }
var i, scanned, token;
scanned = state.scanDelims(state.pos, marker === 0x2A);
for (i = 0; i < scanned.length; i++) {

4
lib/rules_inline/entity.js

@ -13,9 +13,9 @@ var NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i;
module.exports = function entity(state, silent) {
var ch, code, match, pos = state.pos, max = state.posMax;
if (state.src.charCodeAt(state.pos) !== 0x26/* & */) { return false; }
if (state.src.charCodeAt(pos) !== 0x26/* & */) { return false; }
var ch, code, match, pos = state.pos, max = state.posMax;
if (pos + 1 < max) {
ch = state.src.charCodeAt(pos + 1);

4
lib/rules_inline/escape.js

@ -13,9 +13,9 @@ for (var i = 0; i < 256; i++) { ESCAPED.push(0); }
module.exports = function escape(state, silent) {
var ch, pos = state.pos, max = state.posMax;
if (state.src.charCodeAt(state.pos) !== 0x5C/* \ */) { return false; }
if (state.src.charCodeAt(pos) !== 0x5C/* \ */) { return false; }
var ch, pos = state.pos, max = state.posMax;
pos++;

12
lib/rules_inline/html_inline.js

@ -14,18 +14,18 @@ function isLetter(ch) {
module.exports = function html_inline(state, silent) {
var ch, match, max, token,
pos = state.pos;
if (!state.md.options.html) { return false; }
// Check start
max = state.posMax;
if (state.src.charCodeAt(pos) !== 0x3C/* < */ ||
pos + 2 >= max) {
var max = state.posMax;
if (state.src.charCodeAt(state.pos) !== 0x3C/* < */ ||
state.pos + 2 >= max) {
return false;
}
var ch, match, token,
pos = state.pos;
// Quick fail on second char
ch = state.src.charCodeAt(pos + 1);
if (ch !== 0x21/* ! */ &&

6
lib/rules_inline/image.js

@ -7,6 +7,9 @@ var isSpace = require('../common/utils').isSpace;
module.exports = function image(state, silent) {
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
var attrs,
code,
content,
@ -24,9 +27,6 @@ module.exports = function image(state, silent) {
oldPos = state.pos,
max = state.posMax;
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
labelStart = state.pos + 2;
labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false);

4
lib/rules_inline/link.js

@ -7,6 +7,8 @@ var isSpace = require('../common/utils').isSpace;
module.exports = function link(state, silent) {
if (state.src.charCodeAt(state.pos) !== 0x5B/* [ */) { return false; }
var attrs,
code,
label,
@ -23,8 +25,6 @@ module.exports = function link(state, silent) {
start = state.pos,
parseReference = true;
if (state.src.charCodeAt(state.pos) !== 0x5B/* [ */) { return false; }
labelStart = state.pos + 1;
labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true);

16
lib/rules_inline/newline.js

@ -6,11 +6,13 @@ var isSpace = require('../common/utils').isSpace;
module.exports = function newline(state, silent) {
var pmax, max, pos = state.pos;
if (state.src.charCodeAt(state.pos) !== 0x0A/* \n */) { return false; }
if (state.src.charCodeAt(pos) !== 0x0A/* \n */) { return false; }
var pmax, max,
pending = state.pending,
pos = state.pos;
pmax = state.pending.length - 1;
pmax = pending.length - 1;
max = state.posMax;
// ' \n' -> hardbreak
@ -18,12 +20,12 @@ module.exports = function newline(state, silent) {
// Pending string is stored in concat mode, indexed lookups will cause
// convertion to flat mode.
if (!silent) {
if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) {
if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) {
state.pending = state.pending.replace(/ +$/, '');
if (pmax >= 0 && pending.charCodeAt(pmax) === 0x20) {
if (pmax >= 1 && pending.charCodeAt(pmax - 1) === 0x20) {
state.pending = pending.replace(/ +$/, '');
state.push('hardbreak', 'br', 0);
} else {
state.pending = state.pending.slice(0, -1);
state.pending = pending.slice(0, -1);
state.push('softbreak', 'br', 0);
}

8
lib/rules_inline/strikethrough.js

@ -6,14 +6,14 @@
// Insert each marker as a separate text token, and add it to delimiter list
//
module.exports.tokenize = function strikethrough(state, silent) {
var i, scanned, token, len, ch,
start = state.pos,
marker = state.src.charCodeAt(start);
if (silent) { return false; }
var marker = state.src.charCodeAt(state.pos);
if (marker !== 0x7E/* ~ */) { return false; }
var i, scanned, token, len, ch;
scanned = state.scanDelims(state.pos, true);
len = scanned.length;
ch = String.fromCharCode(marker);

17
lib/rules_inline/text_collapse.js

@ -10,26 +10,27 @@
module.exports = function text_collapse(state) {
var curr, last,
var curr, last, token,
level = 0,
tokens = state.tokens,
max = state.tokens.length;
max = tokens.length;
for (curr = last = 0; curr < max; curr++) {
token = tokens[curr];
// re-calculate levels after emphasis/strikethrough turns some text nodes
// into opening/closing tags
if (tokens[curr].nesting < 0) level--; // closing tag
tokens[curr].level = level;
if (tokens[curr].nesting > 0) level++; // opening tag
if (token.nesting < 0) level--; // closing tag
token.level = level;
if (token.nesting > 0) level++; // opening tag
if (tokens[curr].type === 'text' &&
if (token.type === 'text' &&
curr + 1 < max &&
tokens[curr + 1].type === 'text') {
// collapse two adjacent text nodes
tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content;
tokens[curr + 1].content = token.content + tokens[curr + 1].content;
} else {
if (curr !== last) { tokens[last] = tokens[curr]; }
if (curr !== last) { tokens[last] = token; }
last++;
}

1
package.json

@ -49,6 +49,7 @@
"@rollup/plugin-node-resolve": "^10.0.0",
"ansi": "^0.3.0",
"autoprefixer-stylus": "^1.0.0",
"benchloop": "^1.3.2",
"benchmark": "~2.1.0",
"chai": "^4.2.0",
"coveralls": "^3.0.4",

Loading…
Cancel
Save