From 8cf045d94002562352cdb87f9ab9292af4fddb41 Mon Sep 17 00:00:00 2001 From: Vitaly Puzrin Date: Sun, 7 Sep 2014 05:46:17 +0400 Subject: [PATCH] Implemented nested blocks lexing, now block quotes works as expected --- .jshintrc | 2 +- lib/helpers.js | 15 ++++++++++++++- lib/lexer_block/blockquote.js | 16 ++++++++-------- lib/lexer_block/code.js | 6 +++--- lib/lexer_block/fences.js | 4 ++-- lib/lexer_inline.js | 3 +-- lib/parser.js | 1 + lib/renderer.js | 15 +++------------ lib/state.js | 23 +++++++++++++++++++---- 9 files changed, 52 insertions(+), 33 deletions(-) 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