diff --git a/libraries/deno-commons-mod.ts b/libraries/deno-commons-mod.ts index f652d9e..c991352 100644 --- a/libraries/deno-commons-mod.ts +++ b/libraries/deno-commons-mod.ts @@ -240,6 +240,136 @@ export async function printLastLine(line: string) { ); } +interface ColorToken { + type: "color" | "text"; + content?: string; + colorStart?: boolean; + color?: string; +} + +function parseColorTokens(message: string): ColorToken[] { + const tokens: ColorToken[] = []; + if (message) { + let inColorStart = false; + let inColorEnd = false; + let chars: string[] = []; + const messageLength = message.length; + for (let i = 0; i < messageLength; i++) { + const c = message.charAt(i); + const nextC = (i + 1) < messageLength + ? message.charAt(i + 1) + : null; + switch (c) { + case "\\": + if (nextC === null) { + chars.push(c); + } else { + chars.push(nextC); + i++; + } + break; + case "[": + if (inColorStart || inColorEnd) { + // SHOULD NOT HAPPEN + break; + } else if (nextC == "/") { + inColorEnd = true; + i++; + } else { + inColorStart = true; + } + if (chars.length > 0) { + tokens.push({ + type: "text", + content: chars.join(""), + }); + chars = []; + } + break; + case "]": + if (inColorStart || inColorEnd) { + if (chars.length > 0) { + tokens.push({ + type: "color", + colorStart: inColorStart, + color: chars.join(""), + }); + chars = []; + } + inColorStart = false; + inColorEnd = false; + } else { + chars.push(c); + } + break; + default: + chars.push(c); + break; + } + } + const inColor = inColorStart || inColorEnd; + if (chars.length > 0 && !inColor) { + tokens.push({ + type: "text", + content: chars.join(""), + }); + } + } + return tokens; +} + +const COLOR_MAP: Record = { + blink: "5", + bold: "1", + under: "4", + red: "31", + green: "32", + yellow: "33", + blue: "34", + pink: "35", + cyan: "36", +}; + +function renderColorTokens(tokens: ColorToken[]): string { + const text: string[] = []; + const colorMapStack = new Map(); + for (const token of tokens) { + if (token.type === "color") { + const color = token.color; + if (color) { + const colorStack = colorMapStack.get(color) ?? []; + if (colorStack.length == 0) { + colorMapStack.set(color, colorStack); + } + if (token.colorStart) { + colorStack.push(1); + } else { + colorStack.pop(); + text.push("\x1b[0m"); + } + const colors: string[] = []; + for (const [color, colorStack] of colorMapStack) { + if (colorStack.length > 0) { + const colorCode = COLOR_MAP[color]; + if (colorCode) { + colors.push(colorCode); + } + } + } + if (colors.length > 0) { + text.push(`\x1b[${colors.join(";")}m`); + } + } + } else { + if (token.content) { + text.push(token.content); + } + } + } + text.push("\x1b[0m"); // FINALLY END ALL COLOR + return text.join(""); +} + class Term { constructor() { } @@ -279,6 +409,10 @@ class Term { cyan(message: string): string { return `\x1b[36m${message}\x1b[0m`; } + + auto(message: string): string { + return renderColorTokens(parseColorTokens(message)); + } } export const term = new Term();