mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
feat(console): support CSS styling with "%c" (#7357)
This commit is contained in:
parent
c1b4ff61c9
commit
6f70e6e72b
2 changed files with 602 additions and 109 deletions
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
|
const core = window.Deno.core;
|
||||||
const exposeForTest = window.__bootstrap.internals.exposeForTest;
|
const exposeForTest = window.__bootstrap.internals.exposeForTest;
|
||||||
const {
|
const {
|
||||||
stripColor,
|
stripColor,
|
||||||
|
@ -162,15 +163,6 @@
|
||||||
const LINE_BREAKING_LENGTH = 80;
|
const LINE_BREAKING_LENGTH = 80;
|
||||||
const MIN_GROUP_LENGTH = 6;
|
const MIN_GROUP_LENGTH = 6;
|
||||||
const STR_ABBREVIATE_SIZE = 100;
|
const STR_ABBREVIATE_SIZE = 100;
|
||||||
// Char codes
|
|
||||||
const CHAR_PERCENT = 37; /* % */
|
|
||||||
const CHAR_LOWERCASE_S = 115; /* s */
|
|
||||||
const CHAR_LOWERCASE_D = 100; /* d */
|
|
||||||
const CHAR_LOWERCASE_I = 105; /* i */
|
|
||||||
const CHAR_LOWERCASE_F = 102; /* f */
|
|
||||||
const CHAR_LOWERCASE_O = 111; /* o */
|
|
||||||
const CHAR_UPPERCASE_O = 79; /* O */
|
|
||||||
const CHAR_LOWERCASE_C = 99; /* c */
|
|
||||||
|
|
||||||
const PROMISE_STRING_BASE_LENGTH = 12;
|
const PROMISE_STRING_BASE_LENGTH = 12;
|
||||||
|
|
||||||
|
@ -401,7 +393,7 @@
|
||||||
level,
|
level,
|
||||||
inspectOptions,
|
inspectOptions,
|
||||||
) {
|
) {
|
||||||
const proxyDetails = Deno.core.getProxyDetails(value);
|
const proxyDetails = core.getProxyDetails(value);
|
||||||
if (proxyDetails != null) {
|
if (proxyDetails != null) {
|
||||||
return inspectOptions.showProxy
|
return inspectOptions.showProxy
|
||||||
? inspectProxy(proxyDetails, ctx, level, inspectOptions)
|
? inspectProxy(proxyDetails, ctx, level, inspectOptions)
|
||||||
|
@ -639,7 +631,7 @@
|
||||||
level,
|
level,
|
||||||
inspectOptions,
|
inspectOptions,
|
||||||
) {
|
) {
|
||||||
const [state, result] = Deno.core.getPromiseDetails(value);
|
const [state, result] = core.getPromiseDetails(value);
|
||||||
|
|
||||||
if (state === PromiseState.Pending) {
|
if (state === PromiseState.Pending) {
|
||||||
return `Promise { ${cyan("<pending>")} }`;
|
return `Promise { ${cyan("<pending>")} }`;
|
||||||
|
@ -810,116 +802,459 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function inspectArgs(
|
const colorKeywords = new Map([
|
||||||
args,
|
["black", "#000000"],
|
||||||
inspectOptions = {},
|
["silver", "#c0c0c0"],
|
||||||
) {
|
["gray", "#808080"],
|
||||||
const rInspectOptions = { ...DEFAULT_INSPECT_OPTIONS, ...inspectOptions };
|
["white", "#ffffff"],
|
||||||
const first = args[0];
|
["maroon", "#800000"],
|
||||||
let a = 0;
|
["red", "#ff0000"],
|
||||||
let str = "";
|
["purple", "#800080"],
|
||||||
let join = "";
|
["fuchsia", "#ff00ff"],
|
||||||
|
["green", "#008000"],
|
||||||
|
["lime", "#00ff00"],
|
||||||
|
["olive", "#808000"],
|
||||||
|
["yellow", "#ffff00"],
|
||||||
|
["navy", "#000080"],
|
||||||
|
["blue", "#0000ff"],
|
||||||
|
["teal", "#008080"],
|
||||||
|
["aqua", "#00ffff"],
|
||||||
|
["orange", "#ffa500"],
|
||||||
|
["aliceblue", "#f0f8ff"],
|
||||||
|
["antiquewhite", "#faebd7"],
|
||||||
|
["aquamarine", "#7fffd4"],
|
||||||
|
["azure", "#f0ffff"],
|
||||||
|
["beige", "#f5f5dc"],
|
||||||
|
["bisque", "#ffe4c4"],
|
||||||
|
["blanchedalmond", "#ffebcd"],
|
||||||
|
["blueviolet", "#8a2be2"],
|
||||||
|
["brown", "#a52a2a"],
|
||||||
|
["burlywood", "#deb887"],
|
||||||
|
["cadetblue", "#5f9ea0"],
|
||||||
|
["chartreuse", "#7fff00"],
|
||||||
|
["chocolate", "#d2691e"],
|
||||||
|
["coral", "#ff7f50"],
|
||||||
|
["cornflowerblue", "#6495ed"],
|
||||||
|
["cornsilk", "#fff8dc"],
|
||||||
|
["crimson", "#dc143c"],
|
||||||
|
["cyan", "#00ffff"],
|
||||||
|
["darkblue", "#00008b"],
|
||||||
|
["darkcyan", "#008b8b"],
|
||||||
|
["darkgoldenrod", "#b8860b"],
|
||||||
|
["darkgray", "#a9a9a9"],
|
||||||
|
["darkgreen", "#006400"],
|
||||||
|
["darkgrey", "#a9a9a9"],
|
||||||
|
["darkkhaki", "#bdb76b"],
|
||||||
|
["darkmagenta", "#8b008b"],
|
||||||
|
["darkolivegreen", "#556b2f"],
|
||||||
|
["darkorange", "#ff8c00"],
|
||||||
|
["darkorchid", "#9932cc"],
|
||||||
|
["darkred", "#8b0000"],
|
||||||
|
["darksalmon", "#e9967a"],
|
||||||
|
["darkseagreen", "#8fbc8f"],
|
||||||
|
["darkslateblue", "#483d8b"],
|
||||||
|
["darkslategray", "#2f4f4f"],
|
||||||
|
["darkslategrey", "#2f4f4f"],
|
||||||
|
["darkturquoise", "#00ced1"],
|
||||||
|
["darkviolet", "#9400d3"],
|
||||||
|
["deeppink", "#ff1493"],
|
||||||
|
["deepskyblue", "#00bfff"],
|
||||||
|
["dimgray", "#696969"],
|
||||||
|
["dimgrey", "#696969"],
|
||||||
|
["dodgerblue", "#1e90ff"],
|
||||||
|
["firebrick", "#b22222"],
|
||||||
|
["floralwhite", "#fffaf0"],
|
||||||
|
["forestgreen", "#228b22"],
|
||||||
|
["gainsboro", "#dcdcdc"],
|
||||||
|
["ghostwhite", "#f8f8ff"],
|
||||||
|
["gold", "#ffd700"],
|
||||||
|
["goldenrod", "#daa520"],
|
||||||
|
["greenyellow", "#adff2f"],
|
||||||
|
["grey", "#808080"],
|
||||||
|
["honeydew", "#f0fff0"],
|
||||||
|
["hotpink", "#ff69b4"],
|
||||||
|
["indianred", "#cd5c5c"],
|
||||||
|
["indigo", "#4b0082"],
|
||||||
|
["ivory", "#fffff0"],
|
||||||
|
["khaki", "#f0e68c"],
|
||||||
|
["lavender", "#e6e6fa"],
|
||||||
|
["lavenderblush", "#fff0f5"],
|
||||||
|
["lawngreen", "#7cfc00"],
|
||||||
|
["lemonchiffon", "#fffacd"],
|
||||||
|
["lightblue", "#add8e6"],
|
||||||
|
["lightcoral", "#f08080"],
|
||||||
|
["lightcyan", "#e0ffff"],
|
||||||
|
["lightgoldenrodyellow", "#fafad2"],
|
||||||
|
["lightgray", "#d3d3d3"],
|
||||||
|
["lightgreen", "#90ee90"],
|
||||||
|
["lightgrey", "#d3d3d3"],
|
||||||
|
["lightpink", "#ffb6c1"],
|
||||||
|
["lightsalmon", "#ffa07a"],
|
||||||
|
["lightseagreen", "#20b2aa"],
|
||||||
|
["lightskyblue", "#87cefa"],
|
||||||
|
["lightslategray", "#778899"],
|
||||||
|
["lightslategrey", "#778899"],
|
||||||
|
["lightsteelblue", "#b0c4de"],
|
||||||
|
["lightyellow", "#ffffe0"],
|
||||||
|
["limegreen", "#32cd32"],
|
||||||
|
["linen", "#faf0e6"],
|
||||||
|
["magenta", "#ff00ff"],
|
||||||
|
["mediumaquamarine", "#66cdaa"],
|
||||||
|
["mediumblue", "#0000cd"],
|
||||||
|
["mediumorchid", "#ba55d3"],
|
||||||
|
["mediumpurple", "#9370db"],
|
||||||
|
["mediumseagreen", "#3cb371"],
|
||||||
|
["mediumslateblue", "#7b68ee"],
|
||||||
|
["mediumspringgreen", "#00fa9a"],
|
||||||
|
["mediumturquoise", "#48d1cc"],
|
||||||
|
["mediumvioletred", "#c71585"],
|
||||||
|
["midnightblue", "#191970"],
|
||||||
|
["mintcream", "#f5fffa"],
|
||||||
|
["mistyrose", "#ffe4e1"],
|
||||||
|
["moccasin", "#ffe4b5"],
|
||||||
|
["navajowhite", "#ffdead"],
|
||||||
|
["oldlace", "#fdf5e6"],
|
||||||
|
["olivedrab", "#6b8e23"],
|
||||||
|
["orangered", "#ff4500"],
|
||||||
|
["orchid", "#da70d6"],
|
||||||
|
["palegoldenrod", "#eee8aa"],
|
||||||
|
["palegreen", "#98fb98"],
|
||||||
|
["paleturquoise", "#afeeee"],
|
||||||
|
["palevioletred", "#db7093"],
|
||||||
|
["papayawhip", "#ffefd5"],
|
||||||
|
["peachpuff", "#ffdab9"],
|
||||||
|
["peru", "#cd853f"],
|
||||||
|
["pink", "#ffc0cb"],
|
||||||
|
["plum", "#dda0dd"],
|
||||||
|
["powderblue", "#b0e0e6"],
|
||||||
|
["rosybrown", "#bc8f8f"],
|
||||||
|
["royalblue", "#4169e1"],
|
||||||
|
["saddlebrown", "#8b4513"],
|
||||||
|
["salmon", "#fa8072"],
|
||||||
|
["sandybrown", "#f4a460"],
|
||||||
|
["seagreen", "#2e8b57"],
|
||||||
|
["seashell", "#fff5ee"],
|
||||||
|
["sienna", "#a0522d"],
|
||||||
|
["skyblue", "#87ceeb"],
|
||||||
|
["slateblue", "#6a5acd"],
|
||||||
|
["slategray", "#708090"],
|
||||||
|
["slategrey", "#708090"],
|
||||||
|
["snow", "#fffafa"],
|
||||||
|
["springgreen", "#00ff7f"],
|
||||||
|
["steelblue", "#4682b4"],
|
||||||
|
["tan", "#d2b48c"],
|
||||||
|
["thistle", "#d8bfd8"],
|
||||||
|
["tomato", "#ff6347"],
|
||||||
|
["turquoise", "#40e0d0"],
|
||||||
|
["violet", "#ee82ee"],
|
||||||
|
["wheat", "#f5deb3"],
|
||||||
|
["whitesmoke", "#f5f5f5"],
|
||||||
|
["yellowgreen", "#9acd32"],
|
||||||
|
["rebeccapurple", "#663399"],
|
||||||
|
]);
|
||||||
|
|
||||||
if (typeof first === "string") {
|
function parseCssColor(colorString) {
|
||||||
let tempStr;
|
if (colorKeywords.has(colorString)) {
|
||||||
let lastPos = 0;
|
colorString = colorKeywords.get(colorString);
|
||||||
|
}
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const hashMatch = colorString.match(/^#([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})?$/);
|
||||||
|
if (hashMatch != null) {
|
||||||
|
return [
|
||||||
|
Number(`0x${hashMatch[1]}`),
|
||||||
|
Number(`0x${hashMatch[2]}`),
|
||||||
|
Number(`0x${hashMatch[3]}`),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const smallHashMatch = colorString.match(/^#([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])?$/);
|
||||||
|
if (smallHashMatch != null) {
|
||||||
|
return [
|
||||||
|
Number(`0x${smallHashMatch[1]}0`),
|
||||||
|
Number(`0x${smallHashMatch[2]}0`),
|
||||||
|
Number(`0x${smallHashMatch[3]}0`),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const rgbMatch = colorString.match(/^rgba?\(\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)\s*(,\s*([+\-]?\d*\.?\d+)\s*)?\)$/);
|
||||||
|
if (rgbMatch != null) {
|
||||||
|
return [
|
||||||
|
Math.round(Math.max(0, Math.min(255, Number(rgbMatch[1])))),
|
||||||
|
Math.round(Math.max(0, Math.min(255, Number(rgbMatch[2])))),
|
||||||
|
Math.round(Math.max(0, Math.min(255, Number(rgbMatch[3])))),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const hslMatch = colorString.match(/^hsla?\(\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)%\s*,\s*([+\-]?\d*\.?\d+)%\s*(,\s*([+\-]?\d*\.?\d+)\s*)?\)$/);
|
||||||
|
if (hslMatch != null) {
|
||||||
|
// https://www.rapidtables.com/convert/color/hsl-to-rgb.html
|
||||||
|
let h = Number(hslMatch[1]) % 360;
|
||||||
|
if (h < 0) {
|
||||||
|
h += 360;
|
||||||
|
}
|
||||||
|
const s = Math.max(0, Math.min(100, Number(hslMatch[2]))) / 100;
|
||||||
|
const l = Math.max(0, Math.min(100, Number(hslMatch[3]))) / 100;
|
||||||
|
const c = (1 - Math.abs(2 * l - 1)) * s;
|
||||||
|
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
|
||||||
|
const m = l - c / 2;
|
||||||
|
let r_;
|
||||||
|
let g_;
|
||||||
|
let b_;
|
||||||
|
if (h < 60) {
|
||||||
|
[r_, g_, b_] = [c, x, 0];
|
||||||
|
} else if (h < 120) {
|
||||||
|
[r_, g_, b_] = [x, c, 0];
|
||||||
|
} else if (h < 180) {
|
||||||
|
[r_, g_, b_] = [0, c, x];
|
||||||
|
} else if (h < 240) {
|
||||||
|
[r_, g_, b_] = [0, x, c];
|
||||||
|
} else if (h < 300) {
|
||||||
|
[r_, g_, b_] = [x, 0, c];
|
||||||
|
} else {
|
||||||
|
[r_, g_, b_] = [c, 0, x];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
Math.round((r_ + m) * 255),
|
||||||
|
Math.round((g_ + m) * 255),
|
||||||
|
Math.round((b_ + m) * 255),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < first.length - 1; i++) {
|
function parseCss(cssString) {
|
||||||
if (first.charCodeAt(i) === CHAR_PERCENT) {
|
const css = {
|
||||||
const nextChar = first.charCodeAt(++i);
|
backgroundColor: null,
|
||||||
if (a + 1 !== args.length) {
|
color: null,
|
||||||
switch (nextChar) {
|
fontWeight: null,
|
||||||
case CHAR_LOWERCASE_S:
|
fontStyle: null,
|
||||||
// format as a string
|
textDecorationColor: null,
|
||||||
tempStr = String(args[++a]);
|
textDecorationLine: [],
|
||||||
break;
|
};
|
||||||
case CHAR_LOWERCASE_D:
|
|
||||||
case CHAR_LOWERCASE_I:
|
|
||||||
// format as an integer
|
|
||||||
const tempInteger = args[++a];
|
|
||||||
if (typeof tempInteger === "bigint") {
|
|
||||||
tempStr = `${tempInteger}n`;
|
|
||||||
} else if (typeof tempInteger === "symbol") {
|
|
||||||
tempStr = "NaN";
|
|
||||||
} else {
|
|
||||||
tempStr = `${parseInt(String(tempInteger), 10)}`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CHAR_LOWERCASE_F:
|
|
||||||
// format as a floating point value
|
|
||||||
const tempFloat = args[++a];
|
|
||||||
if (typeof tempFloat === "symbol") {
|
|
||||||
tempStr = "NaN";
|
|
||||||
} else {
|
|
||||||
tempStr = `${parseFloat(String(tempFloat))}`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CHAR_LOWERCASE_O:
|
|
||||||
case CHAR_UPPERCASE_O:
|
|
||||||
// format as an object
|
|
||||||
tempStr = inspectValue(
|
|
||||||
args[++a],
|
|
||||||
new Set(),
|
|
||||||
0,
|
|
||||||
rInspectOptions,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case CHAR_PERCENT:
|
|
||||||
str += first.slice(lastPos, i);
|
|
||||||
lastPos = i + 1;
|
|
||||||
continue;
|
|
||||||
case CHAR_LOWERCASE_C:
|
|
||||||
// TODO: applies CSS style rules to the output string as specified
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
// any other character is not a correct placeholder
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastPos !== i - 1) {
|
const rawEntries = [];
|
||||||
str += first.slice(lastPos, i - 1);
|
let inValue = false;
|
||||||
}
|
let currentKey = null;
|
||||||
|
let parenthesesDepth = 0;
|
||||||
|
currentPart = "";
|
||||||
|
for (let i = 0; i < cssString.length; i++) {
|
||||||
|
const c = cssString[i];
|
||||||
|
if (c == "(") {
|
||||||
|
parenthesesDepth++;
|
||||||
|
} else if (parenthesesDepth > 0) {
|
||||||
|
if (c == ")") {
|
||||||
|
parenthesesDepth--;
|
||||||
|
}
|
||||||
|
} else if (inValue) {
|
||||||
|
if (c == ";") {
|
||||||
|
const value = currentPart.trim();
|
||||||
|
if (value != "") {
|
||||||
|
rawEntries.push([currentKey, value]);
|
||||||
|
}
|
||||||
|
currentKey = null;
|
||||||
|
currentPart = "";
|
||||||
|
inValue = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (c == ":") {
|
||||||
|
currentKey = currentPart.trim();
|
||||||
|
currentPart = "";
|
||||||
|
inValue = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
currentPart += c;
|
||||||
|
}
|
||||||
|
if (inValue && parenthesesDepth == 0) {
|
||||||
|
const value = currentPart.trim();
|
||||||
|
if (value != "") {
|
||||||
|
rawEntries.push([currentKey, value]);
|
||||||
|
}
|
||||||
|
currentKey = null;
|
||||||
|
currentPart = "";
|
||||||
|
}
|
||||||
|
|
||||||
str += tempStr;
|
for (const [key, value] of rawEntries) {
|
||||||
lastPos = i + 1;
|
if (key == "background-color") {
|
||||||
} else if (nextChar === CHAR_PERCENT) {
|
const color = parseCssColor(value);
|
||||||
str += first.slice(lastPos, i);
|
if (color != null) {
|
||||||
lastPos = i + 1;
|
css.backgroundColor = color;
|
||||||
|
}
|
||||||
|
} else if (key == "color") {
|
||||||
|
const color = parseCssColor(value);
|
||||||
|
if (color != null) {
|
||||||
|
css.color = color;
|
||||||
|
}
|
||||||
|
} else if (key == "font-weight") {
|
||||||
|
if (["normal", "bold"].includes(value)) {
|
||||||
|
css.fontWeight = value;
|
||||||
|
}
|
||||||
|
} else if (key == "font-style") {
|
||||||
|
if (["normal", "italic", "oblique", "oblique 14deg"].includes(value)) {
|
||||||
|
css.fontStyle = value;
|
||||||
|
}
|
||||||
|
} else if (key == "text-decoration-line") {
|
||||||
|
css.textDecorationLine = [];
|
||||||
|
for (const lineType of value.split(/\s+/g)) {
|
||||||
|
if (["line-through", "overline", "underline"].includes(lineType)) {
|
||||||
|
css.textDecorationLine.push(lineType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (key == "text-decoration-color") {
|
||||||
|
const color = parseCssColor(value);
|
||||||
if (lastPos !== 0) {
|
if (color != null) {
|
||||||
a++;
|
css.textDecorationColor = color;
|
||||||
join = " ";
|
}
|
||||||
if (lastPos < first.length) {
|
} else if (key == "text-decoration") {
|
||||||
str += first.slice(lastPos);
|
css.textDecorationColor = null;
|
||||||
|
css.textDecorationLine = [];
|
||||||
|
for (const arg of value.split(/\s+/g)) {
|
||||||
|
const maybeColor = parseCssColor(arg);
|
||||||
|
if (maybeColor != null) {
|
||||||
|
css.textDecorationColor = maybeColor;
|
||||||
|
} else if (["line-through", "overline", "underline"].includes(arg)) {
|
||||||
|
css.textDecorationLine.push(arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (a < args.length) {
|
return css;
|
||||||
const value = args[a];
|
}
|
||||||
str += join;
|
|
||||||
if (typeof value === "string") {
|
function cssToAnsi(css) {
|
||||||
str += value;
|
let ansi = "";
|
||||||
} else {
|
if (css.backgroundColor != null) {
|
||||||
// use default maximum depth for null or undefined argument
|
const [r, g, b] = css.backgroundColor;
|
||||||
str += inspectValue(value, new Set(), 0, rInspectOptions);
|
ansi += `\x1b[48;2;${r};${g};${b}m`;
|
||||||
}
|
} else {
|
||||||
join = " ";
|
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 (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.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("overline")) {
|
||||||
|
ansi += "\x1b[53m";
|
||||||
|
} else {
|
||||||
|
ansi += "\x1b[55m";
|
||||||
|
}
|
||||||
|
if (css.textDecorationLine.includes("underline")) {
|
||||||
|
ansi += "\x1b[4m";
|
||||||
|
} else {
|
||||||
|
ansi += "\x1b[24m";
|
||||||
|
}
|
||||||
|
return ansi;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inspectArgs(args, inspectOptions = {}) {
|
||||||
|
const noColor = globalThis.Deno?.noColor ?? true;
|
||||||
|
const rInspectOptions = { ...DEFAULT_INSPECT_OPTIONS, ...inspectOptions };
|
||||||
|
const first = args[0];
|
||||||
|
let a = 0;
|
||||||
|
let string = "";
|
||||||
|
|
||||||
|
if (typeof first == "string" && args.length > 1) {
|
||||||
a++;
|
a++;
|
||||||
|
// Index of the first not-yet-appended character. Use this so we only
|
||||||
|
// have to append to `string` when a substitution occurs / at the end.
|
||||||
|
let appendedChars = 0;
|
||||||
|
let usedStyle = false;
|
||||||
|
for (let i = 0; i < first.length - 1; i++) {
|
||||||
|
if (first[i] == "%") {
|
||||||
|
const char = first[++i];
|
||||||
|
if (a < args.length) {
|
||||||
|
let formattedArg = null;
|
||||||
|
if (char == "s") {
|
||||||
|
// Format as a string.
|
||||||
|
formattedArg = String(args[a++]);
|
||||||
|
} else if (["d", "i"].includes(char)) {
|
||||||
|
// Format as an integer.
|
||||||
|
const value = args[a++];
|
||||||
|
if (typeof value == "bigint") {
|
||||||
|
formattedArg = `${value}n`;
|
||||||
|
} else if (typeof value == "number") {
|
||||||
|
formattedArg = `${parseInt(String(value))}`;
|
||||||
|
} else {
|
||||||
|
formattedArg = "NaN";
|
||||||
|
}
|
||||||
|
} else if (char == "f") {
|
||||||
|
// Format as a floating point value.
|
||||||
|
const value = args[a++];
|
||||||
|
if (typeof value == "number") {
|
||||||
|
formattedArg = `${value}`;
|
||||||
|
} else {
|
||||||
|
formattedArg = "NaN";
|
||||||
|
}
|
||||||
|
} else if (["O", "o"].includes(char)) {
|
||||||
|
// Format as an object.
|
||||||
|
formattedArg = inspectValue(
|
||||||
|
args[a++],
|
||||||
|
new Set(),
|
||||||
|
0,
|
||||||
|
rInspectOptions,
|
||||||
|
);
|
||||||
|
} else if (char == "c") {
|
||||||
|
const value = args[a++];
|
||||||
|
formattedArg = noColor ? "" : cssToAnsi(parseCss(value));
|
||||||
|
if (formattedArg != "") {
|
||||||
|
usedStyle = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formattedArg != null) {
|
||||||
|
string += first.slice(appendedChars, i - 1) + formattedArg;
|
||||||
|
appendedChars = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (char == "%") {
|
||||||
|
string += first.slice(appendedChars, i - 1) + "%";
|
||||||
|
appendedChars = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string += first.slice(appendedChars);
|
||||||
|
if (usedStyle) {
|
||||||
|
string += "\x1b[0m";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; a < args.length; a++) {
|
||||||
|
if (a > 0) {
|
||||||
|
string += " ";
|
||||||
|
}
|
||||||
|
// Use default maximum depth for null or undefined arguments.
|
||||||
|
string += inspectValue(args[a], new Set(), 0, rInspectOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rInspectOptions.indentLevel > 0) {
|
if (rInspectOptions.indentLevel > 0) {
|
||||||
const groupIndent = DEFAULT_INDENT.repeat(rInspectOptions.indentLevel);
|
const groupIndent = DEFAULT_INDENT.repeat(rInspectOptions.indentLevel);
|
||||||
if (str.indexOf("\n") !== -1) {
|
string = groupIndent + string.replaceAll("\n", `\n${groupIndent}`);
|
||||||
str = str.replace(/\n/g, `\n${groupIndent}`);
|
|
||||||
}
|
|
||||||
str = groupIndent + str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const countMap = new Map();
|
const countMap = new Map();
|
||||||
|
@ -1202,7 +1537,10 @@
|
||||||
|
|
||||||
// Expose these fields to internalObject for tests.
|
// Expose these fields to internalObject for tests.
|
||||||
exposeForTest("Console", Console);
|
exposeForTest("Console", Console);
|
||||||
|
exposeForTest("cssToAnsi", cssToAnsi);
|
||||||
exposeForTest("inspectArgs", inspectArgs);
|
exposeForTest("inspectArgs", inspectArgs);
|
||||||
|
exposeForTest("parseCss", parseCss);
|
||||||
|
exposeForTest("parseCssColor", parseCssColor);
|
||||||
|
|
||||||
window.__bootstrap.console = {
|
window.__bootstrap.console = {
|
||||||
CSI,
|
CSI,
|
||||||
|
|
|
@ -14,7 +14,10 @@ import { stripColor } from "../../../std/fmt/colors.ts";
|
||||||
const customInspect = Deno.customInspect;
|
const customInspect = Deno.customInspect;
|
||||||
const {
|
const {
|
||||||
Console,
|
Console,
|
||||||
|
cssToAnsi: cssToAnsi_,
|
||||||
inspectArgs,
|
inspectArgs,
|
||||||
|
parseCss: parseCss_,
|
||||||
|
parseCssColor: parseCssColor_,
|
||||||
// @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol
|
// @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol
|
||||||
} = Deno[Deno.internal];
|
} = Deno[Deno.internal];
|
||||||
|
|
||||||
|
@ -22,6 +25,37 @@ function stringify(...args: unknown[]): string {
|
||||||
return stripColor(inspectArgs(args).replace(/\n$/, ""));
|
return stripColor(inspectArgs(args).replace(/\n$/, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Css {
|
||||||
|
backgroundColor: [number, number, number] | null;
|
||||||
|
color: [number, number, number] | null;
|
||||||
|
fontWeight: string | null;
|
||||||
|
fontStyle: string | null;
|
||||||
|
textDecorationColor: [number, number, number] | null;
|
||||||
|
textDecorationLine: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_CSS: Css = {
|
||||||
|
backgroundColor: null,
|
||||||
|
color: null,
|
||||||
|
fontWeight: null,
|
||||||
|
fontStyle: null,
|
||||||
|
textDecorationColor: null,
|
||||||
|
textDecorationLine: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseCss(cssString: string): Css {
|
||||||
|
return parseCss_(cssString);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCssColor(colorString: string): Css {
|
||||||
|
return parseCssColor_(colorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ANSI-fy the CSS, replace "\x1b" with "_". */
|
||||||
|
function cssToAnsiEsc(css: Css): string {
|
||||||
|
return cssToAnsi_(css).replaceAll("\x1b", "_");
|
||||||
|
}
|
||||||
|
|
||||||
// test cases from web-platform-tests
|
// test cases from web-platform-tests
|
||||||
// via https://github.com/web-platform-tests/wpt/blob/master/console/console-is-a-namespace.any.js
|
// via https://github.com/web-platform-tests/wpt/blob/master/console/console-is-a-namespace.any.js
|
||||||
unitTest(function consoleShouldBeANamespace(): void {
|
unitTest(function consoleShouldBeANamespace(): void {
|
||||||
|
@ -745,8 +779,7 @@ unitTest(function consoleTestWithIntegerFormatSpecifier(): void {
|
||||||
assertEquals(stringify("%i"), "%i");
|
assertEquals(stringify("%i"), "%i");
|
||||||
assertEquals(stringify("%i", 42.0), "42");
|
assertEquals(stringify("%i", 42.0), "42");
|
||||||
assertEquals(stringify("%i", 42), "42");
|
assertEquals(stringify("%i", 42), "42");
|
||||||
assertEquals(stringify("%i", "42"), "42");
|
assertEquals(stringify("%i", "42"), "NaN");
|
||||||
assertEquals(stringify("%i", "42.0"), "42");
|
|
||||||
assertEquals(stringify("%i", 1.5), "1");
|
assertEquals(stringify("%i", 1.5), "1");
|
||||||
assertEquals(stringify("%i", -0.5), "0");
|
assertEquals(stringify("%i", -0.5), "0");
|
||||||
assertEquals(stringify("%i", ""), "NaN");
|
assertEquals(stringify("%i", ""), "NaN");
|
||||||
|
@ -764,14 +797,13 @@ unitTest(function consoleTestWithFloatFormatSpecifier(): void {
|
||||||
assertEquals(stringify("%f"), "%f");
|
assertEquals(stringify("%f"), "%f");
|
||||||
assertEquals(stringify("%f", 42.0), "42");
|
assertEquals(stringify("%f", 42.0), "42");
|
||||||
assertEquals(stringify("%f", 42), "42");
|
assertEquals(stringify("%f", 42), "42");
|
||||||
assertEquals(stringify("%f", "42"), "42");
|
assertEquals(stringify("%f", "42"), "NaN");
|
||||||
assertEquals(stringify("%f", "42.0"), "42");
|
|
||||||
assertEquals(stringify("%f", 1.5), "1.5");
|
assertEquals(stringify("%f", 1.5), "1.5");
|
||||||
assertEquals(stringify("%f", -0.5), "-0.5");
|
assertEquals(stringify("%f", -0.5), "-0.5");
|
||||||
assertEquals(stringify("%f", Math.PI), "3.141592653589793");
|
assertEquals(stringify("%f", Math.PI), "3.141592653589793");
|
||||||
assertEquals(stringify("%f", ""), "NaN");
|
assertEquals(stringify("%f", ""), "NaN");
|
||||||
assertEquals(stringify("%f", Symbol("foo")), "NaN");
|
assertEquals(stringify("%f", Symbol("foo")), "NaN");
|
||||||
assertEquals(stringify("%f", 5n), "5");
|
assertEquals(stringify("%f", 5n), "NaN");
|
||||||
assertEquals(stringify("%f %f", 42, 43), "42 43");
|
assertEquals(stringify("%f %f", 42, 43), "42 43");
|
||||||
assertEquals(stringify("%f %f", 42), "42 %f");
|
assertEquals(stringify("%f %f", 42), "42 %f");
|
||||||
});
|
});
|
||||||
|
@ -799,6 +831,129 @@ unitTest(function consoleTestWithObjectFormatSpecifier(): void {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
unitTest(function consoleTestWithStyleSpecifier(): void {
|
||||||
|
assertEquals(stringify("%cfoo%cbar"), "%cfoo%cbar");
|
||||||
|
assertEquals(stringify("%cfoo%cbar", ""), "foo%cbar");
|
||||||
|
assertEquals(stripColor(stringify("%cfoo%cbar", "", "color: red")), "foobar");
|
||||||
|
});
|
||||||
|
|
||||||
|
unitTest(function consoleParseCssColor(): void {
|
||||||
|
assertEquals(parseCssColor("black"), [0, 0, 0]);
|
||||||
|
assertEquals(parseCssColor("darkmagenta"), [139, 0, 139]);
|
||||||
|
assertEquals(parseCssColor("slateblue"), [106, 90, 205]);
|
||||||
|
assertEquals(parseCssColor("#ffaa00"), [255, 170, 0]);
|
||||||
|
assertEquals(parseCssColor("#ffaa00"), [255, 170, 0]);
|
||||||
|
assertEquals(parseCssColor("#18d"), [16, 128, 208]);
|
||||||
|
assertEquals(parseCssColor("#18D"), [16, 128, 208]);
|
||||||
|
assertEquals(parseCssColor("rgb(100, 200, 50)"), [100, 200, 50]);
|
||||||
|
assertEquals(parseCssColor("rgb(+100.3, -200, .5)"), [100, 0, 1]);
|
||||||
|
assertEquals(parseCssColor("hsl(75, 60%, 40%)"), [133, 163, 41]);
|
||||||
|
|
||||||
|
assertEquals(parseCssColor("rgb(100,200,50)"), [100, 200, 50]);
|
||||||
|
assertEquals(
|
||||||
|
parseCssColor("rgb( \t\n100 \t\n, \t\n200 \t\n, \t\n50 \t\n)"),
|
||||||
|
[100, 200, 50],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
unitTest(function consoleParseCss(): void {
|
||||||
|
assertEquals(
|
||||||
|
parseCss("background-color: red"),
|
||||||
|
{ ...DEFAULT_CSS, backgroundColor: [255, 0, 0] },
|
||||||
|
);
|
||||||
|
assertEquals(parseCss("color: blue"), { ...DEFAULT_CSS, color: [0, 0, 255] });
|
||||||
|
assertEquals(
|
||||||
|
parseCss("font-weight: bold"),
|
||||||
|
{ ...DEFAULT_CSS, fontWeight: "bold" },
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
parseCss("font-style: italic"),
|
||||||
|
{ ...DEFAULT_CSS, fontStyle: "italic" },
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
parseCss("font-style: oblique"),
|
||||||
|
{ ...DEFAULT_CSS, fontStyle: "oblique" },
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
parseCss("text-decoration-color: green"),
|
||||||
|
{ ...DEFAULT_CSS, textDecorationColor: [0, 128, 0] },
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
parseCss("text-decoration-line: underline overline line-through"),
|
||||||
|
{
|
||||||
|
...DEFAULT_CSS,
|
||||||
|
textDecorationLine: ["underline", "overline", "line-through"],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
parseCss("text-decoration: yellow underline"),
|
||||||
|
{
|
||||||
|
...DEFAULT_CSS,
|
||||||
|
textDecorationColor: [255, 255, 0],
|
||||||
|
textDecorationLine: ["underline"],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
parseCss("color:red;font-weight:bold;"),
|
||||||
|
{ ...DEFAULT_CSS, color: [255, 0, 0], fontWeight: "bold" },
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
parseCss(
|
||||||
|
" \t\ncolor \t\n: \t\nred \t\n; \t\nfont-weight \t\n: \t\nbold \t\n; \t\n",
|
||||||
|
),
|
||||||
|
{ ...DEFAULT_CSS, color: [255, 0, 0], fontWeight: "bold" },
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
parseCss("color: red; font-weight: bold, font-style: italic"),
|
||||||
|
{ ...DEFAULT_CSS, color: [255, 0, 0] },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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",
|
||||||
|
);
|
||||||
|
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",
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
cssToAnsiEsc({ ...DEFAULT_CSS, textDecorationColor: [206, 207, 208] }),
|
||||||
|
"_[49m_[39m_[22m_[23m_[58;2;206;207;208m_[29m_[55m_[24m",
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
cssToAnsiEsc({ ...DEFAULT_CSS, textDecorationLine: ["underline"] }),
|
||||||
|
"_[49m_[39m_[22m_[23m_[59m_[29m_[55m_[4m",
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
cssToAnsiEsc(
|
||||||
|
{ ...DEFAULT_CSS, textDecorationLine: ["overline", "line-through"] },
|
||||||
|
),
|
||||||
|
"_[49m_[39m_[22m_[23m_[59m_[9m_[53m_[24m",
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
cssToAnsiEsc(
|
||||||
|
{ ...DEFAULT_CSS, color: [203, 204, 205], fontWeight: "bold" },
|
||||||
|
),
|
||||||
|
"_[49m_[38;2;203;204;205m_[1m_[23m_[59m_[29m_[55m_[24m",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
unitTest(function consoleTestWithVariousOrInvalidFormatSpecifier(): void {
|
unitTest(function consoleTestWithVariousOrInvalidFormatSpecifier(): void {
|
||||||
assertEquals(stringify("%s:%s"), "%s:%s");
|
assertEquals(stringify("%s:%s"), "%s:%s");
|
||||||
assertEquals(stringify("%i:%i"), "%i:%i");
|
assertEquals(stringify("%i:%i"), "%i:%i");
|
||||||
|
|
Loading…
Reference in a new issue