Skip to content

Commit

Permalink
Fix #68 escaping pipes in tables
Browse files Browse the repository at this point in the history
  • Loading branch information
ariabuckles committed Aug 5, 2019
1 parent e8e4048 commit f32a628
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 72 deletions.
80 changes: 80 additions & 0 deletions __tests__/simple-markdown-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2632,6 +2632,86 @@ describe("simple markdown", function() {
validateParse(parsedNp, expected);
});

it("should allow escaping pipes inside tables", function() {
var expected = [{
type: "table",
header: [
[
{type: "text", content: '|'},
{type: "text", content: 'Attribute'},
{type: "text", content: '|'},
],
[
{type: "text", content: '|'},
{type: "text", content: 'Type'},
{type: "text", content: '|'},
],
],
align: [null, null],
cells: [[
[
{type: "text", content: "pos"},
{type: "text", content: "|"},
{type: "text", content: "position"}
],
[
{type: "text", content: '"left'},
{type: "text", content: '" '},
{type: "text", content: '|'},
{type: "text", content: ' '},
{type: "text", content: '"right'},
{type: "text", content: '"'}
],
]]
}];

var parsedPiped = blockParse(
'| \\|Attribute\\| | \\|Type\\| |\n' +
'| --------------- | ------------------ |\n' +
'| pos\\|position | "left" \\| "right" |\n' +
'\n'
);
validateParse(parsedPiped, expected);

var parsedNp = blockParse(
'\\|Attribute\\| | \\|Type\\| \n' +
'--------------- | ------------------\n' +
'pos\\|position | "left" \\| "right"\n' +
'\n'
);
validateParse(parsedNp, expected);
});

it("should allow pipes in code inside tables", function() {
var expected = [{
type: "table",
header: [
[{type: "text", content: 'Attribute'}],
[{type: "text", content: 'Type'}],
],
align: [null, null],
cells: [[
[{type: "inlineCode", content: "position"}],
[{type: "inlineCode", content: '"left" | "right"'}],
]]
}];

var parsedPiped = blockParse(
'| Attribute | Type |\n' +
'| ------------ | --------------------- |\n' +
'| `position` | `"left" | "right"` |\n' +
'\n'
);
validateParse(parsedPiped, expected);

var parsedNp = blockParse(
'Attribute | Type \n' +
'------------ | ---------------------\n' +
'`position` | `"left" | "right"`\n' +
'\n'
);
validateParse(parsedNp, expected);
});

it("should be able to parse <br>s", function() {
// Inside a paragraph:
Expand Down
141 changes: 69 additions & 72 deletions simple-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ type MatchFunction = (
type Parser = (
source: string,
state?: ?State
) => ASTNode;
) => Array<SingleASTNode>;
type ParseFunction = (
capture: Capture,
Expand Down Expand Up @@ -660,12 +660,9 @@ var TABLES = (function() {
// predefine regexes so we don't have to create them inside functions
// sure, regex literals should be fast, even inside functions, but they
// aren't in all browsers.
var TABLE_HEADER_TRIM = /^ *| *\| *$/g;
var TABLE_CELLS_TRIM = /\n+$/;
var PLAIN_TABLE_ROW_TRIM = /^ *\| *| *\| *$/g;
var NPTABLE_ROW_TRIM = /^ *| *$/g;
var TABLE_ROW_SPLIT = / *\| */;

var TABLE_BLOCK_TRIM = /\n+/g;
var TABLE_ROW_SEPARATOR_TRIM = /^ *\| *| *\| *$/g;
var TABLE_CELL_END_TRIM = / *$/;
var TABLE_RIGHT_ALIGN = /^ *-+: *$/;
var TABLE_CENTER_ALIGN = /^ *:-+: *$/;
var TABLE_LEFT_ALIGN = /^ *:-+ *$/;
Expand All @@ -682,84 +679,71 @@ var TABLES = (function() {
}
};

var parseTableHeader = function(trimRegex, capture, parse, state) {
var headerText = capture[1]
.replace(trimRegex, "")
.split(TABLE_ROW_SPLIT);
return headerText.map(function(text) {
return parse(text, state);
});
};

var parseTableAlign = function(trimRegex, capture, parse, state) {
var alignText = capture[2]
.replace(trimRegex, "")
.split(TABLE_ROW_SPLIT);

var parseTableAlign = function(source, parse, state, trimEndSeparators) {
if (trimEndSeparators) {
source = source.replace(TABLE_ROW_SEPARATOR_TRIM, "");
}
var alignText = source.trim().split("|");
return alignText.map(parseTableAlignCapture);
};

var parseTableCells = function(capture, parse, state) {
var rowsText = capture[3]
.replace(TABLE_CELLS_TRIM, "")
.split("\n");

return rowsText.map(function(rowText) {
var cellText = rowText
.replace(PLAIN_TABLE_ROW_TRIM, "")
.split(TABLE_ROW_SPLIT);
return cellText.map(function(text) {
return parse(text, state);
});
var parseTableRow = function(source, parse, state, trimEndSeparators) {
var prevInTable = state.inTable;
state.inTable = true;
var tableRow = parse(source.trim(), state);
state.inTable = prevInTable;

var cells = [[]];
tableRow.forEach(function(node, i) {
if (node.type === 'tableSeparator') {
// Filter out empty table separators at the start/end:
if (!trimEndSeparators || i !== 0 && i !== tableRow.length - 1) {
// Split the current row:
cells.push([]);
}
} else {
if (node.type === 'text' && (
tableRow[i + 1] == null ||
tableRow[i + 1].type === 'tableSeparator'
)) {
node.content = node.content.replace(TABLE_CELL_END_TRIM, "");
}
cells[cells.length - 1].push(node);
}
});

return cells;
};

var parseNpTableCells = function(capture, parse, state) {
var rowsText = capture[3]
.replace(TABLE_CELLS_TRIM, "")
.split("\n");
var parseTableCells = function(source, parse, state, trimEndSeparators) {
var rowsText = source.trim().split("\n");

return rowsText.map(function(rowText) {
var cellText = rowText.split(TABLE_ROW_SPLIT);
return cellText.map(function(text) {
return parse(text, state);
});
return parseTableRow(rowText, parse, state, trimEndSeparators);
});
};

var parseTable = function(capture, parse, state) {
state.inline = true;
var header = parseTableHeader(TABLE_HEADER_TRIM, capture, parse, state);
var align = parseTableAlign(TABLE_HEADER_TRIM, capture, parse, state);
var cells = parseTableCells(capture, parse, state);
state.inline = false;

return {
type: "table",
header: header,
align: align,
cells: cells
};
};
var parseTable = function(trimEndSeparators) {
return function(capture, parse, state) {
state.inline = true;
var header = parseTableRow(capture[1], parse, state, trimEndSeparators);
var align = parseTableAlign(capture[2], parse, state, trimEndSeparators);
var cells = parseTableCells(capture[3], parse, state, trimEndSeparators);
state.inline = false;

var parseNpTable = function(capture, parse, state) {
state.inline = true;
var header = parseTableHeader(NPTABLE_ROW_TRIM, capture, parse, state);
var align = parseTableAlign(NPTABLE_ROW_TRIM, capture, parse, state);
var cells = parseNpTableCells(capture, parse, state);
state.inline = false;

return {
type: "table",
header: header,
align: align,
cells: cells
return {
type: "table",
header: header,
align: align,
cells: cells
};
};
};

return {
parseTable: parseTable,
parseNpTable: parseNpTable,
parseTable: parseTable(true),
parseNpTable: parseTable(false),
TABLE_REGEX: /^ *(\|.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/,
NPTABLE_REGEX: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/
};
})();
Expand Down Expand Up @@ -1175,9 +1159,7 @@ var defaultRules /* : DefaultRules */ = {
},
table: {
order: currOrder++,
match: blockRegex(
/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
),
match: blockRegex(TABLES.TABLE_REGEX),
parse: TABLES.parseTable,
react: function(node, output, state) {
var getStyle = function(colIndex) {
Expand Down Expand Up @@ -1313,6 +1295,21 @@ var defaultRules /* : DefaultRules */ = {
react: null,
html: null
},
tableSeparator: {
order: currOrder++,
match: function(source, state) {
if (!state.inTable) {
return null;
}
return /^ *\| */.exec(source);
},
parse: function() {
return { type: 'tableSeparator' };
},
// These shouldn't be reached, but in case they are, be reasonable:
react: function() { return ' | '; },
html: function() { return ' &vert; '; },
},
autolink: {
order: currOrder++,
match: inlineRegex(/^<([^ >]+:\/[^ >]+)>/),
Expand Down

0 comments on commit f32a628

Please sign in to comment.