diff --git a/.jshintrc b/.jshintrc index 53e95eb..6364b4a 100644 --- a/.jshintrc +++ b/.jshintrc @@ -22,7 +22,7 @@ "unused" : false, // This option warns when you define and never use your variables. "strict" : true, // Require `use strict` pragma in every file. "trailing" : true, // Prohibit trailing whitespaces. - "maxparams" : 5, // Enforce max number of formal parameters allowed per function + "maxparams" : 6, // Enforce max number of formal parameters allowed per function "maxdepth" : 5, // Enforce max depth of nested blocks "maxstatements" : false, // Enforce max amount of statements per function "maxcomplexity" : false, // Enforce cyclomatic complexity level diff --git a/lib/helpers.js b/lib/helpers.js index b09c958..fd8d6bf 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -46,9 +46,22 @@ function skipChars(state, pos, code) { return pos; }*/ +// cut lines range from source. +function getLines(state, begin, end, keepLastLF) { + var last; + + if (keepLastLF) { + last = end < state.lineMax ? state.bMarks[end] : state.src.length; + } else { + last = end < state.lineMax ? state.eMarks[end - 1] : state.src.length; + } + + return state.src.slice(state.bMarks[begin], last); +} exports.isWhiteSpace = isWhiteSpace; exports.isEmpty = isEmpty; exports.skipEmptyLines = skipEmptyLines; exports.skipSpaces = skipSpaces; -exports.skipChars = skipChars; \ No newline at end of file +exports.skipChars = skipChars; +exports.getLines = getLines; \ No newline at end of file diff --git a/lib/lexer_block/blockquote.js b/lib/lexer_block/blockquote.js index 0eb9e91..73f0471 100644 --- a/lib/lexer_block/blockquote.js +++ b/lib/lexer_block/blockquote.js @@ -3,12 +3,13 @@ 'use strict'; -var skipEmptyLines = require('../helpers').skipEmptyLines; -var skipSpaces = require('../helpers').skipSpaces; +var skipEmptyLines = require('../helpers').skipEmptyLines; +var skipSpaces = require('../helpers').skipSpaces; +var getLines = require('../helpers').getLines; module.exports = function blockquote(state, startLine, endLine, silent) { - var marker, nextLine, oldBMarks, lastLineEmpty, + var marker, nextLine, oldBMarks, lastLineEmpty, subState, rules_named = state.lexerBlock.rules_named, pos = state.bMarks[startLine] + state.tShift[startLine], max = state.eMarks[startLine]; @@ -75,11 +76,10 @@ module.exports = function blockquote(state, startLine, endLine, silent) { } state.tokens.push({ type: 'blockquote_open' }); - state.lexerInline.tokenize( - state, - state.bMarks[startLine], - state.eMarks[nextLine - 1] - ); + + subState = state.clone(getLines(state, startLine, nextLine, true).replace(/^ {0,3}> ?/mg, '')); + state.lexerBlock.tokenize(subState, 0, subState.lineMax); + state.tokens.push({ type: 'blockquote_close' }); state.line = skipEmptyLines(state, nextLine); diff --git a/lib/lexer_block/code.js b/lib/lexer_block/code.js index bd01948..5c11980 100644 --- a/lib/lexer_block/code.js +++ b/lib/lexer_block/code.js @@ -3,7 +3,8 @@ 'use strict'; -var isEmpty = require('../helpers').isEmpty; +var isEmpty = require('../helpers').isEmpty; +var getLines = require('../helpers').getLines; module.exports = function code(state, startLine, endLine, silent) { @@ -33,8 +34,7 @@ module.exports = function code(state, startLine, endLine, silent) { state.tokens.push({ type: 'code', - startLine: startLine, - endLine: last + content: getLines(state, startLine, last, true).replace(/^ {4}/gm, '') }); state.line = nextLine; diff --git a/lib/lexer_block/fences.js b/lib/lexer_block/fences.js index 4cba9ab..93fb719 100644 --- a/lib/lexer_block/fences.js +++ b/lib/lexer_block/fences.js @@ -6,6 +6,7 @@ var skipEmptyLines = require('../helpers').skipEmptyLines; var skipSpaces = require('../helpers').skipSpaces; var skipChars = require('../helpers').skipChars; +var getLines = require('../helpers').getLines; module.exports =function fences(state, startLine, endLine, silent) { @@ -70,8 +71,7 @@ module.exports =function fences(state, startLine, endLine, silent) { state.tokens.push({ type: 'fence', params: params ? params.split(/\s+/g) : [], - startLine: startLine + 1, - endLine: nextLine + content: getLines(state, startLine + 1, nextLine, true) }); state.line = skipEmptyLines(state, nextLine + 1); diff --git a/lib/lexer_inline.js b/lib/lexer_inline.js index 72e4ce5..3a05099 100644 --- a/lib/lexer_inline.js +++ b/lib/lexer_inline.js @@ -13,8 +13,7 @@ var rules = []; rules.push(function text(state, begin, end) { state.tokens.push({ type: 'text', - begin: begin, - end: end + content: state.src.slice(begin, end) }); state.pos = end; diff --git a/lib/parser.js b/lib/parser.js index 8496343..2501139 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -40,6 +40,7 @@ Parser.prototype.render = function (src) { this.lexerBlock, this.lexerInline, this.renderer, + [], this.options ); diff --git a/lib/renderer.js b/lib/renderer.js index a1ab94d..5431769 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -14,12 +14,6 @@ function unescapeMd(str) { return str.replace(MD_UNESCAPE_RE, '$1'); } -function joinLines(state, begin, end) { - return state.src.slice( - state.bMarks[begin], - end < state.lineMax ? state.bMarks[end] : state.src.length - ); -} var rules = {}; @@ -41,14 +35,11 @@ rules.bullet_list_close = function (state, token) { rules.code = function (state, token) { - var content = joinLines(state, token.startLine, token.endLine).replace(/^ {4}/gm, ''); - - state.result += '
' + escapeHtml(content) + '\n';
+ state.result += '' + escapeHtml(token.content) + '\n';
};
rules.fence = function (state, token) {
- var content = joinLines(state, token.startLine, token.endLine);
var langMark = '';
var langPrefix = state.options.codeLangPrefix || '';
@@ -56,7 +47,7 @@ rules.fence = function (state, token) {
langMark = ' class="' + langPrefix + escapeHtml(token.params[0]) + '"';
}
- state.result += '' + escapeHtml(content) + '\n';
+ state.result += '' + escapeHtml(token.content) + '\n';
};
@@ -90,7 +81,7 @@ rules.paragraph_close = function (state, token) {
rules.text = function (state, token) {
- state.result += escapeHtml(unescapeMd(state.src.slice(token.begin, token.end)));
+ state.result += escapeHtml(unescapeMd(token.content));
};
diff --git a/lib/state.js b/lib/state.js
index 3f460a3..5669502 100644
--- a/lib/state.js
+++ b/lib/state.js
@@ -3,11 +3,14 @@
'use strict';
-function State(src, lexerBlock, lexerInline, renderer, options) {
+function State(src, lexerBlock, lexerInline, renderer, tokens, options) {
var ch, s, start, pos, len, indent, indent_found;
- // TODO: Temporary solution. Check if more effective possible,
- // withous str change
+ // TODO: check if we can move string replaces to parser, to avoid
+ // unnesessary call on shadow clone creation. Or check if we can do
+ // cloning more effectively. Profile first.
+
+ // Prepare string to parse:
//
// - replace tabs with spaces
// - remove `\r` to simplify newlines check (???)
@@ -30,7 +33,7 @@ function State(src, lexerBlock, lexerInline, renderer, options) {
// Internal state vartiables
//
- this.tokens = [];
+ this.tokens = tokens;
this.bMarks = []; // line begin offsets for fast jumps
this.eMarks = []; // line end offsets for fast jumps
@@ -86,4 +89,16 @@ function State(src, lexerBlock, lexerInline, renderer, options) {
}
+// Create shadow clone of curent state with new input data
+State.prototype.clone = function clone(src) {
+ return new State(
+ src,
+ this.lexerBlock,
+ this.lexerInline,
+ this.renderer,
+ this.tokens,
+ this.options
+ );
+};
+
module.exports = State;
\ No newline at end of file