Browse Source

Moved state helpers to State* classes

pull/14/head
Vitaly Puzrin 10 years ago
parent
commit
5bd71f91a7
  1. 87
      lib/helpers.js
  2. 11
      lib/parser_block.js
  3. 3
      lib/parser_ref.js
  4. 7
      lib/rules_block/blockquote.js
  5. 8
      lib/rules_block/code.js
  6. 13
      lib/rules_block/fences.js
  7. 12
      lib/rules_block/heading.js
  8. 7
      lib/rules_block/htmlblock.js
  9. 12
      lib/rules_block/lheading.js
  10. 15
      lib/rules_block/list.js
  11. 6
      lib/rules_block/paragraph.js
  12. 72
      lib/rules_block/state_block.js
  13. 7
      lib/rules_inline/state_inline.js

87
lib/helpers.js

@ -1,87 +0,0 @@
// Common functions for parsers
'use strict';
// Check if line has zero length or contains spaces only
function isEmpty(state, line) {
return state.bMarks[line] + state.tShift[line] >= state.eMarks[line];
}
// Scan lines from given one and return first not empty
function skipEmptyLines(state, from) {
for (var max = state.lineMax; from < max; from++) {
if (state.bMarks[from] + state.tShift[from] < state.eMarks[from]) {
break;
}
}
return from;
}
// Skip spaces from given position.
function skipSpaces(state, pos) {
for (var max = state.src.length; pos < max; pos++) {
if (state.src.charCodeAt(pos) !== 0x20/* space */) { break; }
}
return pos;
}
// Skip char codes from given position
function skipChars(state, pos, code) {
for (var max = state.src.length; pos < max; pos++) {
if (state.src.charCodeAt(pos) !== code) { break; }
}
return pos;
}
// Skip char codes reverse from given position - 1
function skipCharsBack(state, pos, code, min) {
if (pos <= min) { return pos; }
while (pos > min) {
if (code !== state.src.charCodeAt(--pos)) { return pos + 1; }
}
return pos;
}
// cut lines range from source.
function getLines(state, begin, end, indent, keepLastLF) {
var i, first, last, queue,
line = begin;
if (begin >= end) {
return '';
}
// Opt: don't use push queue for single line;
if (line + 1 === end) {
first = state.bMarks[line] + Math.min(state.tShift[line], indent);
last = keepLastLF ? state.bMarks[end] : state.eMarks[end - 1];
return state.src.slice(first, last);
}
queue = new Array(end - begin);
for (i = 0; line < end; line++, i++) {
first = state.bMarks[line] + Math.min(state.tShift[line], indent);
if (line + 1 < end || keepLastLF) {
// No need for bounds check because we have fake entry on tail.
last = state.eMarks[line] + 1;
} else {
last = state.eMarks[line];
}
queue[i] = state.src.slice(first, last);
}
return queue.join('');
}
exports.isEmpty = isEmpty;
exports.skipEmptyLines = skipEmptyLines;
exports.skipSpaces = skipSpaces;
exports.skipChars = skipChars;
exports.getLines = getLines;
exports.skipCharsBack = skipCharsBack;

11
lib/parser_block.js

@ -7,9 +7,6 @@
var Ruler = require('./ruler');
var State = require('./rules_block/state_block');
var skipEmptyLines = require('./helpers').skipEmptyLines;
var isEmpty = require('./helpers').isEmpty;
var rules = [];
@ -60,7 +57,7 @@ ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
hasEmptyLines = false;
while (line < endLine) {
state.line = line = skipEmptyLines(state, line, endLine);
state.line = line = state.skipEmptyLines(line);
if (line >= endLine) { break; }
if (state.tShift[line] < state.blkIndent) { break; }
@ -89,18 +86,18 @@ ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
state.tight = !hasEmptyLines;
// paragraph might "eat" one newline after it in nested lists
if (isEmpty(state, state.line - 1)) {
if (state.isEmpty(state.line - 1)) {
hasEmptyLines = true;
}
line = state.line;
if (line < endLine && isEmpty(state, line)) {
if (line < endLine && state.isEmpty(line)) {
hasEmptyLines = true;
line++;
// two empty lines should stop the parser in list mode
if (line < endLine && state.listMode && isEmpty(state, line)) { break; }
if (line < endLine && state.listMode && state.isEmpty(line)) { break; }
state.line = line;
}
}

3
lib/parser_ref.js

@ -3,7 +3,6 @@
var StateInline = require('./rules_inline/state_inline');
var skipSpaces = require('./helpers').skipSpaces;
var parseLinkLabel = require('./links').parseLinkLabel;
var parseLinkDestination = require('./links').parseLinkDestination;
var parseLinkTitle = require('./links').parseLinkTitle;
@ -58,7 +57,7 @@ module.exports = function parse_reference(str, parser, options, env) {
}
// ensure that the end of the line is empty
pos = skipSpaces(state, pos);
pos = state.skipSpaces(pos);
if (pos < max && state.src.charCodeAt(pos) !== 0x0A) { return -1; }
label = normalizeReference(str.slice(1, labelEnd));

7
lib/rules_block/blockquote.js

@ -3,9 +3,6 @@
'use strict';
var skipSpaces = require('../helpers').skipSpaces;
module.exports = function blockquote(state, startLine, endLine, silent) {
var nextLine, lastLineEmpty, oldTShift, oldBMarks, oldIndent, oldListMode,
terminatorRules = state.parser._rulesBlockquoteTerm, i, l, terminate,
@ -35,7 +32,7 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
state.bMarks[startLine] = pos;
// check if we have an empty blockquote
pos = pos < max ? skipSpaces(state, pos) : pos;
pos = pos < max ? state.skipSpaces(pos) : pos;
lastLineEmpty = pos >= max;
oldTShift = [ state.tShift[startLine] ];
@ -78,7 +75,7 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
oldBMarks.push(state.bMarks[nextLine]);
state.bMarks[nextLine] = pos;
pos = pos < max ? skipSpaces(state, pos) : pos;
pos = pos < max ? state.skipSpaces(pos) : pos;
lastLineEmpty = pos >= max;
oldTShift.push(state.tShift[nextLine]);

8
lib/rules_block/code.js

@ -3,10 +3,6 @@
'use strict';
var isEmpty = require('../helpers').isEmpty;
var getLines = require('../helpers').getLines;
module.exports = function code(state, startLine, endLine, silent) {
var nextLine, last;
@ -16,7 +12,7 @@ module.exports = function code(state, startLine, endLine, silent) {
while (nextLine < endLine) {
if (state.bqMarks[nextLine] < state.bqLevel) { break; }
if (isEmpty(state, nextLine)) {
if (state.isEmpty(nextLine)) {
nextLine++;
continue;
}
@ -32,7 +28,7 @@ module.exports = function code(state, startLine, endLine, silent) {
state.tokens.push({
type: 'code',
content: getLines(state, startLine, last, 4 + state.blkIndent, true),
content: state.getLines(startLine, last, 4 + state.blkIndent, true),
block: true,
level: state.level
});

13
lib/rules_block/fences.js

@ -3,11 +3,6 @@
'use strict';
var skipSpaces = require('../helpers').skipSpaces;
var skipChars = require('../helpers').skipChars;
var getLines = require('../helpers').getLines;
module.exports = function fences(state, startLine, endLine, silent) {
var marker, len, params, nextLine, mem,
haveEndMarker = false,
@ -24,7 +19,7 @@ module.exports = function fences(state, startLine, endLine, silent) {
// scan marker length
mem = pos;
pos = skipChars(state, pos, marker);
pos = state.skipChars(pos, marker);
len = pos - mem;
@ -61,13 +56,13 @@ module.exports = function fences(state, startLine, endLine, silent) {
if (state.src.charCodeAt(pos) !== marker) { continue; }
pos = skipChars(state, pos, marker);
pos = state.skipChars(pos, marker);
// closing code fence must be at least as long as the opening one
if (pos - mem < len) { continue; }
// make sure tail has spaces only
pos = skipSpaces(state, pos);
pos = state.skipSpaces(pos);
if (pos < max) { continue; }
@ -82,7 +77,7 @@ module.exports = function fences(state, startLine, endLine, silent) {
state.tokens.push({
type: 'fence',
params: params,
content: getLines(state, startLine + 1, nextLine, len, true),
content: state.getLines(startLine + 1, nextLine, len, true),
level: state.level
});

12
lib/rules_block/heading.js

@ -3,10 +3,6 @@
'use strict';
var skipSpaces = require('../helpers').skipSpaces;
var skipCharsBack = require('../helpers').skipCharsBack;
module.exports = function heading(state, startLine, endLine, silent) {
var ch, level,
pos = state.bMarks[startLine] + state.tShift[startLine],
@ -29,13 +25,13 @@ module.exports = function heading(state, startLine, endLine, silent) {
if (level > 6 || (pos < max && ch !== 0x20/* space */)) { return false; }
// skip spaces before heading text
pos = skipSpaces(state, pos);
pos = state.skipSpaces(pos);
// Now pos contains offset of first heared char
// Let's cut tails like ' ### ' from the end of string
max = skipCharsBack(state, max, 0x20/* space */, pos);
max = skipCharsBack(state, max, 0x23/* # */, pos);
max = state.skipCharsBack(max, 0x20/* space */, pos);
max = state.skipCharsBack(max, 0x23/* # */, pos);
if (max < state.eMarks[startLine] &&
state.src.charCodeAt(max) === 0x23/* # */ &&
@ -45,7 +41,7 @@ module.exports = function heading(state, startLine, endLine, silent) {
// ## Foo ####
// ^^^
max = skipCharsBack(state, max, 0x20/* space */, pos);
max = state.skipCharsBack(max, 0x20/* space */, pos);
if (silent) { return true; }

7
lib/rules_block/htmlblock.js

@ -3,9 +3,6 @@
'use strict';
var isEmpty = require('../helpers').isEmpty;
var getLines = require('../helpers').getLines;
var block_names = require('../common/html_blocks');
@ -61,14 +58,14 @@ module.exports = function htmlblock(state, startLine, endLine, silent) {
// If we are here - we detected HTML block.
// Let's roll down till empty line (block end).
nextLine = startLine + 1;
while (nextLine < state.lineMax && !isEmpty(state, nextLine)) {
while (nextLine < state.lineMax && !state.isEmpty(nextLine)) {
nextLine++;
}
state.tokens.push({
type: 'htmlblock',
level: state.level,
content: getLines(state, startLine, nextLine, 0, true)
content: state.getLines(startLine, nextLine, 0, true)
});
state.line = nextLine;

12
lib/rules_block/lheading.js

@ -3,11 +3,6 @@
'use strict';
var skipSpaces = require('../helpers').skipSpaces;
var skipChars = require('../helpers').skipChars;
var skipCharsBack = require('../helpers').skipCharsBack;
module.exports = function lheading(state, startLine, endLine, silent) {
var marker, pos, max,
next = startLine + 1;
@ -26,16 +21,15 @@ module.exports = function lheading(state, startLine, endLine, silent) {
if (marker !== 0x2D/* - */ && marker !== 0x3D/* = */) { return false; }
pos = skipChars(state, pos, marker);
pos = state.skipChars(pos, marker);
pos = skipSpaces(state, pos);
pos = state.skipSpaces(pos);
if (pos < max) { return false; }
if (silent) { return true; }
pos = state.bMarks[startLine] + state.tShift[startLine];
max = skipCharsBack(state, state.eMarks[startLine], 0x20/* space */, pos);
state.tokens.push({
type: 'heading_open',
@ -44,7 +38,7 @@ module.exports = function lheading(state, startLine, endLine, silent) {
});
state.tokens.push({
type: 'inline',
content: state.src.slice(pos, max).trim(),
content: state.src.slice(pos, state.eMarks[startLine]).trim(),
level: state.level + 1
});
state.tokens.push({

15
lib/rules_block/list.js

@ -3,10 +3,6 @@
'use strict';
var isEmpty = require('../helpers').isEmpty;
var skipSpaces = require('../helpers').skipSpaces;
// Search `[-+*][\n ]`, returns next pos arter marker on success
// or -1 on fail.
function skipBulletListMarker(state, startLine) {
@ -139,7 +135,7 @@ module.exports = function list(state, startLine, endLine, silent) {
prevEmptyEnd = false;
while (nextLine < endLine) {
contentStart = skipSpaces(state, posAfterMarker);
contentStart = state.skipSpaces(posAfterMarker);
max = state.eMarks[nextLine];
if (contentStart >= max) {
@ -183,7 +179,7 @@ module.exports = function list(state, startLine, endLine, silent) {
}
// Item become loose if finish with empty line,
// but we should filter last element, because it means list finish
prevEmptyEnd = (state.line - startLine) > 1 && isEmpty(state, state.line - 1);
prevEmptyEnd = (state.line - startLine) > 1 && state.isEmpty(state.line - 1);
state.blkIndent = oldIndent;
state.tShift[startLine] = oldTShift;
@ -197,13 +193,8 @@ module.exports = function list(state, startLine, endLine, silent) {
if (nextLine >= endLine) { break; }
if (isEmpty(state, nextLine)) {
if (nextLine >= endLine || isEmpty(state, nextLine)) {
// two newlines end the list
if (state.isEmpty(nextLine)) {
break;
} else {
nextLine++;
}
}
//

6
lib/rules_block/paragraph.js

@ -3,8 +3,6 @@
'use strict';
var isEmpty = require('../helpers').isEmpty;
var getLines = require('../helpers').getLines;
var parseRef = require('../parser_ref');
@ -16,7 +14,7 @@ module.exports = function paragraph(state, startLine/*, endLine*/) {
endLine = state.lineMax;
// jump line-by-line until empty one or EOF
for (; nextLine < endLine && !isEmpty(state, nextLine); nextLine++) {
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.tShift[nextLine] - state.blkIndent > 3) { continue; }
@ -32,7 +30,7 @@ module.exports = function paragraph(state, startLine/*, endLine*/) {
if (terminate) { break; }
}
content = getLines(state, startLine, nextLine, state.blkIndent, false).trim();
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
while (content.length) {
pos = parseRef(content, state.parser.inline, state.options, state.env);

72
lib/rules_block/state_block.js

@ -94,6 +94,78 @@ function State(src, parser, tokens, options, env) {
}
}
State.prototype.isEmpty = function isEmpty(line) {
return this.bMarks[line] + this.tShift[line] >= this.eMarks[line];
};
State.prototype.skipEmptyLines = function skipEmptyLines(from) {
for (var max = this.lineMax; from < max; from++) {
if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) {
break;
}
}
return from;
};
// Skip spaces from given position.
State.prototype.skipSpaces = function skipSpaces(pos) {
for (var max = this.src.length; pos < max; pos++) {
if (this.src.charCodeAt(pos) !== 0x20/* space */) { break; }
}
return pos;
};
// Skip char codes from given position
State.prototype.skipChars = function skipChars(pos, code) {
for (var max = this.src.length; pos < max; pos++) {
if (this.src.charCodeAt(pos) !== code) { break; }
}
return pos;
};
// Skip char codes reverse from given position - 1
State.prototype.skipCharsBack = function skipCharsBack(pos, code, min) {
if (pos <= min) { return pos; }
while (pos > min) {
if (code !== this.src.charCodeAt(--pos)) { return pos + 1; }
}
return pos;
};
// cut lines range from source.
State.prototype.getLines = function getLines(begin, end, indent, keepLastLF) {
var i, first, last, queue,
line = begin;
if (begin >= end) {
return '';
}
// Opt: don't use push queue for single line;
if (line + 1 === end) {
first = this.bMarks[line] + Math.min(this.tShift[line], indent);
last = keepLastLF ? this.bMarks[end] : this.eMarks[end - 1];
return this.src.slice(first, last);
}
queue = new Array(end - begin);
for (i = 0; line < end; line++, i++) {
first = this.bMarks[line] + Math.min(this.tShift[line], indent);
if (line + 1 < end || keepLastLF) {
// No need for bounds check because we have fake entry on tail.
last = this.eMarks[line] + 1;
} else {
last = this.eMarks[line];
}
queue[i] = this.src.slice(first, last);
}
return queue.join('');
};
// Create shadow clone of curent state with new input data
State.prototype.clone = function clone(src) {

7
lib/rules_inline/state_inline.js

@ -42,4 +42,11 @@ StateInline.prototype.push = function (token) {
this.pendingLevel = this.level;
};
StateInline.prototype.skipSpaces = function skipSpaces(pos) {
for (var max = this.src.length; pos < max; pos++) {
if (this.src.charCodeAt(pos) !== 0x20/* space */) { break; }
}
return pos;
};
module.exports = StateInline;

Loading…
Cancel
Save