123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- import isFullwidthCodePoint from 'is-fullwidth-code-point';
- import ansiStyles from 'ansi-styles';
-
- const astralRegex = /^[\uD800-\uDBFF][\uDC00-\uDFFF]$/;
-
- const ESCAPES = [
- '\u001B',
- '\u009B'
- ];
-
- const wrapAnsi = code => `${ESCAPES[0]}[${code}m`;
-
- const checkAnsi = (ansiCodes, isEscapes, endAnsiCode) => {
- let output = [];
- ansiCodes = [...ansiCodes];
-
- for (let ansiCode of ansiCodes) {
- const ansiCodeOrigin = ansiCode;
- if (ansiCode.includes(';')) {
- ansiCode = ansiCode.split(';')[0][0] + '0';
- }
-
- const item = ansiStyles.codes.get(Number.parseInt(ansiCode, 10));
- if (item) {
- const indexEscape = ansiCodes.indexOf(item.toString());
- if (indexEscape === -1) {
- output.push(wrapAnsi(isEscapes ? item : ansiCodeOrigin));
- } else {
- ansiCodes.splice(indexEscape, 1);
- }
- } else if (isEscapes) {
- output.push(wrapAnsi(0));
- break;
- } else {
- output.push(wrapAnsi(ansiCodeOrigin));
- }
- }
-
- if (isEscapes) {
- output = output.filter((element, index) => output.indexOf(element) === index);
-
- if (endAnsiCode !== undefined) {
- const fistEscapeCode = wrapAnsi(ansiStyles.codes.get(Number.parseInt(endAnsiCode, 10)));
- // TODO: Remove the use of `.reduce` here.
- // eslint-disable-next-line unicorn/no-array-reduce
- output = output.reduce((current, next) => next === fistEscapeCode ? [next, ...current] : [...current, next], []);
- }
- }
-
- return output.join('');
- };
-
- export default function sliceAnsi(string, begin, end) {
- const characters = [...string];
- const ansiCodes = [];
-
- let stringEnd = typeof end === 'number' ? end : characters.length;
- let isInsideEscape = false;
- let ansiCode;
- let visible = 0;
- let output = '';
-
- for (const [index, character] of characters.entries()) {
- let leftEscape = false;
-
- if (ESCAPES.includes(character)) {
- const code = /\d[^m]*/.exec(string.slice(index, index + 18));
- ansiCode = code && code.length > 0 ? code[0] : undefined;
-
- if (visible < stringEnd) {
- isInsideEscape = true;
-
- if (ansiCode !== undefined) {
- ansiCodes.push(ansiCode);
- }
- }
- } else if (isInsideEscape && character === 'm') {
- isInsideEscape = false;
- leftEscape = true;
- }
-
- if (!isInsideEscape && !leftEscape) {
- visible++;
- }
-
- if (!astralRegex.test(character) && isFullwidthCodePoint(character.codePointAt())) {
- visible++;
-
- if (typeof end !== 'number') {
- stringEnd++;
- }
- }
-
- if (visible > begin && visible <= stringEnd) {
- output += character;
- } else if (visible === begin && !isInsideEscape && ansiCode !== undefined) {
- output = checkAnsi(ansiCodes);
- } else if (visible >= stringEnd) {
- output += checkAnsi(ansiCodes, true, ansiCode);
- break;
- }
- }
-
- return output;
- }
|