Skip to content

Instantly share code, notes, and snippets.

@llimllib
Last active September 5, 2024 02:35
Show Gist options
  • Save llimllib/56b137141afc06f055ac50e2cbc83470 to your computer and use it in GitHub Desktop.
Save llimllib/56b137141afc06f055ac50e2cbc83470 to your computer and use it in GitHub Desktop.
Printing a reasonable table in javascript when it includes ansi escapes
┌───────────┬────────────────────┬───────┐
│ id │ name │ type │
├───────────┼────────────────────┼───────┤
│ abc123xyz │ something │ web │
│ abc124xyz │ something else │ web │
│ abc125def │ Martin Luther King │ pserv │
│ abc126xyz │ Abraham Lincoln │ web │
└───────────┴────────────────────┴───────┘
// table.js by Bill Mill. Modify it any way you like, use it in any
// application with any license, it's quite terrible but it gets the
// job I need done, done
// from https://github.com/chalk/ansi-regex/pull/58/files
const ANSI_REGEX = new RegExp(
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
'g',
);
/** pad the end of the string, but consider only printable characters by
* substituting out ANSI escapes */
function padEnd(s, l) {
return s + ' '.repeat(Math.max(0, l - s.replace(ANSI_REGEX, '').length));
}
/** given a list of headers and a list of lists of rows, return a string
* representing a table suitable for printing to the terminal.
*
* Note that this is hacky: it does not handle headers that are longer
* than the rows underneath them, and it assumes that every row is the
* same length, among other sins. Fancy it up if you need to.
*/
function table(headers, rows) {
const widths = Array.from({ length: rows[0].length }, (_, i) => i).map(colIndex =>
Math.max(...rows.map(row => row[colIndex].replace(ANSI_REGEX, '').length)),
);
const padding = 1;
const pad = ' '.repeat(padding);
let tableStr = '';
tableStr += `┌${widths.map(w => '─'.repeat(w + padding * 2)).join('┬')}┐\n`;
tableStr += `│${pad}${headers.map((h, i) => h.padEnd(widths[i])).join(`${pad}${pad}`)}${pad}│\n`;
tableStr += `├${widths.map(w => '─'.repeat(w + padding * 2)).join('┼')}┤\n`;
for (const row of rows) {
tableStr += `│${pad}${row.map((r, i) => padEnd(r, widths[i])).join(`${pad}${pad}`)}${pad}│\n`;
}
tableStr += `└${widths.map(w => '─'.repeat(w + padding * 2)).join('┴')}┘\n`;
return tableStr;
}
module.exports = table;
const table = require('./table');
const rows = [
[
'\x1B]8;;https://notes.billmill.org\x1B\\abc123xyz\x1B]8;;\x1B\\',
'\x1B]8;;https://notes.billmill.org\x1B\\something\x1B]8;;\x1B\\',
'web',
],
[
'\x1B]8;;https://notes.billmill.org\x1B\\abc124xyz\x1B]8;;\x1B\\',
'\x1B]8;;https://notes.billmill.org\x1B\\something else\x1B]8;;\x1B\\',
'web',
],
[
'\x1B]8;;https://notes.billmill.org\x1B\\abc125def\x1B]8;;\x1B\\',
'\x1B]8;;https://notes.billmill.org\x1B\\Martin Luther King\x1B]8;;\x1B\\',
'pserv',
],
[
'\x1B]8;;https://notes.billmill.org\x1B\\abc126xyz\x1B]8;;\x1B\\',
'\x1B]8;;https://notes.billmill.org\x1B\\Abraham Lincoln\x1B]8;;\x1B\\',
'web',
],
];
console.log(table(['id', 'name', 'type'], rows));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment