From c888839574b881f3124b310543dee07c124f56b1 Mon Sep 17 00:00:00 2001 From: Fabio Spampinato Date: Sun, 3 Jan 2021 23:27:58 +0100 Subject: [PATCH] Various performance improvements --- benchmark/benchloop.js | 37 ++++++++++++++++++++++++ benchmark/profile.js | 4 ++- lib/common/utils.js | 11 ++++++- lib/renderer.js | 48 ++++++++++++++++++++----------- lib/rules_block/blockquote.js | 6 ++-- lib/rules_block/code.js | 4 +-- lib/rules_block/fence.js | 6 ++-- lib/rules_block/heading.js | 6 ++-- lib/rules_block/hr.js | 9 +++--- lib/rules_block/html_block.js | 8 +++--- lib/rules_block/lheading.js | 6 ++-- lib/rules_block/list.js | 40 ++++++++++++++------------ lib/rules_block/reference.js | 6 ++-- lib/rules_block/state_block.js | 14 ++++----- lib/rules_block/table.js | 10 +++---- lib/rules_core/linkify.js | 4 +-- lib/rules_core/normalize.js | 17 ++++++----- lib/rules_core/smartquotes.js | 2 +- lib/rules_inline/autolink.js | 4 +-- lib/rules_inline/backticks.js | 7 +++-- lib/rules_inline/emphasis.js | 8 +++--- lib/rules_inline/entity.js | 4 +-- lib/rules_inline/escape.js | 4 +-- lib/rules_inline/html_inline.js | 12 ++++---- lib/rules_inline/image.js | 6 ++-- lib/rules_inline/link.js | 4 +-- lib/rules_inline/newline.js | 16 ++++++----- lib/rules_inline/strikethrough.js | 8 +++--- lib/rules_inline/text_collapse.js | 17 +++++------ package.json | 1 + 30 files changed, 202 insertions(+), 127 deletions(-) create mode 100644 benchmark/benchloop.js diff --git a/benchmark/benchloop.js b/benchmark/benchloop.js new file mode 100644 index 0000000..b4f5fe2 --- /dev/null +++ b/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 (); diff --git a/benchmark/profile.js b/benchmark/profile.js index ddc378b..1cd44ac 100755 --- a/benchmark/profile.js +++ b/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'); diff --git a/lib/common/utils.js b/lib/common/utils.js index 712cd29..15f0207 100644 --- a/lib/common/utils.js +++ b/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; diff --git a/lib/renderer.js b/lib/renderer.js index 5a173fe..0b1f700 100644 --- a/lib/renderer.js +++ b/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('' + + highlighted + + '\n'; + } + return '
'
           + highlighted
           + '
\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); } diff --git a/lib/rules_block/blockquote.js b/lib/rules_block/blockquote.js index a02699a..2cb13fc 100644 --- a/lib/rules_block/blockquote.js +++ b/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; } diff --git a/lib/rules_block/code.js b/lib/rules_block/code.js index a83db11..92ab183 100644 --- a/lib/rules_block/code.js +++ b/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) { diff --git a/lib/rules_block/fence.js b/lib/rules_block/fence.js index 44f1538..9d31e6a 100644 --- a/lib/rules_block/fence.js +++ b/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); diff --git a/lib/rules_block/heading.js b/lib/rules_block/heading.js index 9863f48..38c1ae5 100644 --- a/lib/rules_block/heading.js +++ b/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; } diff --git a/lib/rules_block/hr.js b/lib/rules_block/hr.js index a3bb14e..c5f005f 100644 --- a/lib/rules_block/hr.js +++ b/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; }; diff --git a/lib/rules_block/html_block.js b/lib/rules_block/html_block.js index 256b595..cf63b82 100644 --- a/lib/rules_block/html_block.js +++ b/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; } diff --git a/lib/rules_block/lheading.js b/lib/rules_block/lheading.js index 19bdc39..4d51b48 100644 --- a/lib/rules_block/lheading.js +++ b/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 diff --git a/lib/rules_block/list.js b/lib/rules_block/list.js index f6d7c8d..1f21ea1 100644 --- a/lib/rules_block/list.js +++ b/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') { diff --git a/lib/rules_block/reference.js b/lib/rules_block/reference.js index 78daa26..285d75e 100644 --- a/lib/rules_block/reference.js +++ b/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. diff --git a/lib/rules_block/state_block.js b/lib/rules_block/state_block.js index e42cb4b..51e555b 100644 --- a/lib/rules_block/state_block.js +++ b/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 diff --git a/lib/rules_block/table.js b/lib/rules_block/table.js index 89ed65c..e59cd9b 100644 --- a/lib/rules_block/table.js +++ b/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 diff --git a/lib/rules_core/linkify.js b/lib/rules_core/linkify.js index 7c3ffc8..405b4a0 100644 --- a/lib/rules_core/linkify.js +++ b/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)) { diff --git a/lib/rules_core/normalize.js b/lib/rules_core/normalize.js index ad196cd..b1e3dee 100644 --- a/lib/rules_core/normalize.js +++ b/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; }; diff --git a/lib/rules_core/smartquotes.js b/lib/rules_core/smartquotes.js index e96fc71..4ec2dd9 100644 --- a/lib/rules_core/smartquotes.js +++ b/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; } diff --git a/lib/rules_inline/autolink.js b/lib/rules_inline/autolink.js index 66deb90..cd8c6f9 100644 --- a/lib/rules_inline/autolink.js +++ b/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; diff --git a/lib/rules_inline/backticks.js b/lib/rules_inline/backticks.js index b9c9ddb..bd122fc 100644 --- a/lib/rules_inline/backticks.js +++ b/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; diff --git a/lib/rules_inline/emphasis.js b/lib/rules_inline/emphasis.js index c140d2c..7f8bc3d 100644 --- a/lib/rules_inline/emphasis.js +++ b/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++) { diff --git a/lib/rules_inline/entity.js b/lib/rules_inline/entity.js index 6fcc889..ee0beb0 100644 --- a/lib/rules_inline/entity.js +++ b/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); diff --git a/lib/rules_inline/escape.js b/lib/rules_inline/escape.js index 229ead0..d0e38ab 100644 --- a/lib/rules_inline/escape.js +++ b/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++; diff --git a/lib/rules_inline/html_inline.js b/lib/rules_inline/html_inline.js index 28c7980..67e206f 100644 --- a/lib/rules_inline/html_inline.js +++ b/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/* ! */ && diff --git a/lib/rules_inline/image.js b/lib/rules_inline/image.js index 53edd32..9075033 100644 --- a/lib/rules_inline/image.js +++ b/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); diff --git a/lib/rules_inline/link.js b/lib/rules_inline/link.js index 1d242bf..1183e1b 100644 --- a/lib/rules_inline/link.js +++ b/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); diff --git a/lib/rules_inline/newline.js b/lib/rules_inline/newline.js index 14aa42d..f756337 100644 --- a/lib/rules_inline/newline.js +++ b/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); } diff --git a/lib/rules_inline/strikethrough.js b/lib/rules_inline/strikethrough.js index cb8944f..7d45e82 100644 --- a/lib/rules_inline/strikethrough.js +++ b/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); diff --git a/lib/rules_inline/text_collapse.js b/lib/rules_inline/text_collapse.js index 390b0fe..44f5615 100644 --- a/lib/rules_inline/text_collapse.js +++ b/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++; } diff --git a/package.json b/package.json index 22ee3de..28bf2fc 100644 --- a/package.json +++ b/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",