Skip to content

Commit

Permalink
Bring pipe escape algorithm in tables closer to gfm
Browse files Browse the repository at this point in the history
close #689
close #697
  • Loading branch information
rlidwka committed Sep 14, 2020
1 parent 331ae11 commit c61f105
Show file tree
Hide file tree
Showing 2 changed files with 288 additions and 145 deletions.
98 changes: 56 additions & 42 deletions lib/rules_block/table.js
@@ -1,4 +1,4 @@
// GFM table, non-standard
// GFM table, https://github.github.com/gfm/#tables-extension-

'use strict';

Expand All @@ -17,56 +17,42 @@ function escapedSplit(str) {
pos = 0,
max = str.length,
ch,
escapes = 0,
isEscaped = false,
lastPos = 0,
backTicked = false,
lastBackTick = 0;
current = '';

ch = str.charCodeAt(pos);

while (pos < max) {
if (ch === 0x60/* ` */) {
if (backTicked) {
// make \` close code sequence, but not open it;
// the reason is: `\` is correct code block
backTicked = false;
lastBackTick = pos;
} else if (escapes % 2 === 0) {
backTicked = true;
lastBackTick = pos;
if (ch === 0x7c/* | */) {
if (!isEscaped) {
// pipe separating cells, '|'
result.push(current + str.substring(lastPos, pos));
current = '';
lastPos = pos + 1;
} else {
// escaped pipe, '\|'
current += str.substring(lastPos, pos - 1);
lastPos = pos;
}
} else if (ch === 0x7c/* | */ && (escapes % 2 === 0) && !backTicked) {
result.push(str.substring(lastPos, pos));
lastPos = pos + 1;
}

if (ch === 0x5c/* \ */) {
escapes++;
} else {
escapes = 0;
}

isEscaped = (ch === 0x5c/* \ */);
pos++;

// If there was an un-closed backtick, go back to just after
// the last backtick, but as if it was a normal character
if (pos === max && backTicked) {
backTicked = false;
pos = lastBackTick + 1;
}

ch = str.charCodeAt(pos);
}

result.push(str.substring(lastPos));
result.push(current + str.substring(lastPos));

return result;
}


module.exports = function table(state, startLine, endLine, silent) {
var ch, lineText, pos, i, nextLine, columns, columnCount, token,
aligns, t, tableLines, tbodyLines;
var ch, lineText, pos, i, l, nextLine, columns, columnCount, token,
aligns, t, tableLines, tbodyLines, oldParentType, terminate,
terminatorRules;

// should have at least two lines
if (startLine + 2 > endLine) { return false; }
Expand Down Expand Up @@ -125,15 +111,24 @@ module.exports = function table(state, startLine, endLine, silent) {
lineText = getLine(state, startLine).trim();
if (lineText.indexOf('|') === -1) { return false; }
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
columns = escapedSplit(lineText.replace(/^\||\|$/g, ''));
columns = escapedSplit(lineText);
if (columns.length && columns[0] === '') columns.shift();
if (columns.length && columns[columns.length - 1] === '') columns.pop();

// header row will define an amount of columns in the entire table,
// and align row shouldn't be smaller than that (the rest of the rows can)
// and align row should be exactly the same (the rest of the rows can differ)
columnCount = columns.length;
if (columnCount > aligns.length) { return false; }
if (columnCount !== aligns.length) { return false; }

if (silent) { return true; }

oldParentType = state.parentType;
state.parentType = 'table';

// use 'blockquote' lists for termination because it's
// the most similar to tables
terminatorRules = state.md.block.ruler.getRules('blockquote');

token = state.push('table_open', 'table', 1);
token.map = tableLines = [ startLine, 0 ];

Expand Down Expand Up @@ -161,16 +156,29 @@ module.exports = function table(state, startLine, endLine, silent) {
token = state.push('tr_close', 'tr', -1);
token = state.push('thead_close', 'thead', -1);

token = state.push('tbody_open', 'tbody', 1);
token.map = tbodyLines = [ startLine + 2, 0 ];

for (nextLine = startLine + 2; nextLine < endLine; nextLine++) {
if (state.sCount[nextLine] < state.blkIndent) { break; }

terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
}
}

if (terminate) { break; }
lineText = getLine(state, nextLine).trim();
if (lineText.indexOf('|') === -1) { break; }
if (!lineText) { break; }
if (state.sCount[nextLine] - state.blkIndent >= 4) { break; }
columns = escapedSplit(lineText.replace(/^\||\|$/g, ''));
columns = escapedSplit(lineText);
if (columns.length && columns[0] === '') columns.shift();
if (columns.length && columns[columns.length - 1] === '') columns.pop();

if (nextLine === startLine + 2) {
token = state.push('tbody_open', 'tbody', 1);
token.map = tbodyLines = [ startLine + 2, 0 ];
}

token = state.push('tr_open', 'tr', 1);
for (i = 0; i < columnCount; i++) {
Expand All @@ -189,10 +197,16 @@ module.exports = function table(state, startLine, endLine, silent) {
}
token = state.push('tr_close', 'tr', -1);
}
token = state.push('tbody_close', 'tbody', -1);

if (tbodyLines) {
token = state.push('tbody_close', 'tbody', -1);
tbodyLines[1] = nextLine;
}

token = state.push('table_close', 'table', -1);
tableLines[1] = nextLine;

tableLines[1] = tbodyLines[1] = nextLine;
state.parentType = oldParentType;
state.line = nextLine;
return true;
};

0 comments on commit c61f105

Please sign in to comment.