Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle spaces inside background color escapes #29

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,44 @@ const exec = (string, columns, options = {}) => {
const lengths = wordLengths(string);
const rows = [''];

for (const [index, word] of string.split(' ').entries()) {
// Strings with spaces wrapped into escapes like chalk.bgRed(' test ')
// get split up into 3 words, because of .split(' '), which results in incorrect rendering of that string.
// To prevent that, we need to normalize result of .split() and avoid splitting strings wrapped into ansi escapes
const stringParts = string.split(' ');
let normalizedStringParts = [];

// Look for all background color ansi escapes
const bgColorEscape = /(\u001b\[(4[0-7]|10[0-7])m.*?\u001b\[49m)/g; // eslint-disable-line no-control-regex
let match;
let lastMatch;

while ((match = bgColorEscape.exec(string)) !== null) {
if (normalizedStringParts.length === 0) {
// Add string part before first background color escape
normalizedStringParts.push(...string.slice(0, match.index).split(' ').slice(0, -1));
}

normalizedStringParts.push(match[0]);
lastMatch = match;
}

if (lastMatch) {
// Add remaining string after last background color escape
const remainingString = string.slice(lastMatch.index + lastMatch[0].length);

if (remainingString.length > 0) {
normalizedStringParts.push(...remainingString.split(' ').slice(0, -1));
}
} else {
// If there is no match, it means there are no background color escapes
normalizedStringParts = stringParts;
}

for (const [index, word] of normalizedStringParts.entries()) {
rows[rows.length - 1] = options.trim === false ? rows[rows.length - 1] : rows[rows.length - 1].trim();
let rowLength = stringWidth(rows[rows.length - 1]);

if (rowLength || word === '') {
if ((rowLength || word === '')) {
if (rowLength === columns && options.wordWrap === false) {
// If we start with a new word but the current row length equals the length of the columns, add a new row
rows.push('');
Expand Down
20 changes: 20 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ test('does not prepend newline if first string is greater than "cols"', t => {
t.is(res.split('\n').length, 1);
});

test('does not trim beginning of string if it starts with a space and wrapped into background color escape', t => {
const res = m(chalk.bgGreen(' hello '), 10, {hard: true, trim: false});
t.is(res, chalk.bgGreen(' hello '));
});

test('trim beginning of string if it starts with a space and not wrapped into background color escape', t => {
const res = m(chalk.green(' hello '), 10, {hard: true, trim: false});
t.is(res, chalk.green('hello '));
});

test('handles nested color escapes', t => {
const res = m(chalk.bgGreen(' hello ' + chalk.red('world')), 10, {hard: true, trim: false});
t.is(res, chalk.bgGreen(' hello ' + chalk.red('world')));
});

test('handle ending space within a color escape', t => {
const res = m(chalk.bgGreen(' hello ' + chalk.red('world') + ' '), 10, {hard: true, trim: false});
t.is(res, chalk.bgGreen(' hello ' + chalk.red('world') + ' '));
});

// When "hard" is true

test('breaks strings longer than "cols" characters', t => {
Expand Down