@ansi-tools/parser 0.0.0 → 0.0.2
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 +6 -2
- package/dist/escaped.d.ts +4 -1
- package/dist/escaped.js +20 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{parse-ClmKWMZx.js → parse-BE4NnMTj.js} +30 -21
- package/dist/{parse-BirjVUvQ.d.ts → parse-BtpkwNjf.d.ts} +1 -0
- package/package.json +20 -9
- package/src/constants.ts +0 -40
- package/src/escaped.ts +0 -4
- package/src/index.ts +0 -4
- package/src/parse.escaped.test.ts +0 -86
- package/src/parse.test.ts +0 -86
- package/src/parse.ts +0 -111
- package/src/parsers/csi.test.ts +0 -55
- package/src/parsers/csi.ts +0 -54
- package/src/parsers/dcs.test.ts +0 -47
- package/src/parsers/dcs.ts +0 -36
- package/src/parsers/dec.test.ts +0 -24
- package/src/parsers/dec.ts +0 -30
- package/src/parsers/esc.test.ts +0 -19
- package/src/parsers/esc.ts +0 -6
- package/src/parsers/osc.test.ts +0 -36
- package/src/parsers/osc.ts +0 -29
- package/src/tokenize.escaped.test.ts +0 -410
- package/src/tokenize.escaped.ts +0 -191
- package/src/tokenize.test.ts +0 -118
- package/src/tokenize.ts +0 -140
- package/src/types.ts +0 -24
- package/tsconfig.json +0 -16
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ Parser for ANSI escape sequences.
|
|
|
20
20
|
- ✅ Zero dependencies
|
|
21
21
|
- ✅ Separate optimized modules for raw and escaped input
|
|
22
22
|
|
|
23
|
+
Used by [ansi.tools](https://ansi.tools).
|
|
24
|
+
|
|
23
25
|
## Installation
|
|
24
26
|
|
|
25
27
|
```bash
|
|
@@ -145,10 +147,11 @@ for (const code of codes) {
|
|
|
145
147
|
## Type Definitions
|
|
146
148
|
|
|
147
149
|
```ts
|
|
148
|
-
function parse(input: string): CODE[];
|
|
149
150
|
function tokenize(input: string): TOKEN[];
|
|
150
|
-
function
|
|
151
|
+
function parse(input: string): CODE[];
|
|
152
|
+
|
|
151
153
|
function* tokenizer(input: string): Generator<TOKEN>;
|
|
154
|
+
function* parser(tokens: Generator<TOKEN>): Generator<CODE>;
|
|
152
155
|
```
|
|
153
156
|
|
|
154
157
|
### CODE
|
|
@@ -179,6 +182,7 @@ type TOKEN = {
|
|
|
179
182
|
pos: number;
|
|
180
183
|
raw: string;
|
|
181
184
|
code?: string;
|
|
185
|
+
intermediate?: string;
|
|
182
186
|
};
|
|
183
187
|
```
|
|
184
188
|
|
package/dist/escaped.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { APC, APC_OPEN, BACKSLASH, BELL, CODE, CODE_TYPES, CONTROL_CODE, CONTROL_CODE_TEXT, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN, TOKEN_TYPES,
|
|
1
|
+
import { APC, APC_OPEN, BACKSLASH, BELL, CODE, CODE_TYPES, CONTROL_CODE, CONTROL_CODE_TEXT, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN, TOKEN_TYPES, parser } from "./parse-BtpkwNjf.js";
|
|
2
2
|
|
|
3
|
+
//#region src/parse.escaped.d.ts
|
|
4
|
+
declare function parse(input: string): CODE[];
|
|
5
|
+
//#endregion
|
|
3
6
|
//#region src/tokenize.escaped.d.ts
|
|
4
7
|
declare function tokenizer(input: string): Generator<TOKEN>;
|
|
5
8
|
declare function tokenize(input: string): TOKEN[];
|
package/dist/escaped.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { APC, APC_OPEN, BACKSLASH, BELL, CODE_TYPES, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN_TYPES,
|
|
1
|
+
import { APC, APC_OPEN, BACKSLASH, BELL, CODE_TYPES, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN_TYPES, parser } from "./parse-BE4NnMTj.js";
|
|
2
2
|
|
|
3
3
|
//#region src/tokenize.escaped.ts
|
|
4
4
|
const CSI_ESCAPED = "\\u009b";
|
|
@@ -61,7 +61,9 @@ function* tokenizer(input) {
|
|
|
61
61
|
if (i < input.length) {
|
|
62
62
|
const candidates = INTRODUCER_LOOKUP.get(input[i + 1]);
|
|
63
63
|
if (candidates) {
|
|
64
|
+
let matched = false;
|
|
64
65
|
for (const [sequence, len] of candidates) if (i + len <= input.length && input.substring(i, i + len) === sequence) {
|
|
66
|
+
matched = true;
|
|
65
67
|
if (sequence === CSI_ESCAPED) {
|
|
66
68
|
yield emit({
|
|
67
69
|
type: TOKEN_TYPES.INTRODUCER,
|
|
@@ -105,10 +107,11 @@ function* tokenizer(input) {
|
|
|
105
107
|
type: TOKEN_TYPES.INTRODUCER,
|
|
106
108
|
pos: i,
|
|
107
109
|
raw: sequence + nextChar,
|
|
108
|
-
code:
|
|
110
|
+
code: ESC,
|
|
111
|
+
intermediate: nextChar
|
|
109
112
|
});
|
|
110
113
|
i += len + 1;
|
|
111
|
-
setState("SEQUENCE",
|
|
114
|
+
setState("SEQUENCE", ESC);
|
|
112
115
|
} else if (nextChar) {
|
|
113
116
|
yield emit({
|
|
114
117
|
type: TOKEN_TYPES.INTRODUCER,
|
|
@@ -117,12 +120,7 @@ function* tokenizer(input) {
|
|
|
117
120
|
code: ESC
|
|
118
121
|
});
|
|
119
122
|
i += len;
|
|
120
|
-
|
|
121
|
-
type: TOKEN_TYPES.FINAL,
|
|
122
|
-
pos: i,
|
|
123
|
-
raw: nextChar
|
|
124
|
-
});
|
|
125
|
-
i++;
|
|
123
|
+
setState("SEQUENCE", ESC);
|
|
126
124
|
} else {
|
|
127
125
|
yield emit({
|
|
128
126
|
type: TOKEN_TYPES.INTRODUCER,
|
|
@@ -135,9 +133,10 @@ function* tokenizer(input) {
|
|
|
135
133
|
}
|
|
136
134
|
break;
|
|
137
135
|
}
|
|
138
|
-
|
|
136
|
+
if (!matched) i++;
|
|
137
|
+
} else i++;
|
|
139
138
|
}
|
|
140
|
-
} else {
|
|
139
|
+
} else if (state === "SEQUENCE") {
|
|
141
140
|
let terminator = "";
|
|
142
141
|
let terminatorPos = -1;
|
|
143
142
|
const pos = i;
|
|
@@ -151,6 +150,10 @@ function* tokenizer(input) {
|
|
|
151
150
|
terminatorPos = i;
|
|
152
151
|
i++;
|
|
153
152
|
}
|
|
153
|
+
} else if (code === ESC) {
|
|
154
|
+
terminator = char;
|
|
155
|
+
terminatorPos = i;
|
|
156
|
+
i++;
|
|
154
157
|
} else if (code) {
|
|
155
158
|
if (char === BACKSLASH) {
|
|
156
159
|
if (code === OSC) {
|
|
@@ -199,5 +202,11 @@ function tokenize(input) {
|
|
|
199
202
|
return Array.from(tokenizer(input));
|
|
200
203
|
}
|
|
201
204
|
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/parse.escaped.ts
|
|
207
|
+
function parse(input) {
|
|
208
|
+
return Array.from(parser(tokenizer(input)));
|
|
209
|
+
}
|
|
210
|
+
|
|
202
211
|
//#endregion
|
|
203
212
|
export { APC, APC_OPEN, BACKSLASH, BELL, CODE_TYPES, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN_TYPES, parse, parser, tokenize, tokenizer };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { APC, APC_OPEN, BACKSLASH, BELL, CODE, CODE_TYPES, CONTROL_CODE, CONTROL_CODE_TEXT, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN, TOKEN_TYPES, parse, parser } from "./parse-
|
|
1
|
+
import { APC, APC_OPEN, BACKSLASH, BELL, CODE, CODE_TYPES, CONTROL_CODE, CONTROL_CODE_TEXT, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN, TOKEN_TYPES, parse, parser } from "./parse-BtpkwNjf.js";
|
|
2
2
|
|
|
3
3
|
//#region src/tokenize.d.ts
|
|
4
4
|
declare function tokenizer(input: string): Generator<TOKEN>;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { APC, APC_OPEN, BACKSLASH, BELL, CODE_TYPES, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN_TYPES, parse, parser, tokenize, tokenizer } from "./parse-
|
|
1
|
+
import { APC, APC_OPEN, BACKSLASH, BELL, CODE_TYPES, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN_TYPES, parse, parser, tokenize, tokenizer } from "./parse-BE4NnMTj.js";
|
|
2
2
|
|
|
3
3
|
export { APC, APC_OPEN, BACKSLASH, BELL, CODE_TYPES, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN_TYPES, parse, parser, tokenize, tokenizer };
|
|
@@ -177,10 +177,17 @@ function parseDEC(pos, raw, data, final) {
|
|
|
177
177
|
|
|
178
178
|
//#endregion
|
|
179
179
|
//#region src/parsers/esc.ts
|
|
180
|
-
function parseESC(
|
|
180
|
+
function parseESC(token, raw, command, data) {
|
|
181
|
+
if (token.intermediate) return {
|
|
182
|
+
type: CODE_TYPES.ESC,
|
|
183
|
+
pos: token.pos,
|
|
184
|
+
raw,
|
|
185
|
+
command: token.intermediate,
|
|
186
|
+
params: command ? [command] : []
|
|
187
|
+
};
|
|
181
188
|
return {
|
|
182
189
|
type: CODE_TYPES.ESC,
|
|
183
|
-
pos,
|
|
190
|
+
pos: token.pos,
|
|
184
191
|
raw,
|
|
185
192
|
command,
|
|
186
193
|
params: data ? [data] : []
|
|
@@ -303,16 +310,12 @@ function* tokenizer(input) {
|
|
|
303
310
|
yield emit$1({
|
|
304
311
|
type: TOKEN_TYPES.INTRODUCER,
|
|
305
312
|
pos: i,
|
|
306
|
-
raw: char,
|
|
307
|
-
code: ESC
|
|
308
|
-
|
|
309
|
-
i += 1;
|
|
310
|
-
yield emit$1({
|
|
311
|
-
type: TOKEN_TYPES.FINAL,
|
|
312
|
-
pos: i,
|
|
313
|
-
raw: next
|
|
313
|
+
raw: char + next,
|
|
314
|
+
code: ESC,
|
|
315
|
+
intermediate: next
|
|
314
316
|
});
|
|
315
|
-
i
|
|
317
|
+
i += 2;
|
|
318
|
+
setState("SEQUENCE", ESC);
|
|
316
319
|
} else if (next) {
|
|
317
320
|
yield emit$1({
|
|
318
321
|
type: TOKEN_TYPES.INTRODUCER,
|
|
@@ -320,12 +323,8 @@ function* tokenizer(input) {
|
|
|
320
323
|
raw: char,
|
|
321
324
|
code: ESC
|
|
322
325
|
});
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
pos: i + 1,
|
|
326
|
-
raw: next
|
|
327
|
-
});
|
|
328
|
-
i += 2;
|
|
326
|
+
i += 1;
|
|
327
|
+
setState("SEQUENCE", ESC);
|
|
329
328
|
} else {
|
|
330
329
|
yield emit$1({
|
|
331
330
|
type: TOKEN_TYPES.INTRODUCER,
|
|
@@ -337,7 +336,7 @@ function* tokenizer(input) {
|
|
|
337
336
|
}
|
|
338
337
|
}
|
|
339
338
|
}
|
|
340
|
-
} else {
|
|
339
|
+
} else if (state === "SEQUENCE") {
|
|
341
340
|
const pos = i;
|
|
342
341
|
const code = currentCode;
|
|
343
342
|
let data = "";
|
|
@@ -361,7 +360,17 @@ function* tokenizer(input) {
|
|
|
361
360
|
data += char;
|
|
362
361
|
i++;
|
|
363
362
|
}
|
|
364
|
-
else if (code
|
|
363
|
+
else if (code === ESC) {
|
|
364
|
+
if (i < input.length) {
|
|
365
|
+
const char = input[i];
|
|
366
|
+
yield emit$1({
|
|
367
|
+
type: TOKEN_TYPES.FINAL,
|
|
368
|
+
pos: i,
|
|
369
|
+
raw: char
|
|
370
|
+
});
|
|
371
|
+
i++;
|
|
372
|
+
}
|
|
373
|
+
} else if (code) while (i < input.length) {
|
|
365
374
|
const char = input[i];
|
|
366
375
|
let terminator;
|
|
367
376
|
if (char === ST) terminator = ST;
|
|
@@ -470,10 +479,10 @@ function* parser(tokens) {
|
|
|
470
479
|
});
|
|
471
480
|
break;
|
|
472
481
|
case ESC:
|
|
473
|
-
yield emit(parseESC(
|
|
482
|
+
yield emit(parseESC(token, raw, finalToken.raw, data));
|
|
474
483
|
break;
|
|
475
484
|
}
|
|
476
|
-
else if (token.code === ESC) yield emit(parseESC(
|
|
485
|
+
else if (token.code === ESC) yield emit(parseESC(token, raw, "", ""));
|
|
477
486
|
} else current = tokens.next();
|
|
478
487
|
}
|
|
479
488
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ansi-tools/parser",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Tokenize and parse strings containing ANSI escape sequences and control codes",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,11 +11,21 @@
|
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"./escaped": {
|
|
14
|
-
"types": "./dist/
|
|
14
|
+
"types": "./dist/escaped.d.ts",
|
|
15
15
|
"import": "./dist/escaped.js",
|
|
16
16
|
"default": "./dist/escaped.js"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"prebuild": "pnpm type-check && pnpm test",
|
|
24
|
+
"build": "tsdown --dts src/index.ts src/escaped.ts",
|
|
25
|
+
"test": "node --test",
|
|
26
|
+
"type-check": "tsc",
|
|
27
|
+
"prepack": "pnpm build"
|
|
28
|
+
},
|
|
19
29
|
"keywords": [
|
|
20
30
|
"ansi",
|
|
21
31
|
"escape codes",
|
|
@@ -26,15 +36,16 @@
|
|
|
26
36
|
"publishConfig": {
|
|
27
37
|
"access": "public"
|
|
28
38
|
},
|
|
39
|
+
"homepage": "https://github.com/webpro/ANSI.tools/tree/main/packages/parser",
|
|
40
|
+
"bugs": "https://github.com/webpro/ANSI.tools/issues",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "github:webpro/ANSI.tools",
|
|
44
|
+
"directory": "packages/parser"
|
|
45
|
+
},
|
|
29
46
|
"devDependencies": {
|
|
30
47
|
"@types/node": "^24.0.13",
|
|
31
48
|
"tsdown": "^0.12.9",
|
|
32
49
|
"typescript": "^5.8.3"
|
|
33
|
-
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"prebuild": "pnpm type-check && pnpm test",
|
|
36
|
-
"build": "tsdown --dts src/index.ts src/escaped.ts",
|
|
37
|
-
"test": "node --test",
|
|
38
|
-
"type-check": "tsc"
|
|
39
50
|
}
|
|
40
|
-
}
|
|
51
|
+
}
|
package/src/constants.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export const BELL = String.fromCharCode(7);
|
|
2
|
-
export const ESC = String.fromCharCode(27);
|
|
3
|
-
export const BACKSLASH = String.fromCharCode(92);
|
|
4
|
-
export const DCS = String.fromCharCode(144);
|
|
5
|
-
export const SOS = String.fromCharCode(152);
|
|
6
|
-
export const CSI = String.fromCharCode(155);
|
|
7
|
-
export const ST = String.fromCharCode(156);
|
|
8
|
-
export const OSC = String.fromCharCode(157);
|
|
9
|
-
export const PM = String.fromCharCode(158);
|
|
10
|
-
export const APC = String.fromCharCode(159);
|
|
11
|
-
|
|
12
|
-
export const CSI_OPEN = "[";
|
|
13
|
-
export const OSC_OPEN = "]";
|
|
14
|
-
export const DEC_OPEN = "?";
|
|
15
|
-
export const PRIVATE_OPENERS = new Set(["<", "=", ">"]);
|
|
16
|
-
|
|
17
|
-
export const DCS_OPEN = "P";
|
|
18
|
-
export const APC_OPEN = "_";
|
|
19
|
-
export const SOS_OPEN = "^";
|
|
20
|
-
export const PM_OPEN = "X";
|
|
21
|
-
export const STRING_OPENERS = new Set([DCS_OPEN, APC_OPEN, SOS_OPEN, PM_OPEN]);
|
|
22
|
-
|
|
23
|
-
export const TOKEN_TYPES = {
|
|
24
|
-
TEXT: "TEXT",
|
|
25
|
-
INTRODUCER: "INTRODUCER",
|
|
26
|
-
DATA: "DATA",
|
|
27
|
-
FINAL: "FINAL",
|
|
28
|
-
} as const;
|
|
29
|
-
|
|
30
|
-
export const CODE_TYPES = {
|
|
31
|
-
CSI: "CSI",
|
|
32
|
-
DCS: "DCS",
|
|
33
|
-
DEC: "DEC",
|
|
34
|
-
ESC: "ESC",
|
|
35
|
-
OSC: "OSC",
|
|
36
|
-
PRIVATE: "PRIVATE",
|
|
37
|
-
SGR: "SGR",
|
|
38
|
-
STRING: "STRING",
|
|
39
|
-
TEXT: "TEXT",
|
|
40
|
-
} as const;
|
package/src/escaped.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { test } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { tokenizer } from "./tokenize.escaped.ts";
|
|
4
|
-
import { parser } from "./parse.ts";
|
|
5
|
-
import { CODE_TYPES } from "./constants.ts";
|
|
6
|
-
|
|
7
|
-
test("parse simple text", () => {
|
|
8
|
-
const input = "hello world";
|
|
9
|
-
const tokens = tokenizer(input);
|
|
10
|
-
const codes = [...parser(tokens)];
|
|
11
|
-
assert.deepEqual(codes, [{ type: CODE_TYPES.TEXT, pos: 0, raw: "hello world" }]);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("parse mixed text and csi", () => {
|
|
15
|
-
const input = String.raw`hello \e[31mworld\e[0m`;
|
|
16
|
-
const tokens = tokenizer(input);
|
|
17
|
-
const codes = [...parser(tokens)];
|
|
18
|
-
assert.deepEqual(codes, [
|
|
19
|
-
{ type: CODE_TYPES.TEXT, pos: 0, raw: "hello " },
|
|
20
|
-
{ type: CODE_TYPES.CSI, pos: 6, raw: "\\e[31m", params: ["31"], command: "m" },
|
|
21
|
-
{ type: CODE_TYPES.TEXT, pos: 12, raw: "world" },
|
|
22
|
-
{ type: CODE_TYPES.CSI, pos: 17, raw: "\\e[0m", params: ["0"], command: "m" },
|
|
23
|
-
]);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("subsequent escape sequences", () => {
|
|
27
|
-
const input = String.raw`\e[31m\e[32m\e[33m`;
|
|
28
|
-
const tokens = tokenizer(input);
|
|
29
|
-
const codes = [...parser(tokens)];
|
|
30
|
-
assert.deepEqual(codes, [
|
|
31
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\\e[31m", params: ["31"], command: "m" },
|
|
32
|
-
{ type: CODE_TYPES.CSI, pos: 6, raw: "\\e[32m", params: ["32"], command: "m" },
|
|
33
|
-
{ type: CODE_TYPES.CSI, pos: 12, raw: "\\e[33m", params: ["33"], command: "m" },
|
|
34
|
-
]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("parse multiple different sequence types", () => {
|
|
38
|
-
const input = String.raw`\e[1;32m\e]0;title\a\e=\ePdata\e\\`;
|
|
39
|
-
const tokens = tokenizer(input);
|
|
40
|
-
const codes = [...parser(tokens)];
|
|
41
|
-
assert.deepEqual(codes, [
|
|
42
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\\e[1;32m", params: ["1", "32"], command: "m" },
|
|
43
|
-
{ type: CODE_TYPES.OSC, pos: 8, raw: "\\e]0;title\\a", params: ["title"], command: "0" },
|
|
44
|
-
{ type: CODE_TYPES.ESC, pos: 20, raw: "\\e=", command: "=", params: [] },
|
|
45
|
-
{ type: CODE_TYPES.DCS, pos: 23, raw: "\\ePdata\\e\\\\", params: ["data"], command: "" },
|
|
46
|
-
]);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("parse mixed standard and private sequences", () => {
|
|
50
|
-
const input = String.raw`\e[31m\e[<5h\e[?25l\e[>c`;
|
|
51
|
-
const tokens = tokenizer(input);
|
|
52
|
-
const codes = [...parser(tokens)];
|
|
53
|
-
assert.deepEqual(codes, [
|
|
54
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\\e[31m", params: ["31"], command: "m" },
|
|
55
|
-
{ type: CODE_TYPES.PRIVATE, pos: 6, raw: "\\e[<5h", params: ["5"], command: "<h" },
|
|
56
|
-
{ type: CODE_TYPES.DEC, pos: 12, raw: "\\e[?25l", params: ["25"], command: "l" },
|
|
57
|
-
{ type: CODE_TYPES.PRIVATE, pos: 19, raw: "\\e[>c", params: [], command: ">c" },
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("parse complex missing parameter scenarios", () => {
|
|
62
|
-
const input = String.raw`\e[;5;m\e[?;h\eP$q;;\e\\`;
|
|
63
|
-
const tokens = tokenizer(input);
|
|
64
|
-
const codes = [...parser(tokens)];
|
|
65
|
-
assert.deepEqual(codes, [
|
|
66
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\\e[;5;m", params: ["-1", "5", "-1"], command: "m" },
|
|
67
|
-
{ type: CODE_TYPES.DEC, pos: 7, raw: "\\e[?;h", params: ["-1", "-1"], command: "h" },
|
|
68
|
-
{ type: CODE_TYPES.DCS, pos: 13, raw: "\\eP$q;;\\e\\\\", params: ["-1", "-1", "-1"], command: "$q" },
|
|
69
|
-
]);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("parse iTerm2 image sequence", () => {
|
|
73
|
-
const input = String.raw`\e]1337;File=inline=1;width=1;height=1:R0lG=\a`;
|
|
74
|
-
const tokens = tokenizer(input);
|
|
75
|
-
const codes = [...parser(tokens)];
|
|
76
|
-
assert.deepEqual(codes, [
|
|
77
|
-
{ type: CODE_TYPES.OSC, pos: 0, raw: input, command: "1337", params: ["File=inline=1;width=1;height=1:R0lG="] },
|
|
78
|
-
]);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("parse DECUDK sequence", () => {
|
|
82
|
-
const input = String.raw`\eP0|23/68656c6c6f\e\\`;
|
|
83
|
-
const tokens = tokenizer(input);
|
|
84
|
-
const codes = [...parser(tokens)];
|
|
85
|
-
assert.deepEqual(codes, [{ type: CODE_TYPES.DCS, pos: 0, raw: input, command: "", params: ["0|23/68656c6c6f"] }]);
|
|
86
|
-
});
|
package/src/parse.test.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { test } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { tokenizer } from "./tokenize.ts";
|
|
4
|
-
import { parser } from "./parse.ts";
|
|
5
|
-
import { CODE_TYPES } from "./constants.ts";
|
|
6
|
-
|
|
7
|
-
test("parse simple text", () => {
|
|
8
|
-
const input = "hello world";
|
|
9
|
-
const tokens = tokenizer(input);
|
|
10
|
-
const codes = [...parser(tokens)];
|
|
11
|
-
assert.deepEqual(codes, [{ type: CODE_TYPES.TEXT, pos: 0, raw: "hello world" }]);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("parse mixed text and csi", () => {
|
|
15
|
-
const input = `hello \x1b[31mworld\x1b[0m`;
|
|
16
|
-
const tokens = tokenizer(input);
|
|
17
|
-
const codes = [...parser(tokens)];
|
|
18
|
-
assert.deepEqual(codes, [
|
|
19
|
-
{ type: CODE_TYPES.TEXT, pos: 0, raw: "hello " },
|
|
20
|
-
{ type: CODE_TYPES.CSI, pos: 6, raw: "\x1b[31m", params: ["31"], command: "m" },
|
|
21
|
-
{ type: CODE_TYPES.TEXT, pos: 11, raw: "world" },
|
|
22
|
-
{ type: CODE_TYPES.CSI, pos: 16, raw: "\x1b[0m", params: ["0"], command: "m" },
|
|
23
|
-
]);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("subsequent escape sequences", () => {
|
|
27
|
-
const input = `\x1b[31m\x1b[32m\x1b[33m`;
|
|
28
|
-
const tokens = tokenizer(input);
|
|
29
|
-
const codes = [...parser(tokens)];
|
|
30
|
-
assert.deepEqual(codes, [
|
|
31
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\x1b[31m", params: ["31"], command: "m" },
|
|
32
|
-
{ type: CODE_TYPES.CSI, pos: 5, raw: "\x1b[32m", params: ["32"], command: "m" },
|
|
33
|
-
{ type: CODE_TYPES.CSI, pos: 10, raw: "\x1b[33m", params: ["33"], command: "m" },
|
|
34
|
-
]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("parse multiple different sequence types", () => {
|
|
38
|
-
const input = `\x1b[1;32m\x1b]0;title\x07\x1b=\x1bPdata\x1b\\`;
|
|
39
|
-
const tokens = tokenizer(input);
|
|
40
|
-
const codes = [...parser(tokens)];
|
|
41
|
-
assert.deepEqual(codes, [
|
|
42
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\x1b[1;32m", params: ["1", "32"], command: "m" },
|
|
43
|
-
{ type: CODE_TYPES.OSC, pos: 7, raw: "\x1b]0;title\x07", params: ["title"], command: "0" },
|
|
44
|
-
{ type: CODE_TYPES.ESC, pos: 17, raw: "\x1b=", command: "=", params: [] },
|
|
45
|
-
{ type: CODE_TYPES.DCS, pos: 19, raw: "\x1bPdata\x1b\\", params: ["data"], command: "" },
|
|
46
|
-
]);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("parse mixed standard and private sequences", () => {
|
|
50
|
-
const input = `\x1b[31m\x1b[<5h\x1b[?25l\x1b[>c`;
|
|
51
|
-
const tokens = tokenizer(input);
|
|
52
|
-
const codes = [...parser(tokens)];
|
|
53
|
-
assert.deepEqual(codes, [
|
|
54
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\x1b[31m", params: ["31"], command: "m" },
|
|
55
|
-
{ type: CODE_TYPES.PRIVATE, pos: 5, raw: "\x1b[<5h", params: ["5"], command: "<h" },
|
|
56
|
-
{ type: CODE_TYPES.DEC, pos: 10, raw: "\x1b[?25l", params: ["25"], command: "l" },
|
|
57
|
-
{ type: CODE_TYPES.PRIVATE, pos: 16, raw: "\x1b[>c", params: [], command: ">c" },
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("parse complex missing parameter scenarios", () => {
|
|
62
|
-
const input = `\x1b[;5;m\x1b[?;h\x1bP$q;;\x1b\\`;
|
|
63
|
-
const tokens = tokenizer(input);
|
|
64
|
-
const codes = [...parser(tokens)];
|
|
65
|
-
assert.deepEqual(codes, [
|
|
66
|
-
{ type: CODE_TYPES.CSI, pos: 0, raw: "\x1b[;5;m", params: ["-1", "5", "-1"], command: "m" },
|
|
67
|
-
{ type: CODE_TYPES.DEC, pos: 6, raw: "\x1b[?;h", params: ["-1", "-1"], command: "h" },
|
|
68
|
-
{ type: CODE_TYPES.DCS, pos: 11, raw: "\x1bP$q;;\x1b\\", params: ["-1", "-1", "-1"], command: "$q" },
|
|
69
|
-
]);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("parse iTerm2 image sequence", () => {
|
|
73
|
-
const input = `\x1b]1337;File=inline=1;width=1;height=1:R0lG=\x07`;
|
|
74
|
-
const tokens = tokenizer(input);
|
|
75
|
-
const codes = [...parser(tokens)];
|
|
76
|
-
assert.deepEqual(codes, [
|
|
77
|
-
{ type: CODE_TYPES.OSC, pos: 0, raw: input, command: "1337", params: ["File=inline=1;width=1;height=1:R0lG="] },
|
|
78
|
-
]);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("parse DECUDK sequence", () => {
|
|
82
|
-
const input = `\x1bP0|23/68656c6c6f\x1b\\`;
|
|
83
|
-
const tokens = tokenizer(input);
|
|
84
|
-
const codes = [...parser(tokens)];
|
|
85
|
-
assert.deepEqual(codes, [{ type: CODE_TYPES.DCS, pos: 0, raw: input, command: "", params: ["0|23/68656c6c6f"] }]);
|
|
86
|
-
});
|
package/src/parse.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
APC_OPEN,
|
|
3
|
-
APC,
|
|
4
|
-
CODE_TYPES,
|
|
5
|
-
CSI,
|
|
6
|
-
DCS_OPEN,
|
|
7
|
-
DCS,
|
|
8
|
-
DEC_OPEN,
|
|
9
|
-
ESC,
|
|
10
|
-
OSC,
|
|
11
|
-
PM_OPEN,
|
|
12
|
-
PM,
|
|
13
|
-
PRIVATE_OPENERS,
|
|
14
|
-
SOS_OPEN,
|
|
15
|
-
SOS,
|
|
16
|
-
TOKEN_TYPES,
|
|
17
|
-
} from "./constants.ts";
|
|
18
|
-
import { parseCSI, parsePrivateCSI } from "./parsers/csi.ts";
|
|
19
|
-
import { parseDCS } from "./parsers/dcs.ts";
|
|
20
|
-
import { parseDEC } from "./parsers/dec.ts";
|
|
21
|
-
import { parseESC } from "./parsers/esc.ts";
|
|
22
|
-
import { parseOSC } from "./parsers/osc.ts";
|
|
23
|
-
import { tokenizer } from "./tokenize.ts";
|
|
24
|
-
import type { CODE, TOKEN } from "./types.ts";
|
|
25
|
-
|
|
26
|
-
const debug = false;
|
|
27
|
-
|
|
28
|
-
function emit(token: CODE) {
|
|
29
|
-
if (debug) console.log("code", token);
|
|
30
|
-
return token;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function* parser(tokens: Generator<TOKEN>): Generator<CODE> {
|
|
34
|
-
let current = tokens.next();
|
|
35
|
-
|
|
36
|
-
while (!current.done) {
|
|
37
|
-
const token = current.value;
|
|
38
|
-
|
|
39
|
-
if (token.type === TOKEN_TYPES.TEXT) {
|
|
40
|
-
yield emit({ type: CODE_TYPES.TEXT, pos: token.pos, raw: token.raw });
|
|
41
|
-
current = tokens.next();
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (token.type === TOKEN_TYPES.INTRODUCER) {
|
|
46
|
-
const pos = token.pos;
|
|
47
|
-
let raw = token.raw;
|
|
48
|
-
let data = "";
|
|
49
|
-
let finalToken: TOKEN | undefined;
|
|
50
|
-
|
|
51
|
-
current = tokens.next();
|
|
52
|
-
|
|
53
|
-
while (!current.done && !finalToken) {
|
|
54
|
-
const nextToken = current.value;
|
|
55
|
-
|
|
56
|
-
if (nextToken.type === TOKEN_TYPES.DATA) {
|
|
57
|
-
data += nextToken.raw;
|
|
58
|
-
raw += nextToken.raw;
|
|
59
|
-
} else if (nextToken.type === TOKEN_TYPES.FINAL) {
|
|
60
|
-
finalToken = nextToken;
|
|
61
|
-
raw += nextToken.raw;
|
|
62
|
-
}
|
|
63
|
-
current = tokens.next();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (finalToken) {
|
|
67
|
-
switch (token.code) {
|
|
68
|
-
case CSI:
|
|
69
|
-
if (data.startsWith(DEC_OPEN)) {
|
|
70
|
-
yield emit(parseDEC(pos, raw, data, finalToken.raw));
|
|
71
|
-
} else if (PRIVATE_OPENERS.has(data[0])) {
|
|
72
|
-
yield emit(parsePrivateCSI(pos, raw, data, finalToken.raw));
|
|
73
|
-
} else {
|
|
74
|
-
yield emit(parseCSI(pos, raw, data, finalToken.raw));
|
|
75
|
-
}
|
|
76
|
-
break;
|
|
77
|
-
case OSC:
|
|
78
|
-
yield emit(parseOSC(pos, raw, data));
|
|
79
|
-
break;
|
|
80
|
-
case DCS:
|
|
81
|
-
case DCS_OPEN:
|
|
82
|
-
yield emit(parseDCS(pos, raw, data));
|
|
83
|
-
break;
|
|
84
|
-
case APC:
|
|
85
|
-
case APC_OPEN:
|
|
86
|
-
yield emit({ type: CODE_TYPES.STRING, pos, raw, command: "APC", params: data ? [data] : [] });
|
|
87
|
-
break;
|
|
88
|
-
case PM:
|
|
89
|
-
case PM_OPEN:
|
|
90
|
-
yield emit({ type: CODE_TYPES.STRING, pos, raw, command: "PM", params: data ? [data] : [] });
|
|
91
|
-
break;
|
|
92
|
-
case SOS:
|
|
93
|
-
case SOS_OPEN:
|
|
94
|
-
yield emit({ type: CODE_TYPES.STRING, pos, raw, command: "SOS", params: data ? [data] : [] });
|
|
95
|
-
break;
|
|
96
|
-
case ESC:
|
|
97
|
-
yield emit(parseESC(pos, raw, finalToken.raw, data));
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
} else if (token.code === ESC) {
|
|
101
|
-
yield emit(parseESC(pos, raw, "", ""));
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
current = tokens.next();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function parse(input: string): CODE[] {
|
|
110
|
-
return Array.from(parser(tokenizer(input)));
|
|
111
|
-
}
|