mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
5.5 KiB
276 lines
5.5 KiB
/**
|
|
* @author Titus Wormer
|
|
* @copyright 2015 Titus Wormer
|
|
* @license MIT
|
|
* @module remark:parse:tokenize:table
|
|
* @fileoverview Tokenise a table.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var whitespace = require('is-whitespace-character');
|
|
|
|
module.exports = table;
|
|
table.notInList = true;
|
|
|
|
var C_BACKSLASH = '\\';
|
|
var C_TICK = '`';
|
|
var C_DASH = '-';
|
|
var C_PIPE = '|';
|
|
var C_COLON = ':';
|
|
var C_SPACE = ' ';
|
|
var C_NEWLINE = '\n';
|
|
var C_TAB = '\t';
|
|
|
|
var MIN_TABLE_COLUMNS = 1;
|
|
var MIN_TABLE_ROWS = 2;
|
|
|
|
var TABLE_ALIGN_LEFT = 'left';
|
|
var TABLE_ALIGN_CENTER = 'center';
|
|
var TABLE_ALIGN_RIGHT = 'right';
|
|
var TABLE_ALIGN_NONE = null;
|
|
|
|
/* Tokenise a table. */
|
|
function table(eat, value, silent) {
|
|
var self = this;
|
|
var index;
|
|
var alignments;
|
|
var alignment;
|
|
var subvalue;
|
|
var row;
|
|
var length;
|
|
var lines;
|
|
var queue;
|
|
var character;
|
|
var hasDash;
|
|
var align;
|
|
var cell;
|
|
var preamble;
|
|
var count;
|
|
var opening;
|
|
var now;
|
|
var position;
|
|
var lineCount;
|
|
var line;
|
|
var rows;
|
|
var table;
|
|
var lineIndex;
|
|
var pipeIndex;
|
|
var first;
|
|
|
|
/* Exit when not in gfm-mode. */
|
|
if (!self.options.gfm) {
|
|
return;
|
|
}
|
|
|
|
/* Get the rows.
|
|
* Detecting tables soon is hard, so there are some
|
|
* checks for performance here, such as the minimum
|
|
* number of rows, and allowed characters in the
|
|
* alignment row. */
|
|
index = 0;
|
|
lineCount = 0;
|
|
length = value.length + 1;
|
|
lines = [];
|
|
|
|
while (index < length) {
|
|
lineIndex = value.indexOf(C_NEWLINE, index);
|
|
pipeIndex = value.indexOf(C_PIPE, index + 1);
|
|
|
|
if (lineIndex === -1) {
|
|
lineIndex = value.length;
|
|
}
|
|
|
|
if (pipeIndex === -1 || pipeIndex > lineIndex) {
|
|
if (lineCount < MIN_TABLE_ROWS) {
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
lines.push(value.slice(index, lineIndex));
|
|
lineCount++;
|
|
index = lineIndex + 1;
|
|
}
|
|
|
|
/* Parse the alignment row. */
|
|
subvalue = lines.join(C_NEWLINE);
|
|
alignments = lines.splice(1, 1)[0] || [];
|
|
index = 0;
|
|
length = alignments.length;
|
|
lineCount--;
|
|
alignment = false;
|
|
align = [];
|
|
|
|
while (index < length) {
|
|
character = alignments.charAt(index);
|
|
|
|
if (character === C_PIPE) {
|
|
hasDash = null;
|
|
|
|
if (alignment === false) {
|
|
if (first === false) {
|
|
return;
|
|
}
|
|
} else {
|
|
align.push(alignment);
|
|
alignment = false;
|
|
}
|
|
|
|
first = false;
|
|
} else if (character === C_DASH) {
|
|
hasDash = true;
|
|
alignment = alignment || TABLE_ALIGN_NONE;
|
|
} else if (character === C_COLON) {
|
|
if (alignment === TABLE_ALIGN_LEFT) {
|
|
alignment = TABLE_ALIGN_CENTER;
|
|
} else if (hasDash && alignment === TABLE_ALIGN_NONE) {
|
|
alignment = TABLE_ALIGN_RIGHT;
|
|
} else {
|
|
alignment = TABLE_ALIGN_LEFT;
|
|
}
|
|
} else if (!whitespace(character)) {
|
|
return;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
if (alignment !== false) {
|
|
align.push(alignment);
|
|
}
|
|
|
|
/* Exit when without enough columns. */
|
|
if (align.length < MIN_TABLE_COLUMNS) {
|
|
return;
|
|
}
|
|
|
|
/* istanbul ignore if - never used (yet) */
|
|
if (silent) {
|
|
return true;
|
|
}
|
|
|
|
/* Parse the rows. */
|
|
position = -1;
|
|
rows = [];
|
|
|
|
table = eat(subvalue).reset({
|
|
type: 'table',
|
|
align: align,
|
|
children: rows
|
|
});
|
|
|
|
while (++position < lineCount) {
|
|
line = lines[position];
|
|
row = {type: 'tableRow', children: []};
|
|
|
|
/* Eat a newline character when this is not the
|
|
* first row. */
|
|
if (position) {
|
|
eat(C_NEWLINE);
|
|
}
|
|
|
|
/* Eat the row. */
|
|
eat(line).reset(row, table);
|
|
|
|
length = line.length + 1;
|
|
index = 0;
|
|
queue = '';
|
|
cell = '';
|
|
preamble = true;
|
|
count = null;
|
|
opening = null;
|
|
|
|
while (index < length) {
|
|
character = line.charAt(index);
|
|
|
|
if (character === C_TAB || character === C_SPACE) {
|
|
if (cell) {
|
|
queue += character;
|
|
} else {
|
|
eat(character);
|
|
}
|
|
|
|
index++;
|
|
continue;
|
|
}
|
|
|
|
if (character === '' || character === C_PIPE) {
|
|
if (preamble) {
|
|
eat(character);
|
|
} else {
|
|
if (character && opening) {
|
|
queue += character;
|
|
index++;
|
|
continue;
|
|
}
|
|
|
|
if ((cell || character) && !preamble) {
|
|
subvalue = cell;
|
|
|
|
if (queue.length > 1) {
|
|
if (character) {
|
|
subvalue += queue.slice(0, queue.length - 1);
|
|
queue = queue.charAt(queue.length - 1);
|
|
} else {
|
|
subvalue += queue;
|
|
queue = '';
|
|
}
|
|
}
|
|
|
|
now = eat.now();
|
|
|
|
eat(subvalue)({
|
|
type: 'tableCell',
|
|
children: self.tokenizeInline(cell, now)
|
|
}, row);
|
|
}
|
|
|
|
eat(queue + character);
|
|
|
|
queue = '';
|
|
cell = '';
|
|
}
|
|
} else {
|
|
if (queue) {
|
|
cell += queue;
|
|
queue = '';
|
|
}
|
|
|
|
cell += character;
|
|
|
|
if (character === C_BACKSLASH && index !== length - 2) {
|
|
cell += line.charAt(index + 1);
|
|
index++;
|
|
}
|
|
|
|
if (character === C_TICK) {
|
|
count = 1;
|
|
|
|
while (line.charAt(index + 1) === character) {
|
|
cell += character;
|
|
index++;
|
|
count++;
|
|
}
|
|
|
|
if (!opening) {
|
|
opening = count;
|
|
} else if (count >= opening) {
|
|
opening = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
preamble = false;
|
|
index++;
|
|
}
|
|
|
|
/* Eat the alignment row. */
|
|
if (!position) {
|
|
eat(C_NEWLINE + alignments);
|
|
}
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|