slice-ansi 4.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +143 -79
- package/package.json +11 -10
- package/readme.md +8 -20
package/index.js
CHANGED
@@ -1,103 +1,167 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
const astralRegex = require('astral-regex');
|
4
|
-
const ansiStyles = require('ansi-styles');
|
5
|
-
|
6
|
-
const ESCAPES = [
|
7
|
-
'\u001B',
|
8
|
-
'\u009B'
|
9
|
-
];
|
10
|
-
|
11
|
-
const wrapAnsi = code => `${ESCAPES[0]}[${code}m`;
|
12
|
-
|
13
|
-
const checkAnsi = (ansiCodes, isEscapes, endAnsiCode) => {
|
14
|
-
let output = [];
|
15
|
-
ansiCodes = [...ansiCodes];
|
16
|
-
|
17
|
-
for (let ansiCode of ansiCodes) {
|
18
|
-
const ansiCodeOrigin = ansiCode;
|
19
|
-
if (ansiCode.includes(';')) {
|
20
|
-
ansiCode = ansiCode.split(';')[0][0] + '0';
|
21
|
-
}
|
1
|
+
import ansiStyles from 'ansi-styles';
|
2
|
+
import isFullwidthCodePoint from 'is-fullwidth-code-point';
|
22
3
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
4
|
+
// \x1b and \x9b
|
5
|
+
const ESCAPES = new Set([27, 155]);
|
6
|
+
|
7
|
+
const CODE_POINT_0 = '0'.codePointAt(0);
|
8
|
+
const CODE_POINT_9 = '9'.codePointAt(0);
|
9
|
+
|
10
|
+
const endCodesSet = new Set();
|
11
|
+
const endCodesMap = new Map();
|
12
|
+
for (const [start, end] of ansiStyles.codes) {
|
13
|
+
endCodesSet.add(ansiStyles.color.ansi(end));
|
14
|
+
endCodesMap.set(ansiStyles.color.ansi(start), ansiStyles.color.ansi(end));
|
15
|
+
}
|
16
|
+
|
17
|
+
function getEndCode(code) {
|
18
|
+
if (endCodesSet.has(code)) {
|
19
|
+
return code;
|
20
|
+
}
|
21
|
+
|
22
|
+
if (endCodesMap.has(code)) {
|
23
|
+
return endCodesMap.get(code);
|
24
|
+
}
|
25
|
+
|
26
|
+
code = code.slice(2);
|
27
|
+
if (code.includes(';')) {
|
28
|
+
code = code[0] + '0';
|
29
|
+
}
|
30
|
+
|
31
|
+
const returnValue = ansiStyles.codes.get(Number.parseInt(code, 10));
|
32
|
+
if (returnValue) {
|
33
|
+
return ansiStyles.color.ansi(returnValue);
|
34
|
+
}
|
35
|
+
|
36
|
+
return ansiStyles.reset.open;
|
37
|
+
}
|
38
|
+
|
39
|
+
function findNumberIndex(string) {
|
40
|
+
for (let index = 0; index < string.length; index++) {
|
41
|
+
const codePoint = string.codePointAt(index);
|
42
|
+
if (codePoint >= CODE_POINT_0 && codePoint <= CODE_POINT_9) {
|
43
|
+
return index;
|
36
44
|
}
|
37
45
|
}
|
38
46
|
|
39
|
-
|
40
|
-
|
47
|
+
return -1;
|
48
|
+
}
|
41
49
|
|
42
|
-
|
43
|
-
|
44
|
-
|
50
|
+
function parseAnsiCode(string, offset) {
|
51
|
+
string = string.slice(offset, offset + 19);
|
52
|
+
const startIndex = findNumberIndex(string);
|
53
|
+
if (startIndex !== -1) {
|
54
|
+
let endIndex = string.indexOf('m', startIndex);
|
55
|
+
if (endIndex === -1) {
|
56
|
+
endIndex = string.length;
|
45
57
|
}
|
58
|
+
|
59
|
+
return string.slice(0, endIndex + 1);
|
46
60
|
}
|
61
|
+
}
|
62
|
+
|
63
|
+
function tokenize(string, endCharacter = Number.POSITIVE_INFINITY) {
|
64
|
+
const returnValue = [];
|
65
|
+
|
66
|
+
let index = 0;
|
67
|
+
let visibleCount = 0;
|
68
|
+
while (index < string.length) {
|
69
|
+
const codePoint = string.codePointAt(index);
|
70
|
+
|
71
|
+
if (ESCAPES.has(codePoint)) {
|
72
|
+
const code = parseAnsiCode(string, index);
|
73
|
+
if (code) {
|
74
|
+
returnValue.push({
|
75
|
+
type: 'ansi',
|
76
|
+
code,
|
77
|
+
endCode: getEndCode(code),
|
78
|
+
});
|
79
|
+
index += code.length;
|
80
|
+
continue;
|
81
|
+
}
|
82
|
+
}
|
47
83
|
|
48
|
-
|
49
|
-
|
84
|
+
const isFullWidth = isFullwidthCodePoint(codePoint);
|
85
|
+
const character = String.fromCodePoint(codePoint);
|
50
86
|
|
51
|
-
|
52
|
-
|
53
|
-
|
87
|
+
returnValue.push({
|
88
|
+
type: 'character',
|
89
|
+
value: character,
|
90
|
+
isFullWidth,
|
91
|
+
});
|
54
92
|
|
55
|
-
|
56
|
-
|
57
|
-
let ansiCode;
|
58
|
-
let visible = 0;
|
59
|
-
let output = '';
|
93
|
+
index += character.length;
|
94
|
+
visibleCount += isFullWidth ? 2 : character.length;
|
60
95
|
|
61
|
-
|
62
|
-
|
96
|
+
if (visibleCount >= endCharacter) {
|
97
|
+
break;
|
98
|
+
}
|
99
|
+
}
|
63
100
|
|
64
|
-
|
65
|
-
|
66
|
-
ansiCode = code && code.length > 0 ? code[0] : undefined;
|
101
|
+
return returnValue;
|
102
|
+
}
|
67
103
|
|
68
|
-
|
69
|
-
|
104
|
+
function reduceAnsiCodes(codes) {
|
105
|
+
let returnValue = [];
|
70
106
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
} else if (
|
76
|
-
|
77
|
-
|
107
|
+
for (const code of codes) {
|
108
|
+
if (code.code === ansiStyles.reset.open) {
|
109
|
+
// Reset code, disable all codes
|
110
|
+
returnValue = [];
|
111
|
+
} else if (endCodesSet.has(code.code)) {
|
112
|
+
// This is an end code, disable all matching start codes
|
113
|
+
returnValue = returnValue.filter(returnValueCode => returnValueCode.endCode !== code.code);
|
114
|
+
} else {
|
115
|
+
// This is a start code. Disable all styles this "overrides", then enable it
|
116
|
+
returnValue = returnValue.filter(returnValueCode => returnValueCode.endCode !== code.endCode);
|
117
|
+
returnValue.push(code);
|
78
118
|
}
|
119
|
+
}
|
120
|
+
|
121
|
+
return returnValue;
|
122
|
+
}
|
79
123
|
|
80
|
-
|
81
|
-
|
124
|
+
function undoAnsiCodes(codes) {
|
125
|
+
const reduced = reduceAnsiCodes(codes);
|
126
|
+
const endCodes = reduced.map(({endCode}) => endCode);
|
127
|
+
return endCodes.reverse().join('');
|
128
|
+
}
|
129
|
+
|
130
|
+
export default function sliceAnsi(string, start, end) {
|
131
|
+
const tokens = tokenize(string, end);
|
132
|
+
let activeCodes = [];
|
133
|
+
let position = 0;
|
134
|
+
let returnValue = '';
|
135
|
+
let include = false;
|
136
|
+
|
137
|
+
for (const token of tokens) {
|
138
|
+
if (end !== undefined && position >= end) {
|
139
|
+
break;
|
82
140
|
}
|
83
141
|
|
84
|
-
if (
|
85
|
-
|
142
|
+
if (token.type === 'ansi') {
|
143
|
+
activeCodes.push(token);
|
144
|
+
if (include) {
|
145
|
+
returnValue += token.code;
|
146
|
+
}
|
147
|
+
} else {
|
148
|
+
// Character
|
149
|
+
if (!include && position >= start) {
|
150
|
+
include = true;
|
151
|
+
// Simplify active codes
|
152
|
+
activeCodes = reduceAnsiCodes(activeCodes);
|
153
|
+
returnValue = activeCodes.map(({code}) => code).join('');
|
154
|
+
}
|
86
155
|
|
87
|
-
if (
|
88
|
-
|
156
|
+
if (include) {
|
157
|
+
returnValue += token.value;
|
89
158
|
}
|
90
|
-
}
|
91
159
|
|
92
|
-
|
93
|
-
output += character;
|
94
|
-
} else if (visible === begin && !isInsideEscape && ansiCode !== undefined) {
|
95
|
-
output = checkAnsi(ansiCodes);
|
96
|
-
} else if (visible >= stringEnd) {
|
97
|
-
output += checkAnsi(ansiCodes, true, ansiCode);
|
98
|
-
break;
|
160
|
+
position += token.isFullWidth ? 2 : token.value.length;
|
99
161
|
}
|
100
162
|
}
|
101
163
|
|
102
|
-
|
103
|
-
|
164
|
+
// Disable active codes at the end
|
165
|
+
returnValue += undoAnsiCodes(activeCodes);
|
166
|
+
return returnValue;
|
167
|
+
}
|
package/package.json
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
{
|
2
2
|
"name": "slice-ansi",
|
3
|
-
"version": "
|
3
|
+
"version": "6.0.0",
|
4
4
|
"description": "Slice a string with ANSI escape codes",
|
5
5
|
"license": "MIT",
|
6
6
|
"repository": "chalk/slice-ansi",
|
7
7
|
"funding": "https://github.com/chalk/slice-ansi?sponsor=1",
|
8
|
+
"type": "module",
|
9
|
+
"exports": "./index.js",
|
8
10
|
"engines": {
|
9
|
-
"node": ">=
|
11
|
+
"node": ">=14.16"
|
10
12
|
},
|
11
13
|
"scripts": {
|
12
14
|
"test": "xo && ava"
|
@@ -38,15 +40,14 @@
|
|
38
40
|
"text"
|
39
41
|
],
|
40
42
|
"dependencies": {
|
41
|
-
"ansi-styles": "^
|
42
|
-
"
|
43
|
-
"is-fullwidth-code-point": "^3.0.0"
|
43
|
+
"ansi-styles": "^6.2.1",
|
44
|
+
"is-fullwidth-code-point": "^4.0.0"
|
44
45
|
},
|
45
46
|
"devDependencies": {
|
46
|
-
"ava": "^2.
|
47
|
-
"chalk": "^
|
48
|
-
"random-item": "^
|
49
|
-
"strip-ansi": "^
|
50
|
-
"xo": "^0.
|
47
|
+
"ava": "^5.2.0",
|
48
|
+
"chalk": "^5.2.0",
|
49
|
+
"random-item": "^4.0.1",
|
50
|
+
"strip-ansi": "^7.0.1",
|
51
|
+
"xo": "^0.53.1"
|
51
52
|
}
|
52
53
|
}
|
package/readme.md
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
# slice-ansi [](https://github.com/xojs/xo)
|
2
2
|
|
3
3
|
> Slice a string with [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles)
|
4
4
|
|
5
5
|
## Install
|
6
6
|
|
7
|
-
```
|
8
|
-
|
7
|
+
```sh
|
8
|
+
npm install slice-ansi
|
9
9
|
```
|
10
10
|
|
11
11
|
## Usage
|
12
12
|
|
13
13
|
```js
|
14
|
-
|
15
|
-
|
14
|
+
import chalk from 'chalk';
|
15
|
+
import sliceAnsi from 'slice-ansi';
|
16
16
|
|
17
17
|
const string = 'The quick brown ' + chalk.red('fox jumped over ') +
|
18
18
|
'the lazy ' + chalk.green('dog and then ran away with the unicorn.');
|
@@ -22,7 +22,7 @@ console.log(sliceAnsi(string, 20, 30));
|
|
22
22
|
|
23
23
|
## API
|
24
24
|
|
25
|
-
### sliceAnsi(string,
|
25
|
+
### sliceAnsi(string, startSlice, endSlice?)
|
26
26
|
|
27
27
|
#### string
|
28
28
|
|
@@ -30,11 +30,11 @@ Type: `string`
|
|
30
30
|
|
31
31
|
String with ANSI escape codes. Like one styled by [`chalk`](https://github.com/chalk/chalk).
|
32
32
|
|
33
|
-
####
|
33
|
+
#### startSlice
|
34
34
|
|
35
35
|
Type: `number`
|
36
36
|
|
37
|
-
Zero-based index at which to
|
37
|
+
Zero-based index at which to start the slice.
|
38
38
|
|
39
39
|
#### endSlice
|
40
40
|
|
@@ -52,15 +52,3 @@ Zero-based index at which to end the slice.
|
|
52
52
|
|
53
53
|
- [Sindre Sorhus](https://github.com/sindresorhus)
|
54
54
|
- [Josh Junon](https://github.com/qix-)
|
55
|
-
|
56
|
-
---
|
57
|
-
|
58
|
-
<div align="center">
|
59
|
-
<b>
|
60
|
-
<a href="https://tidelift.com/subscription/pkg/npm-slice_ansi?utm_source=npm-slice-ansi&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
|
61
|
-
</b>
|
62
|
-
<br>
|
63
|
-
<sub>
|
64
|
-
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
|
65
|
-
</sub>
|
66
|
-
</div>
|