1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 13:00:36 -05:00

feat(cli/console): Add Deno.InspectOptions::colors (#7742)

Ref: https://github.com/denoland/deno/pull/7516#pullrequestreview-489567120
This commit is contained in:
Nayeem Rahman 2020-09-30 03:59:50 +01:00 committed by GitHub
parent 2184cf5c07
commit 27ee4b2551
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 55 deletions

View file

@ -1941,19 +1941,21 @@ declare namespace Deno {
export function run<T extends RunOptions = RunOptions>(opt: T): Process<T>;
export interface InspectOptions {
/** Traversal depth for nested objects. Defaults to 4. */
depth?: number;
/** Sort Object, Set and Map entries by key. Defaults to false. */
sorted?: boolean;
/** Add a trailing comma for multiline collections. Defaults to false. */
trailingComma?: boolean;
/** Stylize output with ANSI colors. Defaults to false. */
colors?: boolean;
/** Try to fit more than one entry of a collection on the same line.
* Defaults to true. */
compact?: boolean;
/** Traversal depth for nested objects. Defaults to 4. */
depth?: number;
/** The maximum number of iterable entries to print. Defaults to 100. */
iterableLimit?: number;
/** Show a Proxy's target and handler. Defaults to false. */
showProxy?: boolean;
/** Sort Object, Set and Map entries by key. Defaults to false. */
sorted?: boolean;
/** Add a trailing comma for multiline collections. Defaults to false. */
trailingComma?: boolean;
}
/** Converts the input into a string that has the same format as printed by

View file

@ -3,16 +3,7 @@
((window) => {
const core = window.Deno.core;
const exposeForTest = window.__bootstrap.internals.exposeForTest;
const {
stripColor,
yellow,
dim,
cyan,
red,
green,
magenta,
bold,
} = window.__bootstrap.colors;
const colors = window.__bootstrap.colors;
function isInvalidDate(x) {
return isNaN(x.getTime());
@ -88,7 +79,7 @@
}
function getStringWidth(str) {
str = stripColor(str).normalize("NFC");
str = colors.stripColor(str).normalize("NFC");
let width = 0;
for (const ch of str) {
@ -165,6 +156,7 @@
compact: true,
iterableLimit: 100,
showProxy: false,
colors: false,
};
const DEFAULT_INDENT = " "; // Default indent string
@ -198,6 +190,10 @@
return "";
}
function maybeColor(fn, inspectOptions) {
return inspectOptions.colors ? fn : (s) => s;
}
function inspectFunction(value, _ctx) {
if (customInspect in value && typeof value[customInspect] === "function") {
try {
@ -220,6 +216,7 @@
options,
inspectOptions,
) {
const cyan = maybeColor(colors.cyan, inspectOptions);
if (level >= inspectOptions.depth) {
return cyan(`[${options.typeName}]`);
}
@ -274,7 +271,7 @@
} else {
iContent = entries.length === 0 ? "" : ` ${entries.join(", ")} `;
if (
stripColor(iContent).length > LINE_BREAKING_LENGTH ||
colors.stripColor(iContent).length > LINE_BREAKING_LENGTH ||
!inspectOptions.compact
) {
iContent = `${initIndentation}${
@ -309,7 +306,7 @@
for (let i = 0; i < entriesLength; i++) {
// Taking colors into account: removing the ANSI color
// codes from the string before measuring its length
const len = stripColor(entries[i]).length;
const len = colors.stripColor(entries[i]).length;
dataLen[i] = len;
totalLength += len + separatorSpace;
if (maxLength < len) maxLength = len;
@ -414,6 +411,13 @@
: inspectValue(proxyDetails[0], ctx, level, inspectOptions);
}
const green = maybeColor(colors.green, inspectOptions);
const yellow = maybeColor(colors.yellow, inspectOptions);
const dim = maybeColor(colors.dim, inspectOptions);
const cyan = maybeColor(colors.cyan, inspectOptions);
const bold = maybeColor(colors.bold, inspectOptions);
const red = maybeColor(colors.red, inspectOptions);
switch (typeof value) {
case "string":
return green(quoteString(value));
@ -512,6 +516,7 @@
level,
inspectOptions,
) {
const green = maybeColor(colors.green, inspectOptions);
switch (typeof value) {
case "string":
const trunc = value.length > STR_ABBREVIATE_SIZE
@ -529,6 +534,7 @@
level,
inspectOptions,
) {
const dim = maybeColor(colors.dim, inspectOptions);
const options = {
typeName: "Array",
displayName: "",
@ -630,32 +636,39 @@
);
}
function inspectWeakSet() {
function inspectWeakSet(inspectOptions) {
const cyan = maybeColor(colors.cyan, inspectOptions);
return `WeakSet { ${cyan("[items unknown]")} }`; // as seen in Node, with cyan color
}
function inspectWeakMap() {
function inspectWeakMap(inspectOptions) {
const cyan = maybeColor(colors.cyan, inspectOptions);
return `WeakMap { ${cyan("[items unknown]")} }`; // as seen in Node, with cyan color
}
function inspectDate(value) {
function inspectDate(value, inspectOptions) {
// without quotes, ISO format, in magenta like before
const magenta = maybeColor(colors.magenta, inspectOptions);
return magenta(isInvalidDate(value) ? "Invalid Date" : value.toISOString());
}
function inspectRegExp(value) {
function inspectRegExp(value, inspectOptions) {
const red = maybeColor(colors.red, inspectOptions);
return red(value.toString()); // RegExps are red
}
function inspectStringObject(value) {
function inspectStringObject(value, inspectOptions) {
const cyan = maybeColor(colors.cyan, inspectOptions);
return cyan(`[String: "${value.toString()}"]`); // wrappers are in cyan
}
function inspectBooleanObject(value) {
function inspectBooleanObject(value, inspectOptions) {
const cyan = maybeColor(colors.cyan, inspectOptions);
return cyan(`[Boolean: ${value.toString()}]`); // wrappers are in cyan
}
function inspectNumberObject(value) {
function inspectNumberObject(value, inspectOptions) {
const cyan = maybeColor(colors.cyan, inspectOptions);
return cyan(`[Number: ${value.toString()}]`); // wrappers are in cyan
}
@ -671,6 +684,9 @@
level,
inspectOptions,
) {
const cyan = maybeColor(colors.cyan, inspectOptions);
const red = maybeColor(colors.red, inspectOptions);
const [state, result] = core.getPromiseDetails(value);
if (state === PromiseState.Pending) {
@ -714,6 +730,8 @@
level,
inspectOptions,
) {
const cyan = maybeColor(colors.cyan, inspectOptions);
if (level >= inspectOptions.depth) {
return cyan("[Object]"); // wrappers are in cyan
}
@ -770,7 +788,7 @@
}
// Making sure color codes are ignored when calculating the total length
const totalLength = entries.length + level +
stripColor(entries.join("")).length;
colors.stripColor(entries.join("")).length;
ctx.delete(value);
@ -822,25 +840,25 @@
} else if (Array.isArray(value)) {
return inspectArray(value, consoleContext, level, inspectOptions);
} else if (value instanceof Number) {
return inspectNumberObject(value);
return inspectNumberObject(value, inspectOptions);
} else if (value instanceof Boolean) {
return inspectBooleanObject(value);
return inspectBooleanObject(value, inspectOptions);
} else if (value instanceof String) {
return inspectStringObject(value);
return inspectStringObject(value, inspectOptions);
} else if (value instanceof Promise) {
return inspectPromise(value, consoleContext, level, inspectOptions);
} else if (value instanceof RegExp) {
return inspectRegExp(value);
return inspectRegExp(value, inspectOptions);
} else if (value instanceof Date) {
return inspectDate(value);
return inspectDate(value, inspectOptions);
} else if (value instanceof Set) {
return inspectSet(value, consoleContext, level, inspectOptions);
} else if (value instanceof Map) {
return inspectMap(value, consoleContext, level, inspectOptions);
} else if (value instanceof WeakSet) {
return inspectWeakSet();
return inspectWeakSet(inspectOptions);
} else if (value instanceof WeakMap) {
return inspectWeakMap();
return inspectWeakMap(inspectOptions);
} else if (isTypedArray(value)) {
return inspectTypedArray(
Object.getPrototypeOf(value).constructor.name,
@ -1318,6 +1336,11 @@
const timerMap = new Map();
const isConsoleInstance = Symbol("isConsoleInstance");
const CONSOLE_INSPECT_OPTIONS = {
...DEFAULT_INSPECT_OPTIONS,
colors: true,
};
class Console {
#printFunc = null;
[isConsoleInstance] = false;
@ -1339,6 +1362,7 @@
log = (...args) => {
this.#printFunc(
inspectArgs(args, {
...CONSOLE_INSPECT_OPTIONS,
indentLevel: this.indentLevel,
}) + "\n",
false,
@ -1349,7 +1373,10 @@
info = this.log;
dir = (obj, options = {}) => {
this.#printFunc(inspectArgs([obj], options) + "\n", false);
this.#printFunc(
inspectArgs([obj], { ...CONSOLE_INSPECT_OPTIONS, ...options }) + "\n",
false,
);
};
dirxml = this.dir;
@ -1357,6 +1384,7 @@
warn = (...args) => {
this.#printFunc(
inspectArgs(args, {
...CONSOLE_INSPECT_OPTIONS,
indentLevel: this.indentLevel,
}) + "\n",
true,
@ -1560,7 +1588,10 @@
};
trace = (...args) => {
const message = inspectArgs(args, { indentLevel: 0 });
const message = inspectArgs(
args,
{ ...CONSOLE_INSPECT_OPTIONS, indentLevel: 0 },
);
const err = {
name: "Trace",
message,

View file

@ -8,7 +8,12 @@
// std/fmt/colors auto determines whether to put colors in or not. We need
// better infrastructure here so we can properly test the colors.
import { assert, assertEquals, unitTest } from "./test_util.ts";
import {
assert,
assertEquals,
assertStringContains,
unitTest,
} from "./test_util.ts";
import { stripColor } from "../../../std/fmt/colors.ts";
const customInspect = Deno.customInspect;
@ -1650,3 +1655,8 @@ unitTest(function inspectProxy(): void {
"Proxy [ [Function: fn], { get: [Function: get] } ]",
);
});
unitTest(function inspectColors(): void {
assertEquals(Deno.inspect(1), "1");
assertStringContains(Deno.inspect(1, { colors: true }), "\x1b[");
});

View file

@ -7,7 +7,6 @@
import { sprintf } from "./printf.ts";
import { assertEquals } from "../testing/asserts.ts";
import { cyan, yellow } from "./colors.ts";
const S = sprintf;
@ -607,12 +606,12 @@ Deno.test("testWeirdos", function (): void {
Deno.test("formatV", function (): void {
const a = { a: { a: { a: { a: { a: { a: { a: {} } } } } } } };
assertEquals(S("%v", a), "[object Object]");
assertEquals(S("%#v", a), `{ a: { a: { a: { a: ${cyan("[Object]")} } } } }`);
assertEquals(S("%#v", a), `{ a: { a: { a: { a: [Object] } } } }`);
assertEquals(
S("%#.8v", a),
"{ a: { a: { a: { a: { a: { a: { a: {} } } } } } } }",
);
assertEquals(S("%#.1v", a), `{ a: ${cyan("[Object]")} }`);
assertEquals(S("%#.1v", a), `{ a: [Object] }`);
});
Deno.test("formatJ", function (): void {
@ -625,9 +624,7 @@ Deno.test("flagLessThan", function (): void {
const aArray = [a, a, a];
assertEquals(
S("%<#.1v", aArray),
`[ { a: ${cyan("[Object]")} }, { a: ${cyan("[Object]")} }, { a: ${
cyan("[Object]")
} } ]`,
`[ { a: [Object] }, { a: [Object] }, { a: [Object] } ]`,
);
const fArray = [1.2345, 0.98765, 123456789.5678];
assertEquals(S("%<.2f", fArray), "[ 1.23, 0.99, 123456789.57 ]");
@ -649,27 +646,21 @@ Deno.test("testErrors", function (): void {
assertEquals(S("%.*f", "a", 1.1), "%!(BAD PREC 'a')");
assertEquals(
S("%.[2]*f", 1.23, "p"),
`%!(BAD PREC 'p')%!(EXTRA '${yellow("1.23")}')`,
`%!(BAD PREC 'p')%!(EXTRA '1.23')`,
);
assertEquals(S("%.[2]*[1]f Yippie!", 1.23, "p"), "%!(BAD PREC 'p') Yippie!");
assertEquals(S("%[1]*.2f", "a", "p"), "%!(BAD WIDTH 'a')");
assertEquals(
S("A", "a", "p"),
`A%!(EXTRA '\x1b[32m"a"\x1b[39m' '\x1b[32m"p"\x1b[39m')`,
);
assertEquals(
S("%[2]s %[2]s", "a", "p"),
`p p%!(EXTRA '\x1b[32m"a"\x1b[39m')`,
);
assertEquals(S("A", "a", "p"), `A%!(EXTRA '"a"' '"p"')`);
assertEquals(S("%[2]s %[2]s", "a", "p"), `p p%!(EXTRA '"a"')`);
// remains to be determined how to handle bad indices ...
// (realistically) the entire error handling is still up for grabs.
assertEquals(S("%[hallo]s %d %d %d", 1, 2, 3, 4), "%!(BAD INDEX) 2 3 4");
assertEquals(
S("%[5]s", 1, 2, 3, 4),
`%!(BAD INDEX)%!(EXTRA '${yellow("2")}' '${yellow("3")}' '${yellow("4")}')`,
`%!(BAD INDEX)%!(EXTRA '2' '3' '4')`,
);
assertEquals(S("%[5]f"), "%!(BAD INDEX)");
assertEquals(S("%.[5]f"), "%!(BAD INDEX)");

View file

@ -21,13 +21,13 @@ export class AssertionError extends Error {
export function _format(v: unknown): string {
return globalThis.Deno
? stripColor(Deno.inspect(v, {
? Deno.inspect(v, {
depth: Infinity,
sorted: true,
trailingComma: true,
compact: false,
iterableLimit: Infinity,
}))
})
: `"${String(v).replace(/(?=["\\])/g, "\\")}"`;
}