|
|
@ -3,10 +3,11 @@ |
|
|
|
'use strict'; |
|
|
|
|
|
|
|
var Token = require('../token'); |
|
|
|
var isSpace = require('../common/utils').isSpace; |
|
|
|
|
|
|
|
|
|
|
|
function StateBlock(src, md, env, tokens) { |
|
|
|
var ch, s, start, pos, len, indent, indent_found; |
|
|
|
var ch, s, start, pos, len, indent, offset, indent_found; |
|
|
|
|
|
|
|
this.src = src; |
|
|
|
|
|
|
@ -23,7 +24,8 @@ function StateBlock(src, md, env, tokens) { |
|
|
|
|
|
|
|
this.bMarks = []; // line begin offsets for fast jumps
|
|
|
|
this.eMarks = []; // line end offsets for fast jumps
|
|
|
|
this.tShift = []; // indent for each line
|
|
|
|
this.tShift = []; // offsets of the first non-space characters (tabs not expanded)
|
|
|
|
this.sCount = []; // indents for each line (tabs expanded)
|
|
|
|
|
|
|
|
// block parser variables
|
|
|
|
this.blkIndent = 0; // required block content indent
|
|
|
@ -42,15 +44,20 @@ function StateBlock(src, md, env, tokens) { |
|
|
|
// Create caches
|
|
|
|
// Generate markers.
|
|
|
|
s = this.src; |
|
|
|
indent = 0; |
|
|
|
indent_found = false; |
|
|
|
|
|
|
|
for (start = pos = indent = 0, len = s.length; pos < len; pos++) { |
|
|
|
for (start = pos = indent = offset = 0, len = s.length; pos < len; pos++) { |
|
|
|
ch = s.charCodeAt(pos); |
|
|
|
|
|
|
|
if (!indent_found) { |
|
|
|
if (ch === 0x20/* space */) { |
|
|
|
if (isSpace(ch)) { |
|
|
|
indent++; |
|
|
|
|
|
|
|
if (ch === 0x09) { |
|
|
|
offset += 4 - offset % 4; |
|
|
|
} else { |
|
|
|
offset++; |
|
|
|
} |
|
|
|
continue; |
|
|
|
} else { |
|
|
|
indent_found = true; |
|
|
@ -62,9 +69,11 @@ function StateBlock(src, md, env, tokens) { |
|
|
|
this.bMarks.push(start); |
|
|
|
this.eMarks.push(pos); |
|
|
|
this.tShift.push(indent); |
|
|
|
this.sCount.push(offset); |
|
|
|
|
|
|
|
indent_found = false; |
|
|
|
indent = 0; |
|
|
|
offset = 0; |
|
|
|
start = pos + 1; |
|
|
|
} |
|
|
|
} |
|
|
@ -73,6 +82,7 @@ function StateBlock(src, md, env, tokens) { |
|
|
|
this.bMarks.push(s.length); |
|
|
|
this.eMarks.push(s.length); |
|
|
|
this.tShift.push(0); |
|
|
|
this.sCount.push(0); |
|
|
|
|
|
|
|
this.lineMax = this.bMarks.length - 1; // don't count last fake line
|
|
|
|
} |
|
|
@ -106,8 +116,21 @@ StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) { |
|
|
|
|
|
|
|
// Skip spaces from given position.
|
|
|
|
StateBlock.prototype.skipSpaces = function skipSpaces(pos) { |
|
|
|
var ch; |
|
|
|
|
|
|
|
for (var max = this.src.length; pos < max; pos++) { |
|
|
|
if (this.src.charCodeAt(pos) !== 0x20/* space */) { break; } |
|
|
|
ch = this.src.charCodeAt(pos); |
|
|
|
if (!isSpace(ch)) { break; } |
|
|
|
} |
|
|
|
return pos; |
|
|
|
}; |
|
|
|
|
|
|
|
// Skip spaces from given position in reverse.
|
|
|
|
StateBlock.prototype.skipSpacesBack = function skipSpacesBack(pos, min) { |
|
|
|
if (pos <= min) { return pos; } |
|
|
|
|
|
|
|
while (pos > min) { |
|
|
|
if (!isSpace(this.src.charCodeAt(--pos))) { return pos + 1; } |
|
|
|
} |
|
|
|
return pos; |
|
|
|
}; |
|
|
@ -132,28 +155,18 @@ StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) { |
|
|
|
|
|
|
|
// cut lines range from source.
|
|
|
|
StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) { |
|
|
|
var i, first, last, queue, shift, |
|
|
|
var i, lineIndent, ch, first, last, queue, lineStart, |
|
|
|
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 = this.eMarks[end - 1] + (keepLastLF ? 1 : 0); |
|
|
|
return this.src.slice(first, last); |
|
|
|
} |
|
|
|
|
|
|
|
queue = new Array(end - begin); |
|
|
|
|
|
|
|
for (i = 0; line < end; line++, i++) { |
|
|
|
shift = this.tShift[line]; |
|
|
|
if (shift > indent) { shift = indent; } |
|
|
|
if (shift < 0) { shift = 0; } |
|
|
|
|
|
|
|
first = this.bMarks[line] + shift; |
|
|
|
lineIndent = 0; |
|
|
|
lineStart = first = this.bMarks[line]; |
|
|
|
|
|
|
|
if (line + 1 < end || keepLastLF) { |
|
|
|
// No need for bounds check because we have fake entry on tail.
|
|
|
@ -162,6 +175,25 @@ StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF |
|
|
|
last = this.eMarks[line]; |
|
|
|
} |
|
|
|
|
|
|
|
while (first < last && lineIndent < indent) { |
|
|
|
ch = this.src.charCodeAt(first); |
|
|
|
|
|
|
|
if (isSpace(ch)) { |
|
|
|
if (ch === 0x09) { |
|
|
|
lineIndent += 4 - lineIndent % 4; |
|
|
|
} else { |
|
|
|
lineIndent++; |
|
|
|
} |
|
|
|
} else if (first - lineStart < this.tShift[line]) { |
|
|
|
// patched tShift masked characters to look like spaces (blockquotes, list markers)
|
|
|
|
lineIndent++; |
|
|
|
} else { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
first++; |
|
|
|
} |
|
|
|
|
|
|
|
queue[i] = this.src.slice(first, last); |
|
|
|
} |
|
|
|
|
|
|
|