2 var stringWidth = require('string-width');
3 var stripAnsi = require('strip-ansi');
40 function wrapAnsi(code) {
41 return ESCAPES[0] + '[' + code + 'm';
44 // calculate the length of words split on ' ', ignoring
45 // the extra characters added by ansi escape codes.
46 function wordLengths(str) {
47 return str.split(' ').map(function (s) {
48 return stringWidth(s);
52 // wrap a long word across multiple rows.
53 // ansi escape codes do not count towards length.
54 function wrapWord(rows, word, cols) {
55 var insideEscape = false;
56 var visible = stripAnsi(rows[rows.length - 1]).length;
58 for (var i = 0; i < word.length; i++) {
61 rows[rows.length - 1] += x;
63 if (ESCAPES.indexOf(x) !== -1) {
65 } else if (insideEscape && x === 'm') {
76 if (visible >= cols && i < word.length - 1) {
82 // it's possible that the last row we copy over is only
83 // ansi escape characters, handle this edge-case.
84 if (!visible && rows[rows.length - 1].length > 0 && rows.length > 1) {
85 rows[rows.length - 2] += rows.pop();
89 // the wrap-ansi module can be invoked
90 // in either 'hard' or 'soft' wrap mode.
92 // 'hard' will never allow a string to take up more
93 // than cols characters.
95 // 'soft' allows long words to expand past the column length.
96 function exec(str, cols, opts) {
97 var options = opts || {};
103 var lengths = wordLengths(str);
104 var words = str.split(' ');
107 for (var i = 0, word; (word = words[i]) !== undefined; i++) {
108 var rowLength = stringWidth(rows[rows.length - 1]);
111 rows[rows.length - 1] += ' ';
115 // in 'hard' wrap mode, the length of a line is
116 // never allowed to extend past 'cols'.
117 if (lengths[i] > cols && options.hard) {
121 wrapWord(rows, word, cols);
125 if (rowLength + lengths[i] > cols && rowLength > 0) {
126 if (options.wordWrap === false && rowLength < cols) {
127 wrapWord(rows, word, cols);
134 rows[rows.length - 1] += word;
137 pre = rows.map(function (r) {
141 for (var j = 0; j < pre.length; j++) {
146 if (ESCAPES.indexOf(y) !== -1) {
147 var code = parseFloat(/[0-9][^m]*/.exec(pre.slice(j, j + 4)));
148 escapeCode = code === END_CODE ? null : code;
151 if (escapeCode && ESCAPE_CODES[escapeCode]) {
152 if (pre[j + 1] === '\n') {
153 ret += wrapAnsi(ESCAPE_CODES[escapeCode]);
154 } else if (y === '\n') {
155 ret += wrapAnsi(escapeCode);
163 // for each line break, invoke the method separately.
164 module.exports = function (str, cols, opts) {
165 return String(str).split('\n').map(function (substr) {
166 return exec(substr, cols, opts);