@peteanderson/ansi 0.2.4 → 0.3.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 +119 -28
- package/dist/ansi.d.ts +46 -0
- package/dist/ansi.js +49 -0
- package/dist/caret.d.ts +28 -0
- package/dist/caret.js +70 -0
- package/dist/erase.d.ts +13 -0
- package/dist/erase.js +30 -0
- package/dist/features/build.d.ts +11 -0
- package/dist/features/build.js +50 -0
- package/dist/features/detect.d.ts +4 -0
- package/dist/features/detect.js +33 -0
- package/dist/features/features.d.ts +5 -0
- package/dist/features/features.js +39 -0
- package/dist/features/index.d.ts +2 -0
- package/dist/features/index.js +2 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +2 -0
- package/dist/patterns.d.ts +3 -0
- package/dist/patterns.js +43 -0
- package/dist/scroll.d.ts +6 -0
- package/dist/scroll.js +14 -0
- package/dist/sgr/color/color.d.ts +9 -0
- package/dist/sgr/color/color.js +76 -0
- package/dist/sgr/color/index.d.ts +2 -0
- package/dist/sgr/color/index.js +2 -0
- package/dist/sgr/color/utils.d.ts +27 -0
- package/dist/sgr/color/utils.js +67 -0
- package/dist/sgr/index.d.ts +9 -0
- package/dist/sgr/index.js +6 -0
- package/dist/sgr/reset.d.ts +1 -0
- package/dist/sgr/reset.js +3 -0
- package/dist/sgr/sgr.d.ts +14 -0
- package/dist/sgr/sgr.js +57 -0
- package/dist/sgr/style.d.ts +13 -0
- package/dist/sgr/style.js +19 -0
- package/dist/sgr/utils.d.ts +2 -0
- package/dist/sgr/utils.js +16 -0
- package/dist/simplify/atom.d.ts +3 -0
- package/dist/simplify/atom.js +28 -0
- package/dist/simplify/index.d.ts +2 -0
- package/dist/simplify/index.js +2 -0
- package/dist/simplify/lookup.d.ts +5 -0
- package/dist/simplify/lookup.js +77 -0
- package/dist/simplify/simplify.d.ts +1 -0
- package/dist/simplify/simplify.js +25 -0
- package/dist/simplify/state.d.ts +10 -0
- package/dist/simplify/state.js +57 -0
- package/dist/simplify/utils.d.ts +2 -0
- package/dist/simplify/utils.js +31 -0
- package/dist/slice.d.ts +1 -0
- package/dist/slice.js +83 -0
- package/dist/strip.d.ts +15 -0
- package/dist/strip.js +18 -0
- package/package.json +13 -20
- package/src/ansi.js +58 -0
- package/src/caret.js +86 -0
- package/src/erase.js +34 -0
- package/src/features/build.js +60 -0
- package/src/features/detect.js +39 -0
- package/src/features/features.js +53 -0
- package/src/features/index.js +1 -0
- package/src/features/types.d.ts +9 -0
- package/src/index.js +1 -0
- package/src/patterns.js +44 -0
- package/src/scroll.js +18 -0
- package/src/sgr/color/color.js +104 -0
- package/src/sgr/color/index.js +1 -0
- package/src/sgr/color/types.d.ts +28 -0
- package/src/sgr/color/utils.js +81 -0
- package/src/sgr/index.js +5 -0
- package/src/sgr/reset.js +3 -0
- package/src/sgr/sgr.js +77 -0
- package/src/sgr/style.js +23 -0
- package/src/sgr/utils.js +20 -0
- package/src/simplify/atom.js +34 -0
- package/src/simplify/index.js +1 -0
- package/src/simplify/lookup.js +86 -0
- package/src/simplify/simplify.js +33 -0
- package/src/simplify/state.js +73 -0
- package/src/simplify/types.d.ts +28 -0
- package/src/simplify/utils.js +41 -0
- package/src/slice.js +99 -0
- package/src/strip.js +23 -0
- package/ansi.js +0 -28
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const {makeSGR} = require("../sgr");
|
|
2
|
+
const {makeStyle} = require("../style");
|
|
3
|
+
const {clip, rgbToX256, rgbToX16, rgbToX8, x256ToRgb} = require("./utils");
|
|
4
|
+
|
|
5
|
+
/** @typedef {import("../sgr").Code} Code */
|
|
6
|
+
/** @typedef {import("../../features/types").ColorDepth} ColorDepth */
|
|
7
|
+
/** @typedef {import("../sgr").SGR} SGR */
|
|
8
|
+
/** @typedef {import("./types").Channel} Channel */
|
|
9
|
+
|
|
10
|
+
const fgOpen = 30, fgClose = 39;
|
|
11
|
+
const bgOpen = 40, bgClose = 49;
|
|
12
|
+
|
|
13
|
+
/** @type {(colorDepth: ColorDepth, open: number, close: number) => Channel} */
|
|
14
|
+
function makeChannel(colorDepth, open, close) {
|
|
15
|
+
const sgrBase = makeSGR(colorDepth > 1);
|
|
16
|
+
const style = makeStyle(colorDepth > 1);
|
|
17
|
+
|
|
18
|
+
/** @type {(code: Code) => SGR} */
|
|
19
|
+
const sgr = code => sgrBase(code, close);
|
|
20
|
+
|
|
21
|
+
/** @type {(index: number) => SGR} */
|
|
22
|
+
const bright =
|
|
23
|
+
colorDepth >= 4 ? index => sgr(open + 60 + index) :
|
|
24
|
+
colorDepth === 3 ?
|
|
25
|
+
open === fgOpen ?
|
|
26
|
+
index => style.bold.combine(sgr(open + index)) :
|
|
27
|
+
index => sgr(open + index) :
|
|
28
|
+
() => sgr(0);
|
|
29
|
+
|
|
30
|
+
/** @type {(code: number) => SGR} */
|
|
31
|
+
const x16ToX8 =
|
|
32
|
+
open === fgOpen ?
|
|
33
|
+
code => code >= 60 ? style.bold.combine(sgr(open + code - 60)) : sgr(open + code) :
|
|
34
|
+
code => code >= 60 ? sgr(open + code - 60) : sgr(open + code);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
black : sgr(open + 0),
|
|
38
|
+
red : sgr(open + 1),
|
|
39
|
+
green : sgr(open + 2),
|
|
40
|
+
yellow : sgr(open + 3),
|
|
41
|
+
blue : sgr(open + 4),
|
|
42
|
+
magenta : sgr(open + 5),
|
|
43
|
+
cyan : sgr(open + 6),
|
|
44
|
+
white : sgr(open + 7),
|
|
45
|
+
|
|
46
|
+
brightBlack : bright(0),
|
|
47
|
+
brightRed : bright(1),
|
|
48
|
+
brightGreen : bright(2),
|
|
49
|
+
brightYellow : bright(3),
|
|
50
|
+
brightBlue : bright(4),
|
|
51
|
+
brightMagenta : bright(5),
|
|
52
|
+
brightCyan : bright(6),
|
|
53
|
+
brightWhite : bright(7),
|
|
54
|
+
|
|
55
|
+
rgb:
|
|
56
|
+
colorDepth >= 24 ? (r, g, b) => sgr([open + 8, 2, ...clip(r, g, b)]) :
|
|
57
|
+
colorDepth >= 8 ? (r, g, b) => sgr([open + 8, 5, rgbToX256(...clip(r, g, b))]) :
|
|
58
|
+
colorDepth >= 4 ? (r, g, b) => sgr(open + rgbToX16(...clip(r, g, b))) :
|
|
59
|
+
colorDepth === 3 ?
|
|
60
|
+
open === fgOpen ?
|
|
61
|
+
(r, g, b) => x16ToX8(rgbToX16(...clip(r, g, b))) :
|
|
62
|
+
(r, g, b) => sgr(open + rgbToX8(...clip(r, g, b))) :
|
|
63
|
+
() => sgr(0),
|
|
64
|
+
|
|
65
|
+
/** @type {(...args: [number] | [number, number, number]) => SGR} */
|
|
66
|
+
x256:
|
|
67
|
+
colorDepth >= 8 ?
|
|
68
|
+
(...args) => sgr(
|
|
69
|
+
[open + 8, 5, args.length === 3 ? rgbToX256(...clip(...args)) : clip(args[0])],
|
|
70
|
+
) :
|
|
71
|
+
colorDepth >= 4 ?
|
|
72
|
+
(...args) => sgr(
|
|
73
|
+
open +
|
|
74
|
+
rgbToX16(...(args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])))),
|
|
75
|
+
) :
|
|
76
|
+
colorDepth === 3 ?
|
|
77
|
+
open === fgOpen ?
|
|
78
|
+
(...args) => x16ToX8(
|
|
79
|
+
args.length === 3 ?
|
|
80
|
+
rgbToX16(...clip(...args)) :
|
|
81
|
+
rgbToX16(...x256ToRgb(clip(args[0]))),
|
|
82
|
+
) :
|
|
83
|
+
(...args) => sgr(
|
|
84
|
+
open + rgbToX8(
|
|
85
|
+
...args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])),
|
|
86
|
+
),
|
|
87
|
+
) :
|
|
88
|
+
() => sgr(0),
|
|
89
|
+
|
|
90
|
+
default: colorDepth > 1 ? `\x1b[${close}m` : "",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** @type {Partial<Record<ColorDepth, {fg: Channel, bg: Channel}>>} */
|
|
95
|
+
const cache = {};
|
|
96
|
+
|
|
97
|
+
/** @type {(colorDepth: ColorDepth) => {fg: Channel, bg: Channel}} */
|
|
98
|
+
const makeColor = (colorDepth = 4) =>
|
|
99
|
+
cache[colorDepth] ?? (cache[colorDepth] = {
|
|
100
|
+
fg : makeChannel(colorDepth, fgOpen, fgClose),
|
|
101
|
+
bg : makeChannel(colorDepth, bgOpen, bgClose),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
module.exports = {makeColor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./color");
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface Channel {
|
|
2
|
+
black : SGR;
|
|
3
|
+
red : SGR;
|
|
4
|
+
green : SGR;
|
|
5
|
+
yellow : SGR;
|
|
6
|
+
blue : SGR;
|
|
7
|
+
magenta : SGR;
|
|
8
|
+
cyan : SGR;
|
|
9
|
+
white : SGR;
|
|
10
|
+
|
|
11
|
+
brightBlack : SGR;
|
|
12
|
+
brightRed : SGR;
|
|
13
|
+
brightGreen : SGR;
|
|
14
|
+
brightYellow : SGR;
|
|
15
|
+
brightBlue : SGR;
|
|
16
|
+
brightMagenta : SGR;
|
|
17
|
+
brightCyan : SGR;
|
|
18
|
+
brightWhite : SGR;
|
|
19
|
+
|
|
20
|
+
rgb: (r: number, g: number, b: number) => SGR;
|
|
21
|
+
|
|
22
|
+
x256: {
|
|
23
|
+
(code: number): SGR;
|
|
24
|
+
(r: number, g: number, b: number): SGR;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
default: string,
|
|
28
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** @typedef {import("../sgr").Code} Code */
|
|
2
|
+
|
|
3
|
+
/** @type {(r: number, g: number, b: number) => number} */
|
|
4
|
+
function rgbToX256(r, g, b) {
|
|
5
|
+
if (r === g && g === b) {
|
|
6
|
+
// grayscale
|
|
7
|
+
return 232 + Math.floor(r * 24 / 256);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// 6x6x6 color cube
|
|
11
|
+
return (
|
|
12
|
+
16 +
|
|
13
|
+
Math.floor(r * 6 / 256) * 36 +
|
|
14
|
+
Math.floor(g * 6 / 256) * 6 +
|
|
15
|
+
Math.floor(b * 6 / 256)
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @type {(code: number) => [number, number, number]} */
|
|
20
|
+
function x256ToRgb(code) {
|
|
21
|
+
if (code >= 232) {
|
|
22
|
+
// grayscale
|
|
23
|
+
const gray = Math.round((code - 232) * 255 / 24);
|
|
24
|
+
return [gray, gray, gray];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
code -= 16;
|
|
28
|
+
const r = Math.floor(code / 36) * 51;
|
|
29
|
+
const g = Math.floor((code % 36) / 6) * 51;
|
|
30
|
+
const b = (code % 6) * 51;
|
|
31
|
+
return [r, g, b];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @type {(r: number, g: number, b: number) => number} */
|
|
35
|
+
function rgbToX16(r, g, b) {
|
|
36
|
+
// First calculate it as a 4x4x4 cube.
|
|
37
|
+
|
|
38
|
+
r = Math.min(2, Math.floor(r / 85));
|
|
39
|
+
g = Math.min(2, Math.floor(g / 85));
|
|
40
|
+
b = Math.min(2, Math.floor(b / 85));
|
|
41
|
+
|
|
42
|
+
// Which channels are "on" determines the hue (ANSI color index 0–7).
|
|
43
|
+
const index = (r ? 1 : 0) | (g ? 2 : 0) | (b ? 4 : 0);
|
|
44
|
+
|
|
45
|
+
// The bright bit is derived from the max channel level across all three.
|
|
46
|
+
const twos = (r === 2 ? 1 : 0) + (g === 2 ? 1 : 0) + (b === 2 ? 1 : 0);
|
|
47
|
+
const ones = (r === 1 ? 1 : 0) + (g === 1 ? 1 : 0) + (b === 1 ? 1 : 0);
|
|
48
|
+
const bright = twos > 0 && twos >= ones;
|
|
49
|
+
|
|
50
|
+
// Return value uses the SGR offset scheme: 0–7 for normal, 60–67 for bright.
|
|
51
|
+
return index + (bright ? 60 : 0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @type {(r: number, g: number, b: number) => number} */
|
|
55
|
+
function rgbToX8(r, g, b) {
|
|
56
|
+
r = r >= 128 ? 1 : 0;
|
|
57
|
+
g = g >= 128 ? 1 : 0;
|
|
58
|
+
b = b >= 128 ? 1 : 0;
|
|
59
|
+
|
|
60
|
+
return (r ? 1 : 0) | (g ? 2 : 0) | (b ? 4 : 0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @overload
|
|
65
|
+
* @param {number} x
|
|
66
|
+
* @returns {number}
|
|
67
|
+
* @overload
|
|
68
|
+
* @param {number} r
|
|
69
|
+
* @param {number} g
|
|
70
|
+
* @param {number} b
|
|
71
|
+
* @returns {[number, number, number]}
|
|
72
|
+
*//** @param {[number] | [number, number, number]} args */
|
|
73
|
+
const clip = (...args) => args.length === 1 ?
|
|
74
|
+
Math.max(0, Math.min(255, Math.floor(args[0]))) :
|
|
75
|
+
[
|
|
76
|
+
Math.max(0, Math.min(255, Math.floor(args[0]))),
|
|
77
|
+
Math.max(0, Math.min(255, Math.floor(args[1]))),
|
|
78
|
+
Math.max(0, Math.min(255, Math.floor(args[2]))),
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
module.exports = {rgbToX256, x256ToRgb, rgbToX16, rgbToX8, clip};
|
package/src/sgr/index.js
ADDED
package/src/sgr/reset.js
ADDED
package/src/sgr/sgr.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
@typedef {number | number[]} Code
|
|
5
|
+
@typedef {{
|
|
6
|
+
(text: string): string;
|
|
7
|
+
open : string;
|
|
8
|
+
close : string;
|
|
9
|
+
codes : {open: Code, close: Code};
|
|
10
|
+
combine : (...styles: SGR[]) => SGR;
|
|
11
|
+
}} SGR
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @type {(code: Code, codes: Code[]) => Code} */
|
|
15
|
+
function combine(code, codes, ) {
|
|
16
|
+
if (!codes.length)
|
|
17
|
+
return code;
|
|
18
|
+
|
|
19
|
+
const rtn = Array.isArray(code) ? [...code] : [code];
|
|
20
|
+
|
|
21
|
+
for (const c of codes) {
|
|
22
|
+
if (Array.isArray(c))
|
|
23
|
+
rtn.push(...c);
|
|
24
|
+
else
|
|
25
|
+
rtn.push(c);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return rtn;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @type {(code: Code) => string | number} */
|
|
32
|
+
const codeSequence = code => Array.isArray(code) ? code.join(';') : code;
|
|
33
|
+
|
|
34
|
+
/** @type {(open: Code, close: Code, reset?: boolean) => SGR} */
|
|
35
|
+
function sgr(open, close, reset = false) {
|
|
36
|
+
const openCode = codeSequence(open);
|
|
37
|
+
const closeCode = codeSequence(close);
|
|
38
|
+
const openSequence = `\x1b[${openCode}m`;
|
|
39
|
+
const closeSequence = `\x1b[${closeCode}m`;
|
|
40
|
+
const reopenCode = reset ? `${closeCode};${openCode}` : openCode;
|
|
41
|
+
|
|
42
|
+
const rxClose = new RegExp(
|
|
43
|
+
`(?<start>\\x1b\\[(?:\\d+;)*)${closeCode}(?<end>(?:;\\d+)*m)`,
|
|
44
|
+
"g"
|
|
45
|
+
);
|
|
46
|
+
const replace = `$<start>${reopenCode}$<end>`;
|
|
47
|
+
|
|
48
|
+
/** @type {(text: string) => string} */
|
|
49
|
+
const func = s => openSequence + s.replace(rxClose, replace) + closeSequence;
|
|
50
|
+
|
|
51
|
+
return Object.assign(func, {
|
|
52
|
+
codes : {open, close},
|
|
53
|
+
open : openSequence,
|
|
54
|
+
close : closeSequence,
|
|
55
|
+
|
|
56
|
+
/** @type {(...styles: SGR[]) => SGR} */
|
|
57
|
+
combine: (...styles) => sgr(
|
|
58
|
+
combine(open, styles.map(s => s.codes.open)),
|
|
59
|
+
combine(close, styles.map(s => s.codes.close)),
|
|
60
|
+
),
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** @type {SGR} */
|
|
65
|
+
const disabledSGR = Object.assign(/** @type {(text: string) => string} */ s => s, {
|
|
66
|
+
codes : {open: 0, close: 0},
|
|
67
|
+
open : "",
|
|
68
|
+
close : "",
|
|
69
|
+
combine : () => disabledSGR,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/** @type {typeof sgr} */
|
|
73
|
+
const disabled = () => disabledSGR;
|
|
74
|
+
|
|
75
|
+
const makeSGR = (enabled = true) => enabled ? sgr : disabled;
|
|
76
|
+
|
|
77
|
+
module.exports = {makeSGR};
|
package/src/sgr/style.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {makeSGR} = require("./sgr");
|
|
4
|
+
|
|
5
|
+
function makeStyle(enabled = true) {
|
|
6
|
+
const sgr = makeSGR(enabled);
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
bold : sgr(1, 22, true),
|
|
10
|
+
dim : sgr(2, 22, true),
|
|
11
|
+
italic : sgr(3, 23),
|
|
12
|
+
underline : sgr(4, 24),
|
|
13
|
+
inverse : sgr(7, 27),
|
|
14
|
+
hidden : sgr(8, 28),
|
|
15
|
+
strikethrough : sgr(9, 29),
|
|
16
|
+
doubleUnderline : sgr(21, 24),
|
|
17
|
+
frame : sgr(51, 54),
|
|
18
|
+
encircle : sgr(52, 54),
|
|
19
|
+
overline : sgr(53, 55),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {makeStyle};
|
package/src/sgr/utils.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** @typedef {number | number[]} Code */
|
|
2
|
+
|
|
3
|
+
/** @type {(code: Code, codes: Code[]) => Code} */
|
|
4
|
+
function combine(code, codes, ) {
|
|
5
|
+
if (!codes.length)
|
|
6
|
+
return code;
|
|
7
|
+
|
|
8
|
+
const rtn = Array.isArray(code) ? [...code] : [code];
|
|
9
|
+
|
|
10
|
+
for (const c of codes) {
|
|
11
|
+
if (Array.isArray(c))
|
|
12
|
+
rtn.push(...c);
|
|
13
|
+
else
|
|
14
|
+
rtn.push(c);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return rtn;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = {combine};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** @typedef {import("./types").BuildAtom} BuildAtom */
|
|
4
|
+
|
|
5
|
+
/** @type {BuildAtom} */
|
|
6
|
+
function buildExtendedColorAtom(attribute, codes, index) {
|
|
7
|
+
switch (codes[index + 1]) {
|
|
8
|
+
case 2:
|
|
9
|
+
if (index + 5 >= codes.length)
|
|
10
|
+
return {attribute, code: codes.slice(index), skip: codes.length - index - 1};
|
|
11
|
+
return {attribute, code: codes.slice(index, index + 5), skip: 4};
|
|
12
|
+
case 5:
|
|
13
|
+
if (index + 3 >= codes.length)
|
|
14
|
+
return {attribute, code: codes.slice(index), skip: codes.length - index - 1};
|
|
15
|
+
return {attribute, code: codes.slice(index, index + 3), skip: 2};
|
|
16
|
+
default:
|
|
17
|
+
return {attribute, code: codes.slice(index, index + 2), skip: 1};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @type {BuildAtom} */
|
|
22
|
+
function buildIntensityAtom(attribute, codes, index, state) {
|
|
23
|
+
const code = codes[index];
|
|
24
|
+
if (code === 22)
|
|
25
|
+
return {attribute, code: [code]};
|
|
26
|
+
|
|
27
|
+
const current = state.get("intensity");
|
|
28
|
+
if (!current || current.length == 1 && current[0] === code || current[0] === 22)
|
|
29
|
+
return {attribute, code: [code]};
|
|
30
|
+
|
|
31
|
+
return {attribute, code: [1, 2]};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {buildExtendedColorAtom, buildIntensityAtom};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./simplify");
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {buildExtendedColorAtom, buildIntensityAtom} = require("./atom");
|
|
4
|
+
|
|
5
|
+
/** @typedef {import("./types").Atom} Atom */
|
|
6
|
+
/** @typedef {import("./types").Attribute} Attribute */
|
|
7
|
+
/** @typedef {import("./types").AttributeMap} AttributeMap */
|
|
8
|
+
/** @typedef {import("./types").BuildAtom} BuildAtom */
|
|
9
|
+
|
|
10
|
+
/** @type {Record<number, [attribute: Attribute, buildAtom?: BuildAtom]>} */
|
|
11
|
+
const codeMap = {
|
|
12
|
+
0 : ["reset"],
|
|
13
|
+
1 : ["intensity", buildIntensityAtom],
|
|
14
|
+
2 : ["intensity", buildIntensityAtom],
|
|
15
|
+
3 : ["italic"],
|
|
16
|
+
4 : ["underline"],
|
|
17
|
+
7 : ["inverse"],
|
|
18
|
+
8 : ["hidden"],
|
|
19
|
+
9 : ["strikethrough"],
|
|
20
|
+
21 : ["underline"],
|
|
21
|
+
22 : ["intensity", buildIntensityAtom],
|
|
22
|
+
23 : ["italic"],
|
|
23
|
+
24 : ["underline"],
|
|
24
|
+
27 : ["inverse"],
|
|
25
|
+
28 : ["hidden"],
|
|
26
|
+
29 : ["strikethrough"],
|
|
27
|
+
30 : ["fg"],
|
|
28
|
+
31 : ["fg"],
|
|
29
|
+
32 : ["fg"],
|
|
30
|
+
33 : ["fg"],
|
|
31
|
+
34 : ["fg"],
|
|
32
|
+
35 : ["fg"],
|
|
33
|
+
36 : ["fg"],
|
|
34
|
+
37 : ["fg"],
|
|
35
|
+
38 : ["fg", buildExtendedColorAtom],
|
|
36
|
+
39 : ["fg"],
|
|
37
|
+
40 : ["bg"],
|
|
38
|
+
41 : ["bg"],
|
|
39
|
+
42 : ["bg"],
|
|
40
|
+
43 : ["bg"],
|
|
41
|
+
44 : ["bg"],
|
|
42
|
+
45 : ["bg"],
|
|
43
|
+
46 : ["bg"],
|
|
44
|
+
47 : ["bg"],
|
|
45
|
+
48 : ["bg", buildExtendedColorAtom],
|
|
46
|
+
49 : ["bg"],
|
|
47
|
+
51 : ["frame"],
|
|
48
|
+
52 : ["frame"],
|
|
49
|
+
53 : ["overline"],
|
|
50
|
+
54 : ["frame"],
|
|
51
|
+
55 : ["overline"],
|
|
52
|
+
90 : ["fg"],
|
|
53
|
+
91 : ["fg"],
|
|
54
|
+
92 : ["fg"],
|
|
55
|
+
93 : ["fg"],
|
|
56
|
+
94 : ["fg"],
|
|
57
|
+
95 : ["fg"],
|
|
58
|
+
96 : ["fg"],
|
|
59
|
+
97 : ["fg"],
|
|
60
|
+
100 : ["bg"],
|
|
61
|
+
101 : ["bg"],
|
|
62
|
+
102 : ["bg"],
|
|
63
|
+
103 : ["bg"],
|
|
64
|
+
104 : ["bg"],
|
|
65
|
+
105 : ["bg"],
|
|
66
|
+
106 : ["bg"],
|
|
67
|
+
107 : ["bg"],
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** @type {(codes: number[], index: number, state: AttributeMap) => Atom} */
|
|
71
|
+
function lookUpAtom(codes, index, state) {
|
|
72
|
+
const code = codes[index];
|
|
73
|
+
const entry = codeMap[code];
|
|
74
|
+
|
|
75
|
+
if (!entry)
|
|
76
|
+
return {attribute: code, code: [code]};
|
|
77
|
+
|
|
78
|
+
const [attribute, buildAtom] = entry;
|
|
79
|
+
|
|
80
|
+
if (buildAtom)
|
|
81
|
+
return buildAtom(attribute, codes, index, state);
|
|
82
|
+
|
|
83
|
+
return {attribute, code: [code]};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {lookUpAtom};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {rxSGR} = require("../patterns");
|
|
4
|
+
const {State} = require("./state");
|
|
5
|
+
|
|
6
|
+
/** @type {(text: string) => string} */
|
|
7
|
+
function simplify(text) {
|
|
8
|
+
// `.split` doesn't modify `lastIndex`, so no clone is necessary here.
|
|
9
|
+
const parts = text.split(rxSGR);
|
|
10
|
+
|
|
11
|
+
const state = new State();
|
|
12
|
+
let result = parts[0];
|
|
13
|
+
|
|
14
|
+
for (let i = 1; i < parts.length; ) {
|
|
15
|
+
let codes = parts[i++].split(";").map(Number);
|
|
16
|
+
let chunk = parts[i++];
|
|
17
|
+
|
|
18
|
+
while (!chunk && i < parts.length) {
|
|
19
|
+
// Merge consecutive SGR sequences.
|
|
20
|
+
codes.push(...parts[i++].split(";").map(Number));
|
|
21
|
+
chunk = parts[i++];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
codes = state.update(codes);
|
|
25
|
+
if (codes.length)
|
|
26
|
+
result += `\x1b[${codes.join(";")}m`;
|
|
27
|
+
result += chunk;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {simplify};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {lookUpAtom} = require("./lookup");
|
|
4
|
+
const {arraysEqual, transitionIntensity} = require("./utils");
|
|
5
|
+
|
|
6
|
+
/** @typedef {import("./types").Attribute} Attribute */
|
|
7
|
+
/** @typedef {import("./types").AttributeMap} AttributeMap */
|
|
8
|
+
|
|
9
|
+
/** @type {Map<Attribute, number[]>} */
|
|
10
|
+
const defaultCodes = new Map([
|
|
11
|
+
["intensity", [22]],
|
|
12
|
+
["italic", [23]],
|
|
13
|
+
["underline", [24]],
|
|
14
|
+
["inverse", [27]],
|
|
15
|
+
["hidden", [28]],
|
|
16
|
+
["strikethrough",[29]],
|
|
17
|
+
["fg", [39]],
|
|
18
|
+
["bg", [49]],
|
|
19
|
+
["frame", [54]],
|
|
20
|
+
["overline", [55]],
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/** @type {AttributeMap} */
|
|
24
|
+
class State extends Map {
|
|
25
|
+
/** @type {(codes: number[]) => number[]} */
|
|
26
|
+
update(codes) {
|
|
27
|
+
/** @type {AttributeMap} */
|
|
28
|
+
let snapshot = new Map(this);
|
|
29
|
+
|
|
30
|
+
/** @type {Set<Attribute>} */
|
|
31
|
+
const attributes = new Set();
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < codes.length; i++) {
|
|
34
|
+
const {attribute, code, skip} = lookUpAtom(codes, i, this);
|
|
35
|
+
|
|
36
|
+
if (attribute === "reset") {
|
|
37
|
+
attributes.clear();
|
|
38
|
+
this.clear();
|
|
39
|
+
attributes.add("reset");
|
|
40
|
+
for (const [attr, defaultCode] of defaultCodes)
|
|
41
|
+
this.set(attr, defaultCode);
|
|
42
|
+
|
|
43
|
+
snapshot = new Map(this);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
attributes.add(attribute);
|
|
48
|
+
this.set(attribute, code);
|
|
49
|
+
|
|
50
|
+
if (skip)
|
|
51
|
+
i += skip;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Array.from(attributes).flatMap(attribute => {
|
|
55
|
+
if (attribute === "reset")
|
|
56
|
+
return [0];
|
|
57
|
+
|
|
58
|
+
const code = this.get(attribute);
|
|
59
|
+
|
|
60
|
+
if (arraysEqual(code, snapshot.get(attribute)))
|
|
61
|
+
return [];
|
|
62
|
+
|
|
63
|
+
if (attribute === "intensity") {
|
|
64
|
+
const previous = snapshot.get(attribute);
|
|
65
|
+
return transitionIntensity(previous, code);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return code;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {State};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type Attribute =
|
|
2
|
+
| "intensity"
|
|
3
|
+
| "italic"
|
|
4
|
+
| "underline"
|
|
5
|
+
| "inverse"
|
|
6
|
+
| "hidden"
|
|
7
|
+
| "strikethrough"
|
|
8
|
+
| "fg"
|
|
9
|
+
| "bg"
|
|
10
|
+
| "frame"
|
|
11
|
+
| "overline"
|
|
12
|
+
| "reset"
|
|
13
|
+
| number;
|
|
14
|
+
|
|
15
|
+
type AttributeMap = Map<Attribute, number[]>;
|
|
16
|
+
|
|
17
|
+
export interface Atom {
|
|
18
|
+
attribute : Attribute;
|
|
19
|
+
code : number[];
|
|
20
|
+
skip? : number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type BuildAtom = (
|
|
24
|
+
attribute: Attribute,
|
|
25
|
+
codes : number[],
|
|
26
|
+
index : number,
|
|
27
|
+
state : AttributeMap,
|
|
28
|
+
) => Atom;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** @type {<T>(a: T[] | undefined, b: T[] | undefined) => boolean} */
|
|
4
|
+
function arraysEqual(a, b) {
|
|
5
|
+
if (a === b)
|
|
6
|
+
return true;
|
|
7
|
+
|
|
8
|
+
if (!a || !b || a.length !== b.length)
|
|
9
|
+
return false;
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < a.length; i++) {
|
|
12
|
+
if (a[i] !== b[i])
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @type {(previous: number[] | undefined, current: number[]) => number[]} */
|
|
20
|
+
function transitionIntensity(previous, current) {
|
|
21
|
+
if (!previous || previous[0] === 22 || current[0] === 22)
|
|
22
|
+
return current;
|
|
23
|
+
|
|
24
|
+
const oldBold = previous[0] === 1;
|
|
25
|
+
const newBold = current[0] === 1;
|
|
26
|
+
const oldDim = previous[previous.length - 1] === 2;
|
|
27
|
+
const newDim = current[current.length - 1] === 2;
|
|
28
|
+
|
|
29
|
+
const result = [];
|
|
30
|
+
|
|
31
|
+
if (oldBold && !newBold || oldDim && !newDim)
|
|
32
|
+
result.push(22);
|
|
33
|
+
if (newBold && (!oldBold || result[0] === 22))
|
|
34
|
+
result.push(1);
|
|
35
|
+
if (newDim && (!oldDim || result[0] === 22))
|
|
36
|
+
result.push(2);
|
|
37
|
+
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = {arraysEqual, transitionIntensity};
|