basant307/AI_Governance_Project
048
1import sliceAnsi from 'slice-ansi';2import stringWidth from 'string-width';3 4function getIndexOfNearestSpace(string, wantedIndex, shouldSearchRight) {5 if (string.charAt(wantedIndex) === ' ') {6 return wantedIndex;7 }8 9 const direction = shouldSearchRight ? 1 : -1;10 11 for (let index = 0; index <= 3; index++) {12 const finalIndex = wantedIndex + (index * direction);13 if (string.charAt(finalIndex) === ' ') {14 return finalIndex;15 }16 }17 18 return wantedIndex;19}20 21export default function cliTruncate(text, columns, options = {}) {22 const {23 position = 'end',24 space = false,25 preferTruncationOnSpace = false,26 } = options;27 28 let {truncationCharacter = '…'} = options;29 30 if (typeof text !== 'string') {31 throw new TypeError(`Expected \`input\` to be a string, got ${typeof text}`);32 }33 34 if (typeof columns !== 'number') {35 throw new TypeError(`Expected \`columns\` to be a number, got ${typeof columns}`);36 }37 38 if (columns < 1) {39 return '';40 }41 42 const length = stringWidth(text);43 44 if (length <= columns) {45 return text;46 }47 48 if (columns === 1) {49 return truncationCharacter;50 }51 52 // ANSI escape sequence constants53 const ANSI = {54 ESC: 27,55 LEFT_BRACKET: 91,56 LETTER_M: 109,57 };58 59 const isSgrParameter = code => (code >= 48 && code <= 57) || code === 59; // 0-9 or ;60 61 function leadingSgrSpanEndIndex(string) {62 let index = 0;63 while (index + 2 < string.length && string.codePointAt(index) === ANSI.ESC && string.codePointAt(index + 1) === ANSI.LEFT_BRACKET) {64 let j = index + 2;65 while (j < string.length && isSgrParameter(string.codePointAt(j))) {66 j++;67 }68 69 if (j < string.length && string.codePointAt(j) === ANSI.LETTER_M) {70 index = j + 1;71 continue;72 }73 74 break;75 }76 77 return index;78 }79 80 function trailingSgrSpanStartIndex(string) {81 let start = string.length;82 while (start > 1 && string.codePointAt(start - 1) === ANSI.LETTER_M) {83 let j = start - 2;84 while (j >= 0 && isSgrParameter(string.codePointAt(j))) {85 j--;86 }87 88 if (j >= 1 && string.codePointAt(j - 1) === ANSI.ESC && string.codePointAt(j) === ANSI.LEFT_BRACKET) {89 start = j - 1;90 continue;91 }92 93 break;94 }95 96 return start;97 }98 99 function appendWithInheritedStyleFromEnd(visible, suffix) {100 const start = trailingSgrSpanStartIndex(visible);101 if (start === visible.length) {102 return visible + suffix;103 }104 105 return visible.slice(0, start) + suffix + visible.slice(start);106 }107 108 function prependWithInheritedStyleFromStart(prefix, visible) {109 const end = leadingSgrSpanEndIndex(visible);110 if (end === 0) {111 return prefix + visible;112 }113 114 return visible.slice(0, end) + prefix + visible.slice(end);115 }116 117 if (position === 'start') {118 if (preferTruncationOnSpace) {119 const nearestSpace = getIndexOfNearestSpace(text, length - columns + 1, true);120 const right = sliceAnsi(text, nearestSpace, length).trim();121 return prependWithInheritedStyleFromStart(truncationCharacter, right);122 }123 124 if (space) {125 truncationCharacter += ' ';126 }127 128 const right = sliceAnsi(text, length - columns + stringWidth(truncationCharacter), length);129 return prependWithInheritedStyleFromStart(truncationCharacter, right);130 }131 132 if (position === 'middle') {133 if (space) {134 truncationCharacter = ` ${truncationCharacter} `;135 }136 137 const half = Math.floor(columns / 2);138 139 if (preferTruncationOnSpace) {140 const spaceNearFirstBreakPoint = getIndexOfNearestSpace(text, half);141 const spaceNearSecondBreakPoint = getIndexOfNearestSpace(text, length - (columns - half) + 1, true);142 return sliceAnsi(text, 0, spaceNearFirstBreakPoint) + truncationCharacter + sliceAnsi(text, spaceNearSecondBreakPoint, length).trim();143 }144 145 return (146 sliceAnsi(text, 0, half)147 + truncationCharacter148 + sliceAnsi(text, length - (columns - half) + stringWidth(truncationCharacter), length)149 );150 }151 152 if (position === 'end') {153 if (preferTruncationOnSpace) {154 const nearestSpace = getIndexOfNearestSpace(text, columns - 1);155 const left = sliceAnsi(text, 0, nearestSpace);156 return appendWithInheritedStyleFromEnd(left, truncationCharacter);157 }158 159 if (space) {160 truncationCharacter = ` ${truncationCharacter}`;161 }162 163 const left = sliceAnsi(text, 0, columns - stringWidth(truncationCharacter));164 return appendWithInheritedStyleFromEnd(left, truncationCharacter);165 }166 167 throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${position}`);168}169 