Browse Source

Better algorithm for parsing block quotes

pull/14/head
Alex Kocharin 10 years ago
committed by Vitaly Puzrin
parent
commit
470bfecbd4
  1. 9
      lib/lexer_block.js
  2. 79
      lib/lexer_block/blockquote.js
  3. 6
      lib/lexer_block/fences.js
  4. 2
      lib/lexer_block/lheading.js
  5. 7
      lib/lexer_block/paragraph.js
  6. 4
      lib/parser.js

9
lib/lexer_block.js

@ -4,7 +4,6 @@
'use strict';
var isEmpty = require('./helpers').isEmpty;
var skipEmptyLines = require('./helpers').skipEmptyLines;
@ -140,15 +139,7 @@ LexerBlock.prototype.tokenize = function (state, startLine, endLine) {
if (line === state.line) {
throw new Error('None of rules updated state.line');
}
line = state.line;
if (isEmpty(state, line)) {
hasEmptyLines = true;
line++;
// two empty lines should stop the parser
if (isEmpty(state, line + 1)) { break; }
}
}
state.tight = !hasEmptyLines;

79
lib/lexer_block/blockquote.js

@ -3,13 +3,12 @@
'use strict';
var skipSpaces = require('../helpers').skipSpaces;
var getLines = require('../helpers').getLines;
var getLines = require('../helpers').getLines;
var isEmpty = require('../helpers').isEmpty;
module.exports = function blockquote(state, startLine, endLine, silent) {
var nextLine, lastLineEmpty, subState,
rules_named = state.lexerBlock.rules_named,
var nextLine, subState, insideLines, lineMax,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
@ -25,63 +24,37 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
// so no point trying to find the end of it in silent mode
if (silent) { return true; }
// check if we have an empty blockquote
pos = pos < max ? skipSpaces(state, pos) : pos;
lastLineEmpty = pos >= max;
// Search the end of the block
//
// Block ends with either:
// 1. an empty line outside:
// ```
// > test
//
// ```
// 2. an empty line inside:
// ```
// >
// test
// ```
// 3. another tag
// ```
// > test
// - - -
// ```
for (nextLine = startLine + 1; nextLine < endLine; nextLine++) {
lineMax = state.lineMax;
insideLines = 1;
state.tokens.push({ type: 'blockquote_open' });
for (nextLine = startLine + 1; nextLine < lineMax; ) {
pos = state.bMarks[nextLine] + state.tShift[nextLine];
max = state.eMarks[nextLine];
if (pos >= max) {
// Case 1: line is not inside the blockquote, and this line is empty.
break;
if (pos < max && state.src.charCodeAt(pos++) === 0x3E/* > */) {
if (nextLine < endLine) {
nextLine++;
insideLines++;
continue;
} else {
break;
}
}
if (state.src.charCodeAt(pos++) === 0x3E/* > */) {
// This line is inside the blockquote.
pos = pos < max ? skipSpaces(state, pos) : pos;
lastLineEmpty = pos >= max;
continue;
if (insideLines === 0) {
break;
}
// Case 2: line is not inside the blockquote, and the last line was empty.
if (lastLineEmpty) { break; }
// Case 3: another tag found.
if (rules_named.fences(state, nextLine, endLine, true)) { break; }
if (rules_named.hr(state, nextLine, endLine, true)) { break; }
if (rules_named.list(state, nextLine, endLine, true)) { break; }
if (rules_named.heading(state, nextLine, endLine, true)) { break; }
if (rules_named.lheading(state, nextLine, endLine, true)) { break; }
if (rules_named.table(state, nextLine, endLine, true)) { break; }
//if (rules_named.tag(state, nextLine, endLine, true)) { break; }
//if (rules_named.def(state, nextLine, endLine, true)) { break; }
while (nextLine < lineMax) {
if (isEmpty(state, nextLine)) { break; }
nextLine++;
}
subState = state.clone(getLines(state, startLine, nextLine, true)
.replace(/^ {0,3}> ?/mg, ''));
state.lexerBlock.tokenize(subState, 0, insideLines);
nextLine = startLine = subState.line + startLine;
insideLines = 0;
}
state.tokens.push({ type: 'blockquote_open' });
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 = nextLine;

6
lib/lexer_block/fences.js

@ -40,9 +40,7 @@ module.exports = function fences(state, startLine, endLine, silent) {
nextLine = startLine;
for (;;) {
nextLine++;
if (nextLine >= endLine) {
if (nextLine + 1 >= endLine) {
// unclosed block should be autoclosed by end of document.
// also block seems to be autoclosed by end of parent
/*if (state.blkLevel === 0) {
@ -52,6 +50,8 @@ module.exports = function fences(state, startLine, endLine, silent) {
break;
}
nextLine++;
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine];
max = state.eMarks[nextLine];

2
lib/lexer_block/lheading.js

@ -12,7 +12,7 @@ module.exports = function lheading(state, startLine, endLine, silent) {
var marker, pos, max,
next = startLine + 1;
if (next >= state.lineMax) { return false; }
if (next >= endLine) { return false; }
// Scan next line
if (state.tShift[next] > 3) { return false; }

7
lib/lexer_block/paragraph.js

@ -6,10 +6,13 @@
var isEmpty = require('../helpers').isEmpty;
module.exports = function paragraph(state, startLine, endLine) {
var nextLine = startLine + 1,
module.exports = function paragraph(state, startLine/*, endLine*/) {
var endLine,
nextLine = startLine + 1,
rules_named = state.lexerBlock.rules_named;
endLine = state.lineMax;
// jump line-by-line until empty one or EOF
while (nextLine < endLine && !isEmpty(state, nextLine)) {
// Some tags can terminate paragraph without empty line.

4
lib/parser.js

@ -74,9 +74,7 @@ Parser.prototype.render = function (src) {
this.options
);
while (state.line < state.lineMax) {
state.lexerBlock.tokenize(state, state.line, state.lineMax);
}
state.lexerBlock.tokenize(state, state.line, state.lineMax);
return this.renderer.render(state);
};

Loading…
Cancel
Save