trainingproject
Version:
A module to teach you how to module.
365 lines (316 loc) • 9.74 kB
JavaScript
var fs = require('fs');
var path = require('path');
var marked = require('marked');
var cardinal = require('cardinal');
var xtend = require('xtend');
var color = require('./color');
var table = require('text-table');
var addHeader = require('table-header').add;
var chalk = require('chalk');
var wcstring = require('wcstring');
var os = require('os');
var defaultOptions = {
collapseNewlines: true,
space: '',
hrStart: '',
hrChar: '-',
hrEnd: '',
headingStart: '\n',
headingEnd: '\n\n',
headingIndentChar: '#',
headingIndent: function (token) {
return Array(token.depth + 1).join(this.headingIndentChar)
},
codeStart: '\n',
codeEnd: '\n\n',
codePad: ' ',
codeTheme: os.platform() === 'win32' ? require('./syntaxColor_win') : require('./syntaxColor'),
blockquoteStart: '\n',
blockquoteEnd: '\n\n',
blockquoteColor: 'blockquote',
blockquotePad: ' > ',
blockquotePadColor: 'syntax',
listStart: '\n',
listEnd: '\n',
listItemStart: '',
listItemEnd: '\n',
listItemColor: 'ul',
listItemPad: {first: ' * ', regular: ' '},
listItemPadColor: 'syntax',
paragraphStart: '',
paragraphEnd: '\n\n',
width: process.stdout.columns || 80,
maxWidth: -1,
tableStart: '\n',
tableSeparator: ' ',
tableEnd: '\n\n'
};
var tokens;
var inline;
var token;
var blockDepth = 0;
function processInline(src, options) {
var out = '';
var cap;
function outLink (title, href) {
out += '[' + color(title, 'strong') + '](' + color(href, 'link') + ')';
}
while (src) {
// escape
if (cap = inline.rules.escape.exec(src)) {
src = src.substring(cap[0].length);
out += cap[1];
continue;
}
// code
if (cap = inline.rules.code.exec(src)) {
src = src.substring(cap[0].length);
out += color(cap[2], 'code');
continue;
}
// autolink
if (cap = inline.rules.autolink.exec(src)) {
src = src.substring(cap[0].length);
out += color(cap[0], 'link');
continue;
}
// url (gfm)
if (cap = inline.rules.url.exec(src)) {
src = src.substring(cap[0].length);
outLink(cap[1], cap[1]);
continue;
}
// tag
if (cap = inline.rules.tag.exec(src)) {
src = src.substring(cap[0].length);
out += cap[0];
continue;
}
// link
if (cap = inline.rules.link.exec(src)) {
src = src.substring(cap[0].length);
outLink(cap[1], cap[2]);
continue;
}
// reflink, nolink
if ((cap = inline.rules.reflink.exec(src))
|| (cap = inline.rules.nolink.exec(src))) {
src = src.substring(cap[0].length);
out += cap[0];
continue;
}
// strong
if (cap = inline.rules.strong.exec(src)) {
src = src.substring(cap[0].length);
out += color(processInline(cap[2] || cap[1]), 'strong');
continue;
}
// em
if (cap = inline.rules.em.exec(src)) {
src = src.substring(cap[0].length);
out += color(processInline(cap[2] || cap[1]), 'em');
continue;
}
// br
if (cap = inline.rules.br.exec(src)) {
src = src.substring(cap[0].length);
out += '\n';
continue;
}
// del (gfm)
if (cap = inline.rules.del.exec(src)) {
src = src.substring(cap[0].length);
out += color(processInline(cap[1]), 'del');
continue;
}
// text
if (cap = inline.rules.text.exec(src)) {
src = src.substring(cap[0].length);
out += cap[0];
continue;
}
if (src) {
throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
}
}
return out;
}
function processToken(options) {
var type = token.type;
var text = token.text;
var content;
function getOption(key) {
var value = options[key];
return typeof value === 'function' ? value(token) : value;
}
switch (type) {
case 'space': {
return options.space;
}
case 'hr': {
var hrStr = new Array(options.width).join(options.hrChar) + '\n';
return options.hrStart + color(hrStr, type) + options.hrEnd;
}
case 'heading': {
text = blockFormat(processInline(text), {
block_color: type,
pad: options.headingIndent(token) + ' ',
pad_color: 'syntax',
width: options.width
})
return options.headingStart + text + options.headingEnd;
}
case 'code': {
content = '';
if (token.lang === 'raw') {
return text + '\n';
}
try {
content = cardinal.highlight(text, {
theme: chalk.supportsColor
? options.codeTheme
: require('cardinal/themes/empty')
});
}
catch (e) {
content = color(text, type);
}
content = content.replace(/^/gm, getOption('codePad'));
return options.codeStart + content + options.codeEnd;
}
case 'table': {
content = tableFormat(token, options);
return options.tableStart + content + options.tableEnd;
}
case 'blockquote_start': {
content = '';
blockDepth++;
while (next().type !== 'blockquote_end') {
content += processToken(options);
}
content = blockFormat(content, {
block_color: options.blockquoteColor,
pad: options.blockquotePad,
pad_color: options.blockquotePadColor,
width: options.width
});
blockDepth--;
return options.blockquoteStart + content + options.blockquoteEnd;
}
case 'list_start': {
content = '';
while (next().type !== 'list_end') {
content += processToken(options);
}
return options.listStart + content + options.listEnd;
}
case 'list_item_start': {
content = '';
while (next().type !== 'list_item_end') {
if (type === 'text') {
content += text;
} else {
content += processToken(options);
}
}
content = blockFormat(
processInline(content),
{
block_color: options.listItemColor,
pad: options.listItemPad,
pad_color: options.listItemPadColor,
width: options.width
}
);
return options.listItemStart + content + options.listItemEnd;
}
case 'paragraph': {
if (blockDepth > 0) {
return text;
}
text = blockFormat(
processInline(text),
{
block_color: type,
pad: options.paragraphPad,
pad_color: options.paragraphPadColor,
width: options.width
}
);
return options.paragraphStart + text + options.paragraphEnd;
}
default: {
if (text) {
return text;
}
}
}
}
function next() {
return token = tokens.shift();
}
function blockFormat(src, opts) {
opts = opts || {};
var retLines = [];
src = wcstring(src).wrap(opts.width, opts.pad, function (padStr) {
if (opts.pad_color) {
return color(padStr, opts.pad_color)
}
return padStr
});
return color(src, opts.block_color);
}
function tableFormat (token, options) {
var aligns = token.align.map(function (a) {
return (a===null) ? 'l' : a[0];
});
var rows = token.cells.map(function (row) {
return row.map(processInline);
});
var headers = token.header.map(function (s) {
return processInline('**'+s+'**');
});
addHeader(rows, headers, { stringLength: getStringWidth });
return table(rows, {
align: aligns,
stringLength: getStringWidth,
hsep: options.tableSeparator
});
}
/**
* Returns the number of columns required to display the given string.
*/
function getStringWidth(str) {
return wcstring(str).width();
}
exports.parse = function(text, options) {
tokens = marked.lexer(text);
inline = new marked.InlineLexer(tokens.links);
options = xtend(defaultOptions, options);
var outputArr = [];
var output;
if (options.maxWidth !== -1 && options.width > options.maxWidth) {
options.width = options.maxWidth
}
while (next()) {
outputArr.push(processToken(options));
}
if (options.collapseNewlines) {
output = outputArr.join('').replace(/\n\n\n/g, '\n\n');
}
tokens = null;
token = null;
return output;
}
exports.parseFile = function(file, options) {
var filePath = path.resolve(__dirname, file);
var ret = '';
try {
var text = fs.readFileSync(filePath).toString();
ret = exports.parse(text, options);
}
catch (e) {
throw e;
}
return ret;
}