• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const stringWidth = require('string-width');
4const stripAnsi = require('strip-ansi');
5
6const concat = Array.prototype.concat;
7const defaults = {
8	character: ' ',
9	newline: '\n',
10	padding: 2,
11	sort: true,
12	width: 0
13};
14
15function byPlainText(a, b) {
16	const plainA = stripAnsi(a);
17	const plainB = stripAnsi(b);
18
19	if (plainA === plainB) {
20		return 0;
21	}
22
23	if (plainA > plainB) {
24		return 1;
25	}
26
27	return -1;
28}
29
30function makeArray() {
31	return [];
32}
33
34function makeList(count) {
35	return Array.apply(null, Array(count));
36}
37
38function padCell(fullWidth, character, value) {
39	const valueWidth = stringWidth(value);
40	const filler = makeList(fullWidth - valueWidth + 1);
41
42	return value + filler.join(character);
43}
44
45function toRows(rows, cell, i) {
46	rows[i % rows.length].push(cell);
47
48	return rows;
49}
50
51function toString(arr) {
52	return arr.join('');
53}
54
55function columns(values, options) {
56	values = concat.apply([], values);
57	options = Object.assign({}, defaults, options);
58
59	let cells = values
60		.filter(Boolean)
61		.map(String);
62
63	if (options.sort !== false) {
64		cells = cells.sort(byPlainText);
65	}
66
67	const termWidth = options.width || process.stdout.columns;
68	const cellWidth = Math.max.apply(null, cells.map(stringWidth)) + options.padding;
69	const columnCount = Math.floor(termWidth / cellWidth) || 1;
70	const rowCount = Math.ceil(cells.length / columnCount) || 1;
71
72	if (columnCount === 1) {
73		return cells.join(options.newline);
74	}
75
76	return cells
77		.map(padCell.bind(null, cellWidth, options.character))
78		.reduce(toRows, makeList(rowCount).map(makeArray))
79		.map(toString)
80		.join(options.newline);
81}
82
83module.exports = columns;
84