103 lines
2.7 KiB
JavaScript
103 lines
2.7 KiB
JavaScript
|
import sliceAnsi from 'slice-ansi';
|
||
|
import stringWidth from 'string-width';
|
||
|
|
||
|
function getIndexOfNearestSpace(string, wantedIndex, shouldSearchRight) {
|
||
|
if (string.charAt(wantedIndex) === ' ') {
|
||
|
return wantedIndex;
|
||
|
}
|
||
|
|
||
|
for (let index = 1; index <= 3; index++) {
|
||
|
if (shouldSearchRight) {
|
||
|
if (string.charAt(wantedIndex + index) === ' ') {
|
||
|
return wantedIndex + index;
|
||
|
}
|
||
|
} else if (string.charAt(wantedIndex - index) === ' ') {
|
||
|
return wantedIndex - index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return wantedIndex;
|
||
|
}
|
||
|
|
||
|
export default function cliTruncate(text, columns, options) {
|
||
|
options = {
|
||
|
position: 'end',
|
||
|
preferTruncationOnSpace: false,
|
||
|
truncationCharacter: '…',
|
||
|
...options,
|
||
|
};
|
||
|
|
||
|
const {position, space, preferTruncationOnSpace} = options;
|
||
|
let {truncationCharacter} = options;
|
||
|
|
||
|
if (typeof text !== 'string') {
|
||
|
throw new TypeError(`Expected \`input\` to be a string, got ${typeof text}`);
|
||
|
}
|
||
|
|
||
|
if (typeof columns !== 'number') {
|
||
|
throw new TypeError(`Expected \`columns\` to be a number, got ${typeof columns}`);
|
||
|
}
|
||
|
|
||
|
if (columns < 1) {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
if (columns === 1) {
|
||
|
return truncationCharacter;
|
||
|
}
|
||
|
|
||
|
const length = stringWidth(text);
|
||
|
|
||
|
if (length <= columns) {
|
||
|
return text;
|
||
|
}
|
||
|
|
||
|
if (position === 'start') {
|
||
|
if (preferTruncationOnSpace) {
|
||
|
const nearestSpace = getIndexOfNearestSpace(text, length - columns + 1, true);
|
||
|
return truncationCharacter + sliceAnsi(text, nearestSpace, length).trim();
|
||
|
}
|
||
|
|
||
|
if (space === true) {
|
||
|
truncationCharacter += ' ';
|
||
|
}
|
||
|
|
||
|
return truncationCharacter + sliceAnsi(text, length - columns + stringWidth(truncationCharacter), length);
|
||
|
}
|
||
|
|
||
|
if (position === 'middle') {
|
||
|
if (space === true) {
|
||
|
truncationCharacter = ` ${truncationCharacter} `;
|
||
|
}
|
||
|
|
||
|
const half = Math.floor(columns / 2);
|
||
|
|
||
|
if (preferTruncationOnSpace) {
|
||
|
const spaceNearFirstBreakPoint = getIndexOfNearestSpace(text, half);
|
||
|
const spaceNearSecondBreakPoint = getIndexOfNearestSpace(text, length - (columns - half) + 1, 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)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (position === 'end') {
|
||
|
if (preferTruncationOnSpace) {
|
||
|
const nearestSpace = getIndexOfNearestSpace(text, columns - 1);
|
||
|
return sliceAnsi(text, 0, nearestSpace) + truncationCharacter;
|
||
|
}
|
||
|
|
||
|
if (space === true) {
|
||
|
truncationCharacter = ` ${truncationCharacter}`;
|
||
|
}
|
||
|
|
||
|
return sliceAnsi(text, 0, columns - stringWidth(truncationCharacter)) + truncationCharacter;
|
||
|
}
|
||
|
|
||
|
throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${position}`);
|
||
|
}
|