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.

277 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;
}