@alcalzone/ansi-tokenize 0.1.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/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # @alcalzone/ansi-tokenize
2
+
3
+ > Efficiently modify strings containing [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles)
4
+
5
+ If you find yourself modifying styled strings repeatedly, alternatives like [`slice-ansi`](https://github.com/chalk/slice-ansi/) may end up doing a lot of unnecessary work by re-parsing the string each time. This module provides a way to parse the string into an array of tokens (characters or ANSI codes), which can then be modified and re-serialized into a styled string.
6
+
7
+ ## Install
8
+
9
+ ```
10
+ $ npm install @alcalzone/ansi-tokenize
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Tokenize a string
16
+
17
+ ```js
18
+ import { tokenize } from "@alcalzone/ansi-tokenize";
19
+
20
+ // red "foo", followed by unstyled "bar"
21
+ const str = "\x1B[31mfoo\x1B[39mbar";
22
+ const tokens = tokenize(str);
23
+
24
+ // tokens will now look like this:
25
+ [
26
+ {
27
+ type: "ansi",
28
+ code: "\x1B[31m",
29
+ endCode: "\x1B[39m",
30
+ },
31
+ {
32
+ type: "char",
33
+ value: "f",
34
+ fullWidth: false,
35
+ },
36
+ {
37
+ type: "char",
38
+ value: "o",
39
+ fullWidth: false,
40
+ },
41
+ {
42
+ type: "char",
43
+ value: "o",
44
+ fullWidth: false,
45
+ },
46
+ {
47
+ type: "ansi",
48
+ code: "\x1B[39m",
49
+ endCode: "\x1B[39m",
50
+ },
51
+ {
52
+ type: "char",
53
+ value: "b",
54
+ fullWidth: false,
55
+ },
56
+ {
57
+ type: "char",
58
+ value: "a",
59
+ fullWidth: false,
60
+ },
61
+ {
62
+ type: "char",
63
+ value: "r",
64
+ fullWidth: false,
65
+ },
66
+ ];
67
+ ```
68
+
69
+ Each token is either a character
70
+
71
+ ```ts
72
+ export interface Char {
73
+ type: "char";
74
+ value: string;
75
+ fullWidth: boolean;
76
+ }
77
+ ```
78
+
79
+ where
80
+
81
+ - `value` is the string representation of the character
82
+ - `fullWidth` is `true` if the character is full width (takes up 2 characters in monospace, like CJK characters)
83
+
84
+ or an ANSI code
85
+
86
+ ```ts
87
+ export interface AnsiCode {
88
+ type: "ansi";
89
+ code: string;
90
+ endCode: string;
91
+ }
92
+ ```
93
+
94
+ where
95
+
96
+ - `code` is the ANSI code that starts the style
97
+ - and `endCode` is the corresponding ANSI code that ends the style.
98
+
99
+ An `AnsiCode` can also be an end code, in which case `code` and `endCode` will be the same.
100
+
101
+ ### Convert an array of tokens into an array of "styled" chars
102
+
103
+ This representation is a 1:1 mapping of the original string, but not very useful for modifying the string. The `styledCharsFromTokens` function converts a token array to an array of characters, where each character has an all currently active styles associated with it:
104
+
105
+ ```ts
106
+ export interface StyledChar {
107
+ type: "char";
108
+ value: string;
109
+ fullWidth: boolean;
110
+ styles: AnsiCode[];
111
+ }
112
+ ```
113
+
114
+ Using the above example:
115
+
116
+ ```js
117
+ import { tokenize, styledCharsFromTokens } from "@alcalzone/ansi-tokenize";
118
+
119
+ // red "foo", followed by unstyled "bar"
120
+ const str = "\x1B[31mfoo\x1B[39mbar";
121
+ const tokens = tokenize(str);
122
+
123
+ const styledChars = styledCharsFromTokens(tokens);
124
+
125
+ // styledChars will contain the following:
126
+ [
127
+ {
128
+ type: "char",
129
+ value: "f",
130
+ fullWidth: false,
131
+ styles: [
132
+ {
133
+ type: "ansi",
134
+ code: "\x1B[31m",
135
+ endCode: "\x1B[39m",
136
+ },
137
+ ],
138
+ },
139
+ {
140
+ type: "char",
141
+ value: "o",
142
+ fullWidth: false,
143
+ styles: [
144
+ {
145
+ type: "ansi",
146
+ code: "\x1B[31m",
147
+ endCode: "\x1B[39m",
148
+ },
149
+ ],
150
+ },
151
+ {
152
+ type: "char",
153
+ value: "o",
154
+ fullWidth: false,
155
+ styles: [
156
+ {
157
+ type: "ansi",
158
+ code: "\x1B[31m",
159
+ endCode: "\x1B[39m",
160
+ },
161
+ ],
162
+ },
163
+ {
164
+ type: "char",
165
+ value: "b",
166
+ fullWidth: false,
167
+ styles: [],
168
+ },
169
+ {
170
+ type: "char",
171
+ value: "a",
172
+ fullWidth: false,
173
+ styles: [],
174
+ },
175
+ {
176
+ type: "char",
177
+ value: "r",
178
+ fullWidth: false,
179
+ styles: [],
180
+ },
181
+ ];
182
+ ```
183
+
184
+ ### Modify an array of styled characters
185
+
186
+ For modification simply edit the items in the array as necessary, as long as the following rules are followed:
187
+
188
+ 1. The `code` and `endCode` properties must match. You can use the `ansi-styles` module to do this.
189
+ 2. The `fullWidth` property must be correct. You can use the `is-fullwidth-code-point` module to do this, or if working with multiple strings, turn those into styled char arrays first.
190
+
191
+ E.g. to make the first `o` blue and bold:
192
+
193
+ ```js
194
+ import ansiStyles from "ansi-styles";
195
+
196
+ // ... include the above code
197
+
198
+ styledChars[1].styles = [
199
+ {
200
+ type: "ansi",
201
+ code: ansiStyles.blue.open,
202
+ endCode: ansiStyles.blue.close,
203
+ },
204
+ {
205
+ type: "ansi",
206
+ code: ansiStyles.bold.open,
207
+ endCode: ansiStyles.bold.close,
208
+ },
209
+ ];
210
+ ```
211
+
212
+ ### Serialize a styled character array back to a string
213
+
214
+ The `styledCharsToString` function converts a styled character array back to a string:
215
+
216
+ ```js
217
+ import { styledCharsToString } from "@alcalzone/ansi-tokenize";
218
+
219
+ // ... include the above code
220
+
221
+ const strOut = styledCharsToString(styledChars);
222
+
223
+ // str will now be '\x1B[31mf\x1B[34m\x1B[1mo\x1B[22m\x1B[31mo\x1B[39mbar'
224
+ ```
225
+
226
+ This automatically figures out the least amount of escape codes necessary to achieve the desired result, as long as the `styles` arrays contain no unnecessary styles, e.g. blue + red foreground.
227
+
228
+ ## Changelog
229
+
230
+ <!--
231
+ Placeholder for next release:
232
+ ### __WORK IN PROGRESS__
233
+ -->
234
+ ### 0.1.0 (2023-03-20)
235
+
236
+ Initial release
@@ -0,0 +1,5 @@
1
+ import type { AnsiCode } from "./tokenize.js";
2
+ export declare const ESCAPES: Set<number>;
3
+ export declare const endCodesSet: Set<string>;
4
+ export declare function getEndCode(code: string): string;
5
+ export declare function ansiCodesToString(codes: AnsiCode[]): string;
@@ -0,0 +1,29 @@
1
+ import ansiStyles from "ansi-styles";
2
+ export const ESCAPES = new Set([27, 155]); // \x1b and \x9b
3
+ export const endCodesSet = new Set();
4
+ const endCodesMap = new Map();
5
+ for (const [start, end] of ansiStyles.codes) {
6
+ endCodesSet.add(ansiStyles.color.ansi(end));
7
+ endCodesMap.set(ansiStyles.color.ansi(start), ansiStyles.color.ansi(end));
8
+ }
9
+ export function getEndCode(code) {
10
+ if (endCodesSet.has(code))
11
+ return code;
12
+ if (endCodesMap.has(code))
13
+ return endCodesMap.get(code);
14
+ code = code.slice(2);
15
+ if (code.includes(";")) {
16
+ code = code[0] + "0";
17
+ }
18
+ const ret = ansiStyles.codes.get(parseInt(code, 10));
19
+ if (ret) {
20
+ return ansiStyles.color.ansi(ret);
21
+ }
22
+ else {
23
+ return ansiStyles.reset.open;
24
+ }
25
+ }
26
+ export function ansiCodesToString(codes) {
27
+ return codes.map((code) => code.code).join("");
28
+ }
29
+ //# sourceMappingURL=ansiCodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansiCodes.js","sourceRoot":"","sources":["../src/ansiCodes.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAGrC,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB;AAE3D,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;AAC7C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;AAC9C,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,EAAE;IAC5C,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;CAC1E;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;IAEzD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACvB,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;KACrB;IACD,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IACrD,IAAI,GAAG,EAAE;QACR,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KAClC;SAAM;QACN,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;KAC7B;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAiB;IAClD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { AnsiCode } from "./tokenize.js";
2
+ /**
3
+ * Returns the minimum amount of ANSI codes necessary to get from the compound style `from` to `to`.
4
+ * Both `from` and `to` are expected to be reduced.
5
+ */
6
+ export declare function diffAnsiCodes(from: AnsiCode[], to: AnsiCode[]): AnsiCode[];
package/build/diff.js ADDED
@@ -0,0 +1,17 @@
1
+ import { undoAnsiCodes } from "./undo.js";
2
+ /**
3
+ * Returns the minimum amount of ANSI codes necessary to get from the compound style `from` to `to`.
4
+ * Both `from` and `to` are expected to be reduced.
5
+ */
6
+ export function diffAnsiCodes(from, to) {
7
+ const endCodesInTo = new Set(to.map((code) => code.endCode));
8
+ const startCodesInFrom = new Set(from.map((code) => code.code));
9
+ return [
10
+ // Ignore all styles in `from` that are not overwritten or removed by `to`
11
+ // Disable all styles in `from` that are removed in `to`
12
+ ...undoAnsiCodes(from.filter((code) => !endCodesInTo.has(code.endCode))),
13
+ // Add all styles in `to` that don't exist in `from`
14
+ ...to.filter((code) => !startCodesInFrom.has(code.code)),
15
+ ];
16
+ }
17
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAgB,EAAE,EAAc;IAC7D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7D,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhE,OAAO;QACN,0EAA0E;QAC1E,wDAAwD;QACxD,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACxE,oDAAoD;QACpD,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KACxD,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { ansiCodesToString } from "./ansiCodes.js";
2
+ export { diffAnsiCodes } from "./diff.js";
3
+ export { reduceAnsiCodes, reduceAnsiCodesIncremental } from "./reduce.js";
4
+ export * from "./styledChars.js";
5
+ export * from "./tokenize.js";
6
+ export { undoAnsiCodes } from "./undo.js";
package/build/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { ansiCodesToString } from "./ansiCodes.js";
2
+ export { diffAnsiCodes } from "./diff.js";
3
+ export { reduceAnsiCodes, reduceAnsiCodesIncremental } from "./reduce.js";
4
+ export * from "./styledChars.js";
5
+ export * from "./tokenize.js";
6
+ export { undoAnsiCodes } from "./undo.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAC1E,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { AnsiCode } from "./tokenize.js";
2
+ /** Reduces the given array of ANSI codes to the minimum necessary to render with the same style */
3
+ export declare function reduceAnsiCodes(codes: AnsiCode[]): AnsiCode[];
4
+ /** Like {@link reduceAnsiCodes}, but assumes that `codes` is already reduced. Further reductions are only done for the items in `newCodes`. */
5
+ export declare function reduceAnsiCodesIncremental(codes: AnsiCode[], newCodes: AnsiCode[]): AnsiCode[];
@@ -0,0 +1,27 @@
1
+ import ansiStyles from "ansi-styles";
2
+ import { endCodesSet } from "./ansiCodes.js";
3
+ /** Reduces the given array of ANSI codes to the minimum necessary to render with the same style */
4
+ export function reduceAnsiCodes(codes) {
5
+ return reduceAnsiCodesIncremental([], codes);
6
+ }
7
+ /** Like {@link reduceAnsiCodes}, but assumes that `codes` is already reduced. Further reductions are only done for the items in `newCodes`. */
8
+ export function reduceAnsiCodesIncremental(codes, newCodes) {
9
+ let ret = [...codes];
10
+ for (const code of newCodes) {
11
+ if (code.code === ansiStyles.reset.open) {
12
+ // Reset code, disable all codes
13
+ ret = [];
14
+ }
15
+ else if (endCodesSet.has(code.code)) {
16
+ // This is an end code, disable all matching start codes
17
+ ret = ret.filter((retCode) => retCode.endCode !== code.code);
18
+ }
19
+ else {
20
+ // This is a start code. Disable all styles this "overrides", then enable it
21
+ ret = ret.filter((retCode) => retCode.endCode !== code.endCode);
22
+ ret.push(code);
23
+ }
24
+ }
25
+ return ret;
26
+ }
27
+ //# sourceMappingURL=reduce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduce.js","sourceRoot":"","sources":["../src/reduce.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,mGAAmG;AACnG,MAAM,UAAU,eAAe,CAAC,KAAiB;IAChD,OAAO,0BAA0B,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC9C,CAAC;AAED,+IAA+I;AAC/I,MAAM,UAAU,0BAA0B,CAAC,KAAiB,EAAE,QAAoB;IACjF,IAAI,GAAG,GAAe,CAAC,GAAG,KAAK,CAAC,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE;QAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE;YACxC,gCAAgC;YAChC,GAAG,GAAG,EAAE,CAAC;SACT;aAAM,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACtC,wDAAwD;YACxD,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;SAC7D;aAAM;YACN,4EAA4E;YAC5E,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC;YAChE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACf;KACD;IAED,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { AnsiCode, Char, Token } from "./tokenize.js";
2
+ export interface StyledChar extends Char {
3
+ styles: AnsiCode[];
4
+ }
5
+ export declare function styledCharsFromTokens(tokens: Token[]): StyledChar[];
6
+ export declare function styledCharsToString(chars: StyledChar[]): string;
@@ -0,0 +1,34 @@
1
+ import { ansiCodesToString } from "./ansiCodes.js";
2
+ import { diffAnsiCodes } from "./diff.js";
3
+ import { reduceAnsiCodesIncremental } from "./reduce.js";
4
+ export function styledCharsFromTokens(tokens) {
5
+ let codes = [];
6
+ const ret = [];
7
+ for (const token of tokens) {
8
+ if (token.type === "ansi") {
9
+ codes = reduceAnsiCodesIncremental(codes, [token]);
10
+ }
11
+ else if (token.type === "char") {
12
+ ret.push({
13
+ ...token,
14
+ styles: [...codes],
15
+ });
16
+ }
17
+ }
18
+ return ret;
19
+ }
20
+ export function styledCharsToString(chars) {
21
+ let ret = "";
22
+ for (let i = 0; i < chars.length; i++) {
23
+ const char = chars[i];
24
+ if (i === 0) {
25
+ ret += ansiCodesToString(char.styles);
26
+ }
27
+ else {
28
+ ret += ansiCodesToString(diffAnsiCodes(chars[i - 1].styles, char.styles));
29
+ }
30
+ ret += char.value;
31
+ }
32
+ return ret;
33
+ }
34
+ //# sourceMappingURL=styledChars.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styledChars.js","sourceRoot":"","sources":["../src/styledChars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAOzD,MAAM,UAAU,qBAAqB,CAAC,MAAe;IACpD,IAAI,KAAK,GAAe,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE;YAC1B,KAAK,GAAG,0BAA0B,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;SACnD;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE;YACjC,GAAG,CAAC,IAAI,CAAC;gBACR,GAAG,KAAK;gBACR,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC;aAClB,CAAC,CAAC;SACH;KACD;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAmB;IACtD,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,EAAE;YACZ,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACtC;aAAM;YACN,GAAG,IAAI,iBAAiB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;SAC1E;QACD,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC;KAClB;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface AnsiCode {
2
+ type: "ansi";
3
+ code: string;
4
+ endCode: string;
5
+ }
6
+ export interface Char {
7
+ type: "char";
8
+ value: string;
9
+ fullWidth: boolean;
10
+ }
11
+ export type Token = AnsiCode | Char;
12
+ export declare function tokenize(str: string, endChar?: number): Token[];
@@ -0,0 +1,56 @@
1
+ import isFullwidthCodePoint from "is-fullwidth-code-point";
2
+ import { ESCAPES, getEndCode } from "./ansiCodes.js";
3
+ function findNumberIndex(str) {
4
+ for (let index = 0; index < str.length; index++) {
5
+ const charCode = str.charCodeAt(index);
6
+ if (charCode >= 48 && charCode <= 57) {
7
+ return index;
8
+ }
9
+ }
10
+ return -1;
11
+ }
12
+ function parseAnsiCode(string, offset) {
13
+ string = string.slice(offset, offset + 19);
14
+ const startIndex = findNumberIndex(string);
15
+ if (startIndex !== -1) {
16
+ let endIndex = string.indexOf("m", startIndex);
17
+ if (endIndex === -1) {
18
+ endIndex = string.length;
19
+ }
20
+ return string.slice(0, endIndex + 1);
21
+ }
22
+ }
23
+ export function tokenize(str, endChar = Number.POSITIVE_INFINITY) {
24
+ const ret = [];
25
+ let index = 0;
26
+ let visible = 0;
27
+ while (index < str.length) {
28
+ const codePoint = str.codePointAt(index);
29
+ if (ESCAPES.has(codePoint)) {
30
+ const code = parseAnsiCode(str, index);
31
+ if (code) {
32
+ ret.push({
33
+ type: "ansi",
34
+ code,
35
+ endCode: getEndCode(code),
36
+ });
37
+ index += code.length;
38
+ continue;
39
+ }
40
+ }
41
+ const fullWidth = isFullwidthCodePoint(codePoint);
42
+ const character = String.fromCodePoint(codePoint);
43
+ ret.push({
44
+ type: "char",
45
+ value: character,
46
+ fullWidth,
47
+ });
48
+ index += character.length;
49
+ visible += fullWidth ? 2 : character.length;
50
+ if (visible >= endChar) {
51
+ break;
52
+ }
53
+ }
54
+ return ret;
55
+ }
56
+ //# sourceMappingURL=tokenize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenize.js","sourceRoot":"","sources":["../src/tokenize.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAgBrD,SAAS,eAAe,CAAC,GAAW;IACnC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAChD,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,QAAQ,IAAI,EAAE,IAAI,QAAQ,IAAI,EAAE,EAAE;YACrC,OAAO,KAAK,CAAC;SACb;KACD;IAED,OAAO,CAAC,CAAC,CAAC;AACX,CAAC;AAED,SAAS,aAAa,CAAC,MAAc,EAAE,MAAc;IACpD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE;QACtB,IAAI,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC/C,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE;YACpB,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;SACzB;QAED,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;KACrC;AACF,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,UAAkB,MAAM,CAAC,iBAAiB;IAC/E,MAAM,GAAG,GAAY,EAAE,CAAC;IAExB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,OAAO,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE;QAC1B,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,KAAK,CAAE,CAAC;QAE1C,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;YAC3B,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACvC,IAAI,IAAI,EAAE;gBACT,GAAG,CAAC,IAAI,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI;oBACJ,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC;iBACzB,CAAC,CAAC;gBACH,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;gBACrB,SAAS;aACT;SACD;QAED,MAAM,SAAS,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAElD,GAAG,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,SAAS;YAChB,SAAS;SACT,CAAC,CAAC;QAEH,KAAK,IAAI,SAAS,CAAC,MAAM,CAAC;QAC1B,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC;QAE5C,IAAI,OAAO,IAAI,OAAO,EAAE;YACvB,MAAM;SACN;KACD;IAED,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AnsiCode } from "./tokenize.js";
2
+ /** Returns the combination of ANSI codes needed to undo the given ANSI codes */
3
+ export declare function undoAnsiCodes(codes: AnsiCode[]): AnsiCode[];
package/build/undo.js ADDED
@@ -0,0 +1,11 @@
1
+ import { reduceAnsiCodes } from "./reduce.js";
2
+ /** Returns the combination of ANSI codes needed to undo the given ANSI codes */
3
+ export function undoAnsiCodes(codes) {
4
+ return reduceAnsiCodes(codes)
5
+ .reverse()
6
+ .map((code) => ({
7
+ ...code,
8
+ code: code.endCode,
9
+ }));
10
+ }
11
+ //# sourceMappingURL=undo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.js","sourceRoot":"","sources":["../src/undo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC9C,OAAO,eAAe,CAAC,KAAK,CAAC;SAC3B,OAAO,EAAE;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACf,GAAG,IAAI;QACP,IAAI,EAAE,IAAI,CAAC,OAAO;KAClB,CAAC,CAAC,CAAC;AACN,CAAC"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@alcalzone/ansi-tokenize",
3
+ "version": "0.1.0",
4
+ "description": "Efficiently modify strings containing ANSI escape codes",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "author": {
9
+ "name": "AlCalzone",
10
+ "email": "d.griesel@gmx.net"
11
+ },
12
+ "license": "MIT",
13
+ "type": "module",
14
+ "module": "build/index.js",
15
+ "types": "build/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./build/index.d.ts",
19
+ "import": "./build/index.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "build"
25
+ ],
26
+ "engines": {
27
+ "node": ">=16"
28
+ },
29
+ "devDependencies": {
30
+ "@alcalzone/release-script": "~3.5.9",
31
+ "@alcalzone/release-script-plugin-license": "~3.5.9",
32
+ "@tsconfig/node16": "^1.0.3",
33
+ "@types/node": "16",
34
+ "@typescript-eslint/eslint-plugin": "^5.55.0",
35
+ "@typescript-eslint/parser": "^5.55.0",
36
+ "ava": "^5.2.0",
37
+ "eslint": "^8.36.0",
38
+ "eslint-config-prettier": "^8.7.0",
39
+ "eslint-plugin-prettier": "^4.2.1",
40
+ "prettier": "^2.8.4",
41
+ "source-map-support": "^0.5.21",
42
+ "ts-node": "^10.9.1",
43
+ "typescript": "^5.0.2"
44
+ },
45
+ "dependencies": {
46
+ "ansi-styles": "^6.2.1",
47
+ "is-fullwidth-code-point": "^4.0.0"
48
+ },
49
+ "scripts": {
50
+ "prepare": "tsc -p tsconfig.build.json",
51
+ "build": "tsc -p tsconfig.build.json",
52
+ "test": "ava",
53
+ "lint": "eslint .",
54
+ "release": "release-script"
55
+ },
56
+ "ava": {
57
+ "extensions": {
58
+ "ts": "module"
59
+ },
60
+ "nodeArguments": [
61
+ "--loader=ts-node/esm"
62
+ ]
63
+ },
64
+ "packageManager": "yarn@3.5.0"
65
+ }