Skip to content
Merged
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
16 changes: 13 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,30 @@ export default function cliTruncate(text, columns, options = {}) {
if (position === 'middle') {
if (space) {
truncationCharacter = ` ${truncationCharacter} `;

// Drop the padding spaces if the padded character does not fit, so the
// truncation character itself never exceeds the budget.
if (stringWidth(truncationCharacter) >= columns) {
truncationCharacter = truncationCharacter.trim();
}
}

const half = Math.floor(columns / 2);
const truncationWidth = stringWidth(truncationCharacter);
// Reserve room for the truncation character before splitting the budget
// between the two sides, otherwise small budgets overflow (e.g. a width of
// 4 was returned for `columns: 2`).
const half = Math.min(Math.floor(columns / 2), Math.max(0, columns - truncationWidth));

if (preferTruncationOnSpace) {
const spaceNearFirstBreakPoint = getIndexOfNearestSpace(text, half);
const spaceNearSecondBreakPoint = getIndexOfNearestSpace(text, length - (columns - half) + 1, true);
const spaceNearSecondBreakPoint = getIndexOfNearestSpace(text, length - (columns - half) + truncationWidth, true);
return sliceAnsi(text, 0, spaceNearFirstBreakPoint) + truncationCharacter + sliceAnsi(text, spaceNearSecondBreakPoint, length).trim();
}

return (
sliceAnsi(text, 0, half)
+ truncationCharacter
+ sliceAnsi(text, length - (columns - half) + stringWidth(truncationCharacter), length)
+ sliceAnsi(text, length - (columns - half) + truncationWidth, length)
);
}

Expand Down
13 changes: 13 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ test('space option', t => {
t.is(cliTruncate('\u001B[31municornsareawesome\u001B[39m', 10, {position: 'middle', space: true}), '\u001B[31munico\u001B[39m … \u001B[31mme\u001B[39m');
t.is(cliTruncate('Plant a tree every day.', 14, {position: 'middle', space: true}), 'Plant a … day.');
t.is(cliTruncate('안녕하세요', 4, {position: 'start', space: true}), '… 요', 'wide char');

// `middle` + `space` must stay within the budget for small widths
t.is(cliTruncate('unicorns', 2, {position: 'middle', space: true}), 'u…');
t.is(cliTruncate('unicorns', 3, {position: 'middle', space: true}), 'u…s');
for (const columns of [2, 3, 4, 5, 6, 7]) {
t.true(stringWidth(cliTruncate('unicorns', columns, {position: 'middle', space: true})) <= columns, `width <= ${columns}`);
}
});

test('preferTruncationOnSpace option', t => {
Expand All @@ -44,6 +51,12 @@ test('preferTruncationOnSpace option', t => {
t.is(cliTruncate('unicorns rainbow dragons', 6, {position: 'end', preferTruncationOnSpace: true}), 'unico…');
t.is(cliTruncate('unicorns rainbow dragons', 6, {position: 'middle', preferTruncationOnSpace: true}), 'uni…ns');
t.is(cliTruncate('unicorns partying with dragons', 20, {position: 'middle', preferTruncationOnSpace: true}), 'unicorns…dragons');

// `middle` + `space` + `preferTruncationOnSpace` must stay within the budget
t.is(cliTruncate('unicorns', 4, {position: 'middle', space: true, preferTruncationOnSpace: true}), 'u … ');
for (const columns of [2, 3, 4, 5, 6, 7]) {
t.true(stringWidth(cliTruncate('unicorns', columns, {position: 'middle', space: true, preferTruncationOnSpace: true})) <= columns, `width <= ${columns}`);
}
});

test('truncationCharacter option', t => {
Expand Down