| import sliceAnsi from 'slice-ansi'; |
| import stringWidth from 'string-width'; |
|
|
| function getIndexOfNearestSpace(string, wantedIndex, shouldSearchRight) { |
| if (string.charAt(wantedIndex) === ' ') { |
| return wantedIndex; |
| } |
|
|
| const direction = shouldSearchRight ? 1 : -1; |
|
|
| for (let index = 0; index <= 3; index++) { |
| const finalIndex = wantedIndex + (index * direction); |
| if (string.charAt(finalIndex) === ' ') { |
| return finalIndex; |
| } |
| } |
|
|
| return wantedIndex; |
| } |
|
|
| export default function cliTruncate(text, columns, options = {}) { |
| const { |
| position = 'end', |
| space = false, |
| preferTruncationOnSpace = false, |
| } = 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}`); |
| } |
|
|