@ansi-tools/parser 0.0.0 → 0.0.1
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 +1 -0
- package/dist/escaped.d.ts +4 -1
- package/dist/escaped.js +16 -10
- 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 +5 -2
- 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
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";
|
|
@@ -105,10 +105,11 @@ function* tokenizer(input) {
|
|
|
105
105
|
type: TOKEN_TYPES.INTRODUCER,
|
|
106
106
|
pos: i,
|
|
107
107
|
raw: sequence + nextChar,
|
|
108
|
-
code:
|
|
108
|
+
code: ESC,
|
|
109
|
+
intermediate: nextChar
|
|
109
110
|
});
|
|
110
111
|
i += len + 1;
|
|
111
|
-
setState("SEQUENCE",
|
|
112
|
+
setState("SEQUENCE", ESC);
|
|
112
113
|
} else if (nextChar) {
|
|
113
114
|
yield emit({
|
|
114
115
|
type: TOKEN_TYPES.INTRODUCER,
|
|
@@ -117,12 +118,7 @@ function* tokenizer(input) {
|
|
|
117
118
|
code: ESC
|
|
118
119
|
});
|
|
119
120
|
i += len;
|
|
120
|
-
|
|
121
|
-
type: TOKEN_TYPES.FINAL,
|
|
122
|
-
pos: i,
|
|
123
|
-
raw: nextChar
|
|
124
|
-
});
|
|
125
|
-
i++;
|
|
121
|
+
setState("SEQUENCE", ESC);
|
|
126
122
|
} else {
|
|
127
123
|
yield emit({
|
|
128
124
|
type: TOKEN_TYPES.INTRODUCER,
|
|
@@ -137,7 +133,7 @@ function* tokenizer(input) {
|
|
|
137
133
|
}
|
|
138
134
|
}
|
|
139
135
|
}
|
|
140
|
-
} else {
|
|
136
|
+
} else if (state === "SEQUENCE") {
|
|
141
137
|
let terminator = "";
|
|
142
138
|
let terminatorPos = -1;
|
|
143
139
|
const pos = i;
|
|
@@ -151,6 +147,10 @@ function* tokenizer(input) {
|
|
|
151
147
|
terminatorPos = i;
|
|
152
148
|
i++;
|
|
153
149
|
}
|
|
150
|
+
} else if (code === ESC) {
|
|
151
|
+
terminator = char;
|
|
152
|
+
terminatorPos = i;
|
|
153
|
+
i++;
|
|
154
154
|
} else if (code) {
|
|
155
155
|
if (char === BACKSLASH) {
|
|
156
156
|
if (code === OSC) {
|
|
@@ -199,5 +199,11 @@ function tokenize(input) {
|
|
|
199
199
|
return Array.from(tokenizer(input));
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/parse.escaped.ts
|
|
204
|
+
function parse(input) {
|
|
205
|
+
return Array.from(parser(tokenizer(input)));
|
|
206
|
+
}
|
|
207
|
+
|
|
202
208
|
//#endregion
|
|
203
209
|
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.1",
|
|
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,14 @@
|
|
|
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
|
+
],
|
|
19
22
|
"keywords": [
|
|
20
23
|
"ansi",
|
|
21
24
|
"escape codes",
|
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
|
-
}
|
package/src/parsers/csi.test.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { test } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { parseCSI, parsePrivateCSI } from "./csi.ts";
|
|
4
|
-
import { CODE_TYPES } from "../constants.ts";
|
|
5
|
-
|
|
6
|
-
test("parseCSI simple command", () => {
|
|
7
|
-
const result = parseCSI(0, "\\e[31m", "31", "m");
|
|
8
|
-
assert.deepEqual(result, { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[31m", params: ["31"], command: "m" });
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test("parseCSI with multiple params", () => {
|
|
12
|
-
const result = parseCSI(0, "\\e[1;31m", "1;31", "m");
|
|
13
|
-
assert.deepEqual(result, { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[1;31m", params: ["1", "31"], command: "m" });
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test("parseCSI with missing parameters", () => {
|
|
17
|
-
const result = parseCSI(0, "\\e[;31m", ";31", "m");
|
|
18
|
-
assert.deepEqual(result, { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[;31m", params: ["-1", "31"], command: "m" });
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("parseCSI with trailing semicolon", () => {
|
|
22
|
-
const result = parseCSI(0, "\\e[31;m", "31;", "m");
|
|
23
|
-
assert.deepEqual(result, { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[31;m", params: ["31", "-1"], command: "m" });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("parseCSI with leading semicolon", () => {
|
|
27
|
-
const result = parseCSI(0, "\\e[;m", ";", "m");
|
|
28
|
-
assert.deepEqual(result, { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[;m", params: ["-1", "-1"], command: "m" });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("parseCSI no data", () => {
|
|
32
|
-
const result = parseCSI(0, "\\e[m", "", "m");
|
|
33
|
-
assert.deepEqual(result, { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[m", params: [], command: "m" });
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("parsePrivateCSI with < introducer", () => {
|
|
37
|
-
const result = parsePrivateCSI(0, "\\e[<31m", "<31", "m");
|
|
38
|
-
assert.deepEqual(result, { type: CODE_TYPES.PRIVATE, pos: 0, raw: "\\e[<31m", params: ["31"], command: "<m" });
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("parsePrivateCSI with > introducer", () => {
|
|
42
|
-
const result = parsePrivateCSI(0, "\\e[>c", ">", "c");
|
|
43
|
-
assert.deepEqual(result, { type: CODE_TYPES.PRIVATE, pos: 0, raw: "\\e[>c", params: [], command: ">c" });
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("parsePrivateCSI with = introducer", () => {
|
|
47
|
-
const result = parsePrivateCSI(0, "\\e[=1;2c", "=1;2", "c");
|
|
48
|
-
assert.deepEqual(result, {
|
|
49
|
-
type: CODE_TYPES.PRIVATE,
|
|
50
|
-
pos: 0,
|
|
51
|
-
raw: "\\e[=1;2c",
|
|
52
|
-
params: ["1", "2"],
|
|
53
|
-
command: "=c",
|
|
54
|
-
});
|
|
55
|
-
});
|
package/src/parsers/csi.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { CODE_TYPES } from "../constants.ts";
|
|
2
|
-
import type { CONTROL_CODE } from "../types.ts";
|
|
3
|
-
|
|
4
|
-
export function parseCSI(pos: number, raw: string, data: string, final: string): CONTROL_CODE {
|
|
5
|
-
const params = [];
|
|
6
|
-
let intermediates = "";
|
|
7
|
-
if (data) {
|
|
8
|
-
let i = 0;
|
|
9
|
-
let paramSection = "";
|
|
10
|
-
while (i < data.length && data.charCodeAt(i) >= 0x30 && data.charCodeAt(i) <= 0x3f) {
|
|
11
|
-
paramSection += data[i];
|
|
12
|
-
i++;
|
|
13
|
-
}
|
|
14
|
-
intermediates = data.slice(i);
|
|
15
|
-
if (paramSection) {
|
|
16
|
-
let current = "";
|
|
17
|
-
for (let j = 0; j < paramSection.length; j++) {
|
|
18
|
-
if (paramSection[j] === ";") {
|
|
19
|
-
params.push(current || "-1");
|
|
20
|
-
current = "";
|
|
21
|
-
} else {
|
|
22
|
-
current += paramSection[j];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
params.push(current || "-1");
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
const command = intermediates + final;
|
|
29
|
-
return { type: CODE_TYPES.CSI, pos, raw, command, params };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function parsePrivateCSI(pos: number, raw: string, data: string, final: string): CONTROL_CODE {
|
|
33
|
-
const privateIndicator = data[0];
|
|
34
|
-
const withoutIndicator = data.slice(1);
|
|
35
|
-
const match = withoutIndicator.match(/^([\d;]*)(.*)/);
|
|
36
|
-
const paramsRaw = match?.[1] ?? "";
|
|
37
|
-
const intermediates = match?.[2] ?? "";
|
|
38
|
-
const command = `${privateIndicator}${intermediates}${final}`;
|
|
39
|
-
const params = [];
|
|
40
|
-
if (paramsRaw) {
|
|
41
|
-
let current = "";
|
|
42
|
-
for (let i = 0; i < paramsRaw.length; i++) {
|
|
43
|
-
if (paramsRaw[i] === ";") {
|
|
44
|
-
params.push(current || "-1");
|
|
45
|
-
current = "";
|
|
46
|
-
} else {
|
|
47
|
-
current += paramsRaw[i];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
params.push(current || "-1");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return { type: CODE_TYPES.PRIVATE, pos, raw, command, params };
|
|
54
|
-
}
|