zhangjian
2023-06-05 0976d2d0f90cff460cedfdc8bd74e98c2c31a58c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
'use strict';
const isFullwidthCodePoint = require('is-fullwidth-code-point');
const astralRegex = require('astral-regex');
const ansiStyles = require('ansi-styles');
 
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.match(';')) {
            ansiCode = ansiCode.split(';')[0][0] + '0';
        }
 
        const item = ansiStyles.codes.get(parseInt(ansiCode, 10));
        if (item) {
            const indexEscape = ansiCodes.indexOf(item.toString());
            if (indexEscape >= 0) {
                ansiCodes.splice(indexEscape, 1);
            } else {
                output.push(wrapAnsi(isEscapes ? item : ansiCodeOrigin));
            }
        } 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(parseInt(endAnsiCode, 10)));
            output = output.reduce((current, next) => next === fistEscapeCode ? [next, ...current] : [...current, next], []);
        }
    }
 
    return output.join('');
};
 
module.exports = (string, begin, end) => {
    const characters = [...string.normalize()];
    const ansiCodes = [];
 
    end = 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 < end) {
                isInsideEscape = true;
                if (ansiCode !== undefined) {
                    ansiCodes.push(ansiCode);
                }
            }
        } else if (isInsideEscape && character === 'm') {
            isInsideEscape = false;
            leftEscape = true;
        }
 
        if (!isInsideEscape && !leftEscape) {
            ++visible;
        }
 
        if (!astralRegex({exact: true}).test(character) && isFullwidthCodePoint(character.codePointAt())) {
            ++visible;
        }
 
        if (visible > begin && visible <= end) {
            output += character;
        } else if (visible === begin && !isInsideEscape && ansiCode !== undefined) {
            output = checkAnsi(ansiCodes);
        } else if (visible >= end) {
            output += checkAnsi(ansiCodes, true, ansiCode);
            break;
        }
    }
 
    return output;
};