2015-09-16 03:55:10 +00:00
|
|
|
|
/* jslint node: true */
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2018-01-15 19:22:11 +00:00
|
|
|
|
const renderStringLength = require('./string_util.js').renderStringLength;
|
2015-09-16 03:55:10 +00:00
|
|
|
|
|
2018-01-15 19:22:11 +00:00
|
|
|
|
// deps
|
|
|
|
|
const assert = require('assert');
|
|
|
|
|
const _ = require('lodash');
|
|
|
|
|
|
|
|
|
|
exports.wordWrapText = wordWrapText;
|
2016-05-24 02:45:18 +00:00
|
|
|
|
|
|
|
|
|
const SPACE_CHARS = [
|
2018-01-15 19:22:11 +00:00
|
|
|
|
' ', '\f', '\n', '\r', '\v',
|
2016-05-24 02:45:18 +00:00
|
|
|
|
'\u00a0', '\u1680', '\u180e', '\u2000', '\u2001', '\u2002', '\u2003', '\u2004',
|
2018-01-15 19:22:11 +00:00
|
|
|
|
'\u2005', '\u2006', '\u2007', '\u2008', '\u2009', '\u200a', '\u2028', '\u2029',
|
2016-05-24 02:45:18 +00:00
|
|
|
|
'\u202f', '\u205f', '\u3000',
|
|
|
|
|
];
|
|
|
|
|
|
2018-01-15 19:22:11 +00:00
|
|
|
|
const REGEXP_WORD_WRAP = new RegExp(`\t|[${SPACE_CHARS.join('')}]`, 'g');
|
2016-05-24 02:45:18 +00:00
|
|
|
|
|
2018-01-15 19:22:11 +00:00
|
|
|
|
function wordWrapText(text, options) {
|
2016-05-24 02:45:18 +00:00
|
|
|
|
assert(_.isObject(options));
|
|
|
|
|
assert(_.isNumber(options.width));
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
options.tabHandling = options.tabHandling || 'expand';
|
|
|
|
|
options.tabWidth = options.tabWidth || 4;
|
2018-01-15 19:22:11 +00:00
|
|
|
|
options.tabChar = options.tabChar || ' ';
|
|
|
|
|
|
2017-08-25 04:18:07 +00:00
|
|
|
|
//const REGEXP_GOBBLE = new RegExp(`.{0,${options.width}}`, 'g');
|
|
|
|
|
//
|
|
|
|
|
// For a given word, match 0->options.width chars -- alwasy include a full trailing ESC
|
|
|
|
|
// sequence if present!
|
|
|
|
|
//
|
|
|
|
|
// :TODO: Need to create ansi.getMatchRegex or something - this is used all over
|
|
|
|
|
const REGEXP_GOBBLE = new RegExp(`.{0,${options.width}}\\x1b\\[[\\?=;0-9]*[ABCDEFGHJKLMSTfhlmnprsu]|.{0,${options.width}}`, 'g');
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
let m;
|
|
|
|
|
let word;
|
|
|
|
|
let c;
|
|
|
|
|
let renderLen;
|
|
|
|
|
let i = 0;
|
|
|
|
|
let wordStart = 0;
|
2018-01-27 04:37:26 +00:00
|
|
|
|
let result = { wrapped : [ '' ], renderLen : [ 0 ] };
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
function expandTab(column) {
|
|
|
|
|
const remainWidth = options.tabWidth - (column % options.tabWidth);
|
|
|
|
|
return new Array(remainWidth).join(options.tabChar);
|
|
|
|
|
}
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
function appendWord() {
|
|
|
|
|
word.match(REGEXP_GOBBLE).forEach( w => {
|
2018-02-04 17:39:14 +00:00
|
|
|
|
renderLen = renderStringLength(w);
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
if(result.renderLen[i] + renderLen > options.width) {
|
|
|
|
|
if(0 === i) {
|
|
|
|
|
result.firstWrapRange = { start : wordStart, end : wordStart + w.length };
|
|
|
|
|
}
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
result.wrapped[++i] = w;
|
|
|
|
|
result.renderLen[i] = renderLen;
|
|
|
|
|
} else {
|
2018-01-15 19:22:11 +00:00
|
|
|
|
result.wrapped[i] += w;
|
|
|
|
|
result.renderLen[i] = (result.renderLen[i] || 0) + renderLen;
|
2016-05-24 02:45:18 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
//
|
|
|
|
|
// Some of the way we word wrap is modeled after Sublime Test 3:
|
|
|
|
|
//
|
|
|
|
|
// * Sublime Text 3 for example considers spaces after a word
|
|
|
|
|
// part of said word. For example, "word " would be wraped
|
|
|
|
|
// in it's entirity.
|
|
|
|
|
//
|
|
|
|
|
// * Tabs in Sublime Text 3 are also treated as a word, so, e.g.
|
|
|
|
|
// "\t" may resolve to " " and must fit within the space.
|
|
|
|
|
//
|
|
|
|
|
// * If a word is ultimately too long to fit, break it up until it does.
|
2018-01-15 19:22:11 +00:00
|
|
|
|
//
|
2016-05-24 02:45:18 +00:00
|
|
|
|
while(null !== (m = REGEXP_WORD_WRAP.exec(text))) {
|
|
|
|
|
word = text.substring(wordStart, REGEXP_WORD_WRAP.lastIndex - 1);
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
c = m[0].charAt(0);
|
|
|
|
|
if(SPACE_CHARS.indexOf(c) > -1) {
|
|
|
|
|
word += m[0];
|
|
|
|
|
} else if('\t' === c) {
|
|
|
|
|
if('expand' === options.tabHandling) {
|
|
|
|
|
// Good info here: http://c-for-dummies.com/blog/?p=424
|
|
|
|
|
word += expandTab(result.wrapped[i].length + word.length) + options.tabChar;
|
|
|
|
|
} else {
|
|
|
|
|
word += m[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-15 19:22:11 +00:00
|
|
|
|
|
2016-05-24 02:45:18 +00:00
|
|
|
|
appendWord();
|
|
|
|
|
wordStart = REGEXP_WORD_WRAP.lastIndex + m[0].length - 1;
|
|
|
|
|
}
|
2015-09-16 03:55:10 +00:00
|
|
|
|
|
|
|
|
|
word = text.substring(wordStart);
|
2018-01-15 19:22:11 +00:00
|
|
|
|
appendWord();
|
2015-09-16 03:55:10 +00:00
|
|
|
|
|
2018-01-15 19:22:11 +00:00
|
|
|
|
return result;
|
2016-05-24 02:45:18 +00:00
|
|
|
|
}
|