From 72425810b869a5401f368d1865cb77c2a0e567a2 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 5 Oct 2020 10:50:49 +0100 Subject: [PATCH] perf(cli/console): Don't add redundant ANSI codes (#7823) --- cli/rt/02_console.js | 130 ++++++++++++++++++++++----------- cli/tests/unit/console_test.ts | 40 +++++----- 2 files changed, 104 insertions(+), 66 deletions(-) diff --git a/cli/rt/02_console.js b/cli/rt/02_console.js index 913ea44f29..8c853deb98 100644 --- a/cli/rt/02_console.js +++ b/cli/rt/02_console.js @@ -1097,8 +1097,8 @@ return null; } - function parseCss(cssString) { - const css = { + function getDefaultCss() { + return { backgroundColor: null, color: null, fontWeight: null, @@ -1106,6 +1106,10 @@ textDecorationColor: null, textDecorationLine: [], }; + } + + function parseCss(cssString) { + const css = getDefaultCss(); const rawEntries = []; let inValue = false; @@ -1160,12 +1164,12 @@ css.color = color; } } else if (key == "font-weight") { - if (["normal", "bold"].includes(value)) { + if (value == "bold") { css.fontWeight = value; } } else if (key == "font-style") { - if (["normal", "italic", "oblique", "oblique 14deg"].includes(value)) { - css.fontStyle = value; + if (["italic", "oblique", "oblique 14deg"].includes(value)) { + css.fontStyle = "italic"; } } else if (key == "text-decoration-line") { css.textDecorationLine = []; @@ -1196,50 +1200,81 @@ return css; } - function cssToAnsi(css) { + function colorEquals(color1, color2) { + return color1?.[0] == color2?.[0] && color1?.[1] == color2?.[1] && + color1?.[2] == color2?.[2]; + } + + function cssToAnsi(css, prevCss = null) { + prevCss = prevCss ?? getDefaultCss(); let ansi = ""; - if (css.backgroundColor != null) { - const [r, g, b] = css.backgroundColor; - ansi += `\x1b[48;2;${r};${g};${b}m`; - } else { - ansi += "\x1b[49m"; + if (!colorEquals(css.backgroundColor, prevCss.backgroundColor)) { + if (css.backgroundColor != null) { + const [r, g, b] = css.backgroundColor; + ansi += `\x1b[48;2;${r};${g};${b}m`; + } else { + ansi += "\x1b[49m"; + } } - if (css.color != null) { - const [r, g, b] = css.color; - ansi += `\x1b[38;2;${r};${g};${b}m`; - } else { - ansi += "\x1b[39m"; + if (!colorEquals(css.color, prevCss.color)) { + if (css.color != null) { + const [r, g, b] = css.color; + ansi += `\x1b[38;2;${r};${g};${b}m`; + } else { + ansi += "\x1b[39m"; + } } - if (css.fontWeight == "bold") { - ansi += `\x1b[1m`; - } else { - ansi += "\x1b[22m"; + if (css.fontWeight != prevCss.fontWeight) { + if (css.fontWeight == "bold") { + ansi += `\x1b[1m`; + } else { + ansi += "\x1b[22m"; + } } - if (["italic", "oblique"].includes(css.fontStyle)) { - ansi += `\x1b[3m`; - } else { - ansi += "\x1b[23m"; + if (css.fontStyle != prevCss.fontStyle) { + if (css.fontStyle == "italic") { + ansi += `\x1b[3m`; + } else { + ansi += "\x1b[23m"; + } } - if (css.textDecorationColor != null) { - const [r, g, b] = css.textDecorationColor; - ansi += `\x1b[58;2;${r};${g};${b}m`; - } else { - ansi += "\x1b[59m"; + if (!colorEquals(css.textDecorationColor, prevCss.textDecorationColor)) { + if (css.textDecorationColor != null) { + const [r, g, b] = css.textDecorationColor; + ansi += `\x1b[58;2;${r};${g};${b}m`; + } else { + ansi += "\x1b[59m"; + } } - if (css.textDecorationLine.includes("line-through")) { - ansi += "\x1b[9m"; - } else { - ansi += "\x1b[29m"; + if ( + css.textDecorationLine.includes("line-through") != + prevCss.textDecorationLine.includes("line-through") + ) { + if (css.textDecorationLine.includes("line-through")) { + ansi += "\x1b[9m"; + } else { + ansi += "\x1b[29m"; + } } - if (css.textDecorationLine.includes("overline")) { - ansi += "\x1b[53m"; - } else { - ansi += "\x1b[55m"; + if ( + css.textDecorationLine.includes("overline") != + prevCss.textDecorationLine.includes("overline") + ) { + if (css.textDecorationLine.includes("overline")) { + ansi += "\x1b[53m"; + } else { + ansi += "\x1b[55m"; + } } - if (css.textDecorationLine.includes("underline")) { - ansi += "\x1b[4m"; - } else { - ansi += "\x1b[24m"; + if ( + css.textDecorationLine.includes("underline") != + prevCss.textDecorationLine.includes("underline") + ) { + if (css.textDecorationLine.includes("underline")) { + ansi += "\x1b[4m"; + } else { + ansi += "\x1b[24m"; + } } return ansi; } @@ -1257,6 +1292,7 @@ // have to append to `string` when a substitution occurs / at the end. let appendedChars = 0; let usedStyle = false; + let prevCss = null; for (let i = 0; i < first.length - 1; i++) { if (first[i] == "%") { const char = first[++i]; @@ -1293,9 +1329,15 @@ ); } else if (char == "c") { const value = args[a++]; - formattedArg = noColor ? "" : cssToAnsi(parseCss(value)); - if (formattedArg != "") { - usedStyle = true; + if (!noColor) { + const css = parseCss(value); + formattedArg = cssToAnsi(css, prevCss); + if (formattedArg != "") { + usedStyle = true; + prevCss = css; + } + } else { + formattedArg = ""; } } diff --git a/cli/tests/unit/console_test.ts b/cli/tests/unit/console_test.ts index caee51a31f..2270f9724d 100644 --- a/cli/tests/unit/console_test.ts +++ b/cli/tests/unit/console_test.ts @@ -57,8 +57,8 @@ function parseCssColor(colorString: string): Css { } /** ANSI-fy the CSS, replace "\x1b" with "_". */ -function cssToAnsiEsc(css: Css): string { - return cssToAnsi_(css).replaceAll("\x1b", "_"); +function cssToAnsiEsc(css: Css, prevCss: Css | null = null): string { + return cssToAnsi_(css, prevCss).replaceAll("\x1b", "_"); } // test cases from web-platform-tests @@ -944,7 +944,7 @@ unitTest(function consoleParseCss(): void { ); assertEquals( parseCss("font-style: oblique"), - { ...DEFAULT_CSS, fontStyle: "oblique" }, + { ...DEFAULT_CSS, fontStyle: "italic" }, ); assertEquals( parseCss("text-decoration-color: green"), @@ -983,46 +983,42 @@ unitTest(function consoleParseCss(): void { }); unitTest(function consoleCssToAnsi(): void { - // TODO(nayeemrmn): Optimize these by accounting for the previous CSS. assertEquals( cssToAnsiEsc({ ...DEFAULT_CSS, backgroundColor: [200, 201, 202] }), - "_[48;2;200;201;202m_[39m_[22m_[23m_[59m_[29m_[55m_[24m", + "_[48;2;200;201;202m", ); assertEquals( cssToAnsiEsc({ ...DEFAULT_CSS, color: [203, 204, 205] }), - "_[49m_[38;2;203;204;205m_[22m_[23m_[59m_[29m_[55m_[24m", - ); - assertEquals( - cssToAnsiEsc({ ...DEFAULT_CSS, fontWeight: "bold" }), - "_[49m_[39m_[1m_[23m_[59m_[29m_[55m_[24m", - ); - assertEquals( - cssToAnsiEsc({ ...DEFAULT_CSS, fontStyle: "italic" }), - "_[49m_[39m_[22m_[3m_[59m_[29m_[55m_[24m", - ); - assertEquals( - cssToAnsiEsc({ ...DEFAULT_CSS, fontStyle: "oblique" }), - "_[49m_[39m_[22m_[3m_[59m_[29m_[55m_[24m", + "_[38;2;203;204;205m", ); + assertEquals(cssToAnsiEsc({ ...DEFAULT_CSS, fontWeight: "bold" }), "_[1m"); + assertEquals(cssToAnsiEsc({ ...DEFAULT_CSS, fontStyle: "italic" }), "_[3m"); assertEquals( cssToAnsiEsc({ ...DEFAULT_CSS, textDecorationColor: [206, 207, 208] }), - "_[49m_[39m_[22m_[23m_[58;2;206;207;208m_[29m_[55m_[24m", + "_[58;2;206;207;208m", ); assertEquals( cssToAnsiEsc({ ...DEFAULT_CSS, textDecorationLine: ["underline"] }), - "_[49m_[39m_[22m_[23m_[59m_[29m_[55m_[4m", + "_[4m", ); assertEquals( cssToAnsiEsc( { ...DEFAULT_CSS, textDecorationLine: ["overline", "line-through"] }, ), - "_[49m_[39m_[22m_[23m_[59m_[9m_[53m_[24m", + "_[9m_[53m", ); assertEquals( cssToAnsiEsc( { ...DEFAULT_CSS, color: [203, 204, 205], fontWeight: "bold" }, ), - "_[49m_[38;2;203;204;205m_[1m_[23m_[59m_[29m_[55m_[24m", + "_[38;2;203;204;205m_[1m", + ); + assertEquals( + cssToAnsiEsc( + { ...DEFAULT_CSS, color: [0, 0, 0], fontWeight: "bold" }, + { ...DEFAULT_CSS, color: [203, 204, 205], fontStyle: "italic" }, + ), + "_[38;2;0;0;0m_[1m_[23m", ); });