Skip to content

Commit

Permalink
Fix tab behavior inside blockquotes
Browse files Browse the repository at this point in the history
  • Loading branch information
rlidwka committed Sep 15, 2016
1 parent 18dd8e3 commit 7a053ef
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 17 deletions.
98 changes: 87 additions & 11 deletions lib/rules_block/blockquote.js
Expand Up @@ -6,9 +6,25 @@ var isSpace = require('../common/utils').isSpace;


module.exports = function blockquote(state, startLine, endLine, silent) {
var nextLine, lastLineEmpty, oldTShift, oldSCount, oldBMarks, oldIndent, oldParentType, lines, initial, offset, ch,
terminatorRules, token,
i, l, terminate,
var adjustTab,
ch,
i,
initial,
l,
lastLineEmpty,
lines,
nextLine,
offset,
oldBMarks,
oldBSCount,
oldIndent,
oldParentType,
oldSCount,
oldTShift,
spaceAfterMarker,
terminate,
terminatorRules,
token,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];

Expand All @@ -19,15 +35,41 @@ 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; }

// skip one optional space (but not tab, check cmark impl) after '>'
if (state.src.charCodeAt(pos) === 0x20) { pos++; }

oldIndent = state.blkIndent;
state.blkIndent = 0;

// skip spaces after ">" and re-calculate offset
initial = offset = state.sCount[startLine] + pos - (state.bMarks[startLine] + state.tShift[startLine]);

// skip one optional space after '>'
if (state.src.charCodeAt(pos) === 0x20 /* space */) {
// ' > test '
// ^ -- position start of line here:
pos++;
initial++;
offset++;
adjustTab = false;
spaceAfterMarker = true;
} else if (state.src.charCodeAt(pos) === 0x09 /* tab */) {
spaceAfterMarker = true;

if ((state.bsCount[startLine] + offset) % 4 === 3) {
// ' >\t test '
// ^ -- position start of line here (tab has width===1)
pos++;
initial++;
offset++;
adjustTab = false;
} else {
// ' >\t test '
// ^ -- position start of line here + shift bsCount slightly
// to make extra space appear
adjustTab = true;
}
} else {
spaceAfterMarker = false;
}

oldBMarks = [ state.bMarks[startLine] ];
state.bMarks[startLine] = pos;

Expand All @@ -36,7 +78,7 @@ module.exports = function blockquote(state, startLine, endLine, silent) {

if (isSpace(ch)) {
if (ch === 0x09) {
offset += 4 - offset % 4;
offset += 4 - (offset + state.bsCount[startLine] + (adjustTab ? 1 : 0)) % 4;
} else {
offset++;
}
Expand All @@ -47,6 +89,9 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
pos++;
}

oldBSCount = [ state.bsCount[startLine] ];
state.bsCount[startLine] = state.sCount[startLine] + 1 + (spaceAfterMarker ? 1 : 0);

lastLineEmpty = pos >= max;

oldSCount = [ state.sCount[startLine] ];
Expand Down Expand Up @@ -92,12 +137,38 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
if (state.src.charCodeAt(pos++) === 0x3E/* > */) {
// This line is inside the blockquote.

// skip one optional space (but not tab, check cmark impl) after '>'
if (state.src.charCodeAt(pos) === 0x20) { pos++; }

// skip spaces after ">" and re-calculate offset
initial = offset = state.sCount[nextLine] + pos - (state.bMarks[nextLine] + state.tShift[nextLine]);

// skip one optional space after '>'
if (state.src.charCodeAt(pos) === 0x20 /* space */) {
// ' > test '
// ^ -- position start of line here:
pos++;
initial++;
offset++;
adjustTab = false;
spaceAfterMarker = true;
} else if (state.src.charCodeAt(pos) === 0x09 /* tab */) {
spaceAfterMarker = true;

if ((state.bsCount[nextLine] + offset) % 4 === 3) {
// ' >\t test '
// ^ -- position start of line here (tab has width===1)
pos++;
initial++;
offset++;
adjustTab = false;
} else {
// ' >\t test '
// ^ -- position start of line here + shift bsCount slightly
// to make extra space appear
adjustTab = true;
}
} else {
spaceAfterMarker = false;
}

oldBMarks.push(state.bMarks[nextLine]);
state.bMarks[nextLine] = pos;

Expand All @@ -106,7 +177,7 @@ module.exports = function blockquote(state, startLine, endLine, silent) {

if (isSpace(ch)) {
if (ch === 0x09) {
offset += 4 - offset % 4;
offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4;
} else {
offset++;
}
Expand All @@ -119,6 +190,9 @@ module.exports = function blockquote(state, startLine, endLine, silent) {

lastLineEmpty = pos >= max;

oldBSCount.push(state.bsCount[nextLine]);
state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0);

oldSCount.push(state.sCount[nextLine]);
state.sCount[nextLine] = offset - initial;

Expand All @@ -141,6 +215,7 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
if (terminate) { break; }

oldBMarks.push(state.bMarks[nextLine]);
oldBSCount.push(state.bsCount[nextLine]);
oldTShift.push(state.tShift[nextLine]);
oldSCount.push(state.sCount[nextLine]);

Expand All @@ -167,6 +242,7 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
state.bMarks[i + startLine] = oldBMarks[i];
state.tShift[i + startLine] = oldTShift[i];
state.sCount[i + startLine] = oldSCount[i];
state.bsCount[i + startLine] = oldBSCount[i];
}
state.blkIndent = oldIndent;

Expand Down
2 changes: 1 addition & 1 deletion lib/rules_block/list.js
Expand Up @@ -209,7 +209,7 @@ module.exports = function list(state, startLine, endLine, silent) {

if (isSpace(ch)) {
if (ch === 0x09) {
offset += 4 - offset % 4;
offset += 4 - (offset + state.bsCount[nextLine]) % 4;
} else {
offset++;
}
Expand Down
16 changes: 15 additions & 1 deletion lib/rules_block/state_block.js
Expand Up @@ -27,6 +27,18 @@ function StateBlock(src, md, env, tokens) {
this.tShift = []; // offsets of the first non-space characters (tabs not expanded)
this.sCount = []; // indents for each line (tabs expanded)

// An amount of virtual spaces (tabs expanded) between beginning
// of each line (bMarks) and real beginning of that line.
//
// It exists only as a hack because blockquotes override bMarks
// losing information in the process.
//
// It's used only when expanding tabs, you can think about it as
// an initial tab length, e.g. bsCount=21 applied to string `\t123`
// means first tab should be expanded to 4-21%4 === 3 spaces.
//
this.bsCount = [];

// block parser variables
this.blkIndent = 0; // required block content indent
// (for example, if we are in list)
Expand Down Expand Up @@ -73,6 +85,7 @@ function StateBlock(src, md, env, tokens) {
this.eMarks.push(pos);
this.tShift.push(indent);
this.sCount.push(offset);
this.bsCount.push(0);

indent_found = false;
indent = 0;
Expand All @@ -86,6 +99,7 @@ function StateBlock(src, md, env, tokens) {
this.eMarks.push(s.length);
this.tShift.push(0);
this.sCount.push(0);
this.bsCount.push(0);

this.lineMax = this.bMarks.length - 1; // don't count last fake line
}
Expand Down Expand Up @@ -183,7 +197,7 @@ StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF

if (isSpace(ch)) {
if (ch === 0x09) {
lineIndent += 4 - lineIndent % 4;
lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4;
} else {
lineIndent++;
}
Expand Down
84 changes: 80 additions & 4 deletions test/fixtures/markdown-it/commonmark_extras.txt
Expand Up @@ -288,12 +288,88 @@ test

Coverage. Tabs in blockquotes.
.
> foo
> bar
> test

> test

> test

> ---
> test

> ---
> test

> ---
> test

> test

> test

> test

> ---
> test

> ---
> test

> ---
> test
.
<blockquote>
<pre><code> foo
bar
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<pre><code>test
</code></pre>
</blockquote>
<blockquote>
<hr>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<hr>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<hr>
<pre><code>test
</code></pre>
</blockquote>
<blockquote>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<hr>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<hr>
<pre><code> test
</code></pre>
</blockquote>
<blockquote>
<hr>
<pre><code> test
</code></pre>
</blockquote>
.
Expand Down

0 comments on commit 7a053ef

Please sign in to comment.