From 5bd71f91a75817146c91857c7fbda85907cb29c6 Mon Sep 17 00:00:00 2001 From: Vitaly Puzrin Date: Sat, 11 Oct 2014 11:43:33 +0400 Subject: [PATCH] Moved state helpers to State* classes --- lib/helpers.js | 87 -------------------------------- lib/parser_block.js | 11 ++-- lib/parser_ref.js | 3 +- lib/rules_block/blockquote.js | 7 +-- lib/rules_block/code.js | 8 +-- lib/rules_block/fences.js | 13 ++--- lib/rules_block/heading.js | 12 ++--- lib/rules_block/htmlblock.js | 7 +-- lib/rules_block/lheading.js | 12 ++--- lib/rules_block/list.js | 17 ++----- lib/rules_block/paragraph.js | 6 +-- lib/rules_block/state_block.js | 72 ++++++++++++++++++++++++++ lib/rules_inline/state_inline.js | 7 +++ 13 files changed, 107 insertions(+), 155 deletions(-) delete mode 100644 lib/helpers.js diff --git a/lib/helpers.js b/lib/helpers.js deleted file mode 100644 index 449ea71..0000000 --- a/lib/helpers.js +++ /dev/null @@ -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; diff --git a/lib/parser_block.js b/lib/parser_block.js index bd71f7a..8276823 100644 --- a/lib/parser_block.js +++ b/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; } } diff --git a/lib/parser_ref.js b/lib/parser_ref.js index 18b3350..7aa9039 100644 --- a/lib/parser_ref.js +++ b/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)); diff --git a/lib/rules_block/blockquote.js b/lib/rules_block/blockquote.js index f4391a4..1964afb 100644 --- a/lib/rules_block/blockquote.js +++ b/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]); diff --git a/lib/rules_block/code.js b/lib/rules_block/code.js index 794a237..3c68f33 100644 --- a/lib/rules_block/code.js +++ b/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 }); diff --git a/lib/rules_block/fences.js b/lib/rules_block/fences.js index fd9ef07..b763d31 100644 --- a/lib/rules_block/fences.js +++ b/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 }); diff --git a/lib/rules_block/heading.js b/lib/rules_block/heading.js index c664aa1..2123530 100644 --- a/lib/rules_block/heading.js +++ b/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; } diff --git a/lib/rules_block/htmlblock.js b/lib/rules_block/htmlblock.js index ba6c4a8..9d4ef96 100644 --- a/lib/rules_block/htmlblock.js +++ b/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; diff --git a/lib/rules_block/lheading.js b/lib/rules_block/lheading.js index fac87c0..42a1ffb 100644 --- a/lib/rules_block/lheading.js +++ b/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({ diff --git a/lib/rules_block/list.js b/lib/rules_block/list.js index 8de67b7..6dea29b 100644 --- a/lib/rules_block/list.js +++ b/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 - break; - } else { - nextLine++; - } + if (state.isEmpty(nextLine)) { + break; } // diff --git a/lib/rules_block/paragraph.js b/lib/rules_block/paragraph.js index 25d92b7..5e5e4d5 100644 --- a/lib/rules_block/paragraph.js +++ b/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); diff --git a/lib/rules_block/state_block.js b/lib/rules_block/state_block.js index d4e2ea1..288a996 100644 --- a/lib/rules_block/state_block.js +++ b/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) { diff --git a/lib/rules_inline/state_inline.js b/lib/rules_inline/state_inline.js index 1ba5c66..40be36a 100644 --- a/lib/rules_inline/state_inline.js +++ b/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;