From 7d3728e3f4b8c9764518bcbe7e4a49cd18fb1f90 Mon Sep 17 00:00:00 2001 From: Oliver Lenehan Date: Sat, 9 May 2020 20:29:44 +1000 Subject: [PATCH] feat(std/fmt): 8bit and 24bit ANSI colors (#5168) --- std/fmt/colors.ts | 113 +++++++++++++++++++++++++++++++---------- std/fmt/colors_test.ts | 38 ++++++++++++++ 2 files changed, 124 insertions(+), 27 deletions(-) diff --git a/std/fmt/colors.ts b/std/fmt/colors.ts index ad2a6a1bea..64b4458fcb 100644 --- a/std/fmt/colors.ts +++ b/std/fmt/colors.ts @@ -19,6 +19,13 @@ interface Code { regexp: RegExp; } +/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */ +interface Rgb { + r: number; + g: number; + b: number; +} + let enabled = !noColor; export function setColorEnabled(value: boolean): void { @@ -33,9 +40,9 @@ export function getColorEnabled(): boolean { return enabled; } -function code(open: number, close: number): Code { +function code(open: number[], close: number): Code { return { - open: `\x1b[${open}m`, + open: `\x1b[${open.join(";")}m`, close: `\x1b[${close}m`, regexp: new RegExp(`\\x1b\\[${close}m`, "g"), }; @@ -48,101 +55,153 @@ function run(str: string, code: Code): string { } export function reset(str: string): string { - return run(str, code(0, 0)); + return run(str, code([0], 0)); } export function bold(str: string): string { - return run(str, code(1, 22)); + return run(str, code([1], 22)); } export function dim(str: string): string { - return run(str, code(2, 22)); + return run(str, code([2], 22)); } export function italic(str: string): string { - return run(str, code(3, 23)); + return run(str, code([3], 23)); } export function underline(str: string): string { - return run(str, code(4, 24)); + return run(str, code([4], 24)); } export function inverse(str: string): string { - return run(str, code(7, 27)); + return run(str, code([7], 27)); } export function hidden(str: string): string { - return run(str, code(8, 28)); + return run(str, code([8], 28)); } export function strikethrough(str: string): string { - return run(str, code(9, 29)); + return run(str, code([9], 29)); } export function black(str: string): string { - return run(str, code(30, 39)); + return run(str, code([30], 39)); } export function red(str: string): string { - return run(str, code(31, 39)); + return run(str, code([31], 39)); } export function green(str: string): string { - return run(str, code(32, 39)); + return run(str, code([32], 39)); } export function yellow(str: string): string { - return run(str, code(33, 39)); + return run(str, code([33], 39)); } export function blue(str: string): string { - return run(str, code(34, 39)); + return run(str, code([34], 39)); } export function magenta(str: string): string { - return run(str, code(35, 39)); + return run(str, code([35], 39)); } export function cyan(str: string): string { - return run(str, code(36, 39)); + return run(str, code([36], 39)); } export function white(str: string): string { - return run(str, code(37, 39)); + return run(str, code([37], 39)); } export function gray(str: string): string { - return run(str, code(90, 39)); + return run(str, code([90], 39)); } export function bgBlack(str: string): string { - return run(str, code(40, 49)); + return run(str, code([40], 49)); } export function bgRed(str: string): string { - return run(str, code(41, 49)); + return run(str, code([41], 49)); } export function bgGreen(str: string): string { - return run(str, code(42, 49)); + return run(str, code([42], 49)); } export function bgYellow(str: string): string { - return run(str, code(43, 49)); + return run(str, code([43], 49)); } export function bgBlue(str: string): string { - return run(str, code(44, 49)); + return run(str, code([44], 49)); } export function bgMagenta(str: string): string { - return run(str, code(45, 49)); + return run(str, code([45], 49)); } export function bgCyan(str: string): string { - return run(str, code(46, 49)); + return run(str, code([46], 49)); } export function bgWhite(str: string): string { - return run(str, code(47, 49)); + return run(str, code([47], 49)); +} + +/* Special Color Sequences */ + +function clampAndTruncate(n: number, max = 255, min = 0): number { + return Math.trunc(Math.max(Math.min(n, max), min)); +} + +/** Set text color using paletted 8bit colors. + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit */ +export function rgb8(str: string, color: number): string { + return run(str, code([38, 5, clampAndTruncate(color)], 39)); +} + +/** Set background color using paletted 8bit colors. + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit */ +export function bgRgb8(str: string, color: number): string { + return run(str, code([48, 5, clampAndTruncate(color)], 49)); +} + +/** Set text color using 24bit rgb. */ +export function rgb24(str: string, color: Rgb): string { + return run( + str, + code( + [ + 38, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b), + ], + 39 + ) + ); +} + +/** Set background color using 24bit rgb. */ +export function bgRgb24(str: string, color: Rgb): string { + return run( + str, + code( + [ + 48, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b), + ], + 49 + ) + ); } diff --git a/std/fmt/colors_test.ts b/std/fmt/colors_test.ts index 6c80b41095..cfa189d301 100644 --- a/std/fmt/colors_test.ts +++ b/std/fmt/colors_test.ts @@ -118,3 +118,41 @@ Deno.test("testBgCyan", function (): void { Deno.test("testBgWhite", function (): void { assertEquals(c.bgWhite("foo bar"), "foo bar"); }); + +Deno.test("testClampUsingRgb8", function (): void { + assertEquals(c.rgb8("foo bar", -10), "foo bar"); +}); + +Deno.test("testTruncateUsingRgb8", function (): void { + assertEquals(c.rgb8("foo bar", 42.5), "foo bar"); +}); + +Deno.test("testRgb8", function (): void { + assertEquals(c.rgb8("foo bar", 42), "foo bar"); +}); + +Deno.test("test_bgRgb8", function (): void { + assertEquals(c.bgRgb8("foo bar", 42), "foo bar"); +}); + +Deno.test("test_rgb24", function (): void { + assertEquals( + c.rgb24("foo bar", { + r: 41, + g: 42, + b: 43, + }), + "foo bar" + ); +}); + +Deno.test("test_bgRgb24", function (): void { + assertEquals( + c.bgRgb24("foo bar", { + r: 41, + g: 42, + b: 43, + }), + "foo bar" + ); +});