const ansiEscape = String.fromCharCode(27); export const ansiPattern = new RegExp(`${ansiEscape}\\[[0-?]*[ -/]*[@-~]`, 'g'); export const ansiPrefixPattern = new RegExp( `^${ansiEscape}\\[[0-?]*[ -/]*[@-~]`, ); export function stripAnsi(input: string): string { return input.replace(ansiPattern, ''); } export function visibleLength(input: string): number { let width = 0; let index = 0; while (index < input.length) { const ansiMatch = input.slice(index).match(ansiPrefixPattern); if (ansiMatch) { index += ansiMatch[0].length; continue; } const codePoint = input.codePointAt(index); if (codePoint == null) { break; } const character = String.fromCodePoint(codePoint); width += codePointWidth(codePoint); index += character.length; } return width; } export function sliceVisible(input: string, width: number): string { if (width <= 0) { return ''; } let output = ''; let visible = 0; let index = 0; while (index < input.length && visible < width) { const ansiMatch = input.slice(index).match(ansiPrefixPattern); if (ansiMatch) { output += ansiMatch[0]; index += ansiMatch[0].length; continue; } const codePoint = input.codePointAt(index); if (codePoint == null) { break; } const character = String.fromCodePoint(codePoint); const characterWidth = codePointWidth(codePoint); if (characterWidth > 0 && visible + characterWidth > width) { break; } output += character; index += character.length; visible += characterWidth; } while (index < input.length) { const ansiMatch = input.slice(index).match(ansiPrefixPattern); if (!ansiMatch) { break; } output += ansiMatch[0]; index += ansiMatch[0].length; } return output; } export function codePointWidth(codePoint: number): number { if (codePoint === 0x09) { return 4; } if (codePoint < 0x20 || (codePoint >= 0x7f && codePoint < 0xa0)) { return 0; } if (isZeroWidthCodePoint(codePoint)) { return 0; } return isWideCodePoint(codePoint) ? 2 : 1; } function isZeroWidthCodePoint(codePoint: number): boolean { return ( (codePoint >= 0x0300 && codePoint <= 0x036f) || (codePoint >= 0x0483 && codePoint <= 0x0489) || (codePoint >= 0x0591 && codePoint <= 0x05bd) || codePoint === 0x05bf || (codePoint >= 0x05c1 && codePoint <= 0x05c2) || (codePoint >= 0x05c4 && codePoint <= 0x05c5) || codePoint === 0x05c7 || (codePoint >= 0x0610 && codePoint <= 0x061a) || (codePoint >= 0x064b && codePoint <= 0x065f) || codePoint === 0x0670 || (codePoint >= 0x06d6 && codePoint <= 0x06dc) || (codePoint >= 0x06df && codePoint <= 0x06e4) || (codePoint >= 0x06e7 && codePoint <= 0x06e8) || (codePoint >= 0x06ea && codePoint <= 0x06ed) || codePoint === 0x0711 || (codePoint >= 0x0730 && codePoint <= 0x074a) || (codePoint >= 0x07a6 && codePoint <= 0x07b0) || (codePoint >= 0x07eb && codePoint <= 0x07f3) || (codePoint >= 0x0816 && codePoint <= 0x0819) || (codePoint >= 0x081b && codePoint <= 0x0823) || (codePoint >= 0x0825 && codePoint <= 0x0827) || (codePoint >= 0x0829 && codePoint <= 0x082d) || (codePoint >= 0x0859 && codePoint <= 0x085b) || (codePoint >= 0x08d3 && codePoint <= 0x0902) || codePoint === 0x093a || codePoint === 0x093c || (codePoint >= 0x0941 && codePoint <= 0x0948) || codePoint === 0x094d || (codePoint >= 0x0951 && codePoint <= 0x0957) || codePoint === 0x200d || (codePoint >= 0xfe00 && codePoint <= 0xfe0f) || (codePoint >= 0xe0100 && codePoint <= 0xe01ef) ); } function isWideCodePoint(codePoint: number): boolean { return ( codePoint >= 0x1100 && (codePoint <= 0x115f || codePoint === 0x2329 || codePoint === 0x232a || (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) || (codePoint >= 0xac00 && codePoint <= 0xd7a3) || (codePoint >= 0xf900 && codePoint <= 0xfaff) || (codePoint >= 0xfe10 && codePoint <= 0xfe19) || (codePoint >= 0xfe30 && codePoint <= 0xfe6f) || (codePoint >= 0xff00 && codePoint <= 0xff60) || (codePoint >= 0xffe0 && codePoint <= 0xffe6) || (codePoint >= 0x1f300 && codePoint <= 0x1f64f) || (codePoint >= 0x1f900 && codePoint <= 0x1f9ff) || (codePoint >= 0x20000 && codePoint <= 0x3fffd)) ); }