blob: 2b72907b1393421109db8fc4d914b08061b4e297 [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001/*
2 * grunt
3 * http://gruntjs.com/
4 *
5 * Copyright (c) 2016 "Cowboy" Ben Alman
6 * Licensed under the MIT license.
7 * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
8 */
9
10'use strict';
11
12var chalk = require('chalk');
13var _ = require('lodash');
14
15// Pretty-format a word list.
16exports.wordlist = function(arr, options) {
17 options = _.defaults(options || {}, {
18 separator: ', ',
19 color: 'cyan'
20 });
21 return arr.map(function(item) {
22 return options.color ? chalk[options.color](item) : item;
23 }).join(options.separator);
24};
25
26// Return a string, uncolored (suitable for testing .length, etc).
27exports.uncolor = function(str) {
28 return str.replace(/\x1B\[\d+m/g, '');
29};
30
31// Word-wrap text to a given width, permitting ANSI color codes.
32exports.wraptext = function(width, text) {
33 // notes to self:
34 // grab 1st character or ansi code from string
35 // if ansi code, add to array and save for later, strip from front of string
36 // if character, add to array and increment counter, strip from front of string
37 // if width + 1 is reached and current character isn't space:
38 // slice off everything after last space in array and prepend it to string
39 // etc
40
41 // This result array will be joined on \n.
42 var result = [];
43 var matches, color, tmp;
44 var captured = [];
45 var charlen = 0;
46
47 while (matches = text.match(/(?:(\x1B\[\d+m)|\n|(.))([\s\S]*)/)) {
48 // Updated text to be everything not matched.
49 text = matches[3];
50
51 // Matched a color code?
52 if (matches[1]) {
53 // Save last captured color code for later use.
54 color = matches[1];
55 // Capture color code.
56 captured.push(matches[1]);
57 continue;
58
59 // Matched a non-newline character?
60 } else if (matches[2]) {
61 // If this is the first character and a previous color code was set, push
62 // that onto the captured array first.
63 if (charlen === 0 && color) { captured.push(color); }
64 // Push the matched character.
65 captured.push(matches[2]);
66 // Increment the current charlen.
67 charlen++;
68 // If not yet at the width limit or a space was matched, continue.
69 if (charlen <= width || matches[2] === ' ') { continue; }
70 // The current charlen exceeds the width and a space wasn't matched.
71 // "Roll everything back" until the last space character.
72 tmp = captured.lastIndexOf(' ');
73 text = captured.slice(tmp === -1 ? tmp : tmp + 1).join('') + text;
74 captured = captured.slice(0, tmp);
75 }
76
77 // The limit has been reached. Push captured string onto result array.
78 result.push(captured.join(''));
79
80 // Reset captured array and charlen.
81 captured = [];
82 charlen = 0;
83 }
84
85 result.push(captured.join(''));
86 return result.join('\n');
87};
88
89// Format output into columns, wrapping words as-necessary.
90exports.table = function(widths, texts) {
91 var rows = [];
92 widths.forEach(function(width, i) {
93 var lines = this.wraptext(width, texts[i]).split('\n');
94 lines.forEach(function(line, j) {
95 var row = rows[j];
96 if (!row) { row = rows[j] = []; }
97 row[i] = line;
98 });
99 }, this);
100
101 var lines = [];
102 rows.forEach(function(row) {
103 var txt = '';
104 var column;
105 for (var i = 0; i < row.length; i++) {
106 column = row[i] || '';
107 txt += column;
108 var diff = widths[i] - this.uncolor(column).length;
109 if (diff > 0) { txt += _.repeat(' ', diff); }
110 }
111 lines.push(txt);
112 }, this);
113
114 return lines.join('\n');
115};