Skip to content

Commit

Permalink
console output with format (denoland#1565)
Browse files Browse the repository at this point in the history
  • Loading branch information
justjavac authored and ry committed Jan 24, 2019
1 parent 7f88b5f commit 6904628
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 20 deletions.
137 changes: 117 additions & 20 deletions js/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ type ConsoleOptions = Partial<{
// Default depth of logging nested objects
const DEFAULT_MAX_DEPTH = 4;

// 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 */

// tslint:disable-next-line:no-any
function getClassInstanceName(instance: any): string {
if (typeof instance !== "object") {
Expand Down Expand Up @@ -342,38 +352,125 @@ export function stringifyArgs(
args: any[],
options: ConsoleOptions = {}
): string {
const out: string[] = [];
const { collapsedAt, indentLevel } = options;
for (const a of args) {
if (typeof a === "string") {
out.push(a);
const first = args[0];
let a = 0;
let str = "";
let join = "";

if (typeof first === "string") {
let tempStr: string;
let lastPos = 0;

for (let i = 0; i < first.length - 1; i++) {
if (first.charCodeAt(i) === CHAR_PERCENT) {
const nextChar = first.charCodeAt(++i);
if (a + 1 !== args.length) {
switch (nextChar) {
case CHAR_LOWERCASE_S:
// format as a string
tempStr = String(args[++a]);
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(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(tempFloat)}`;
}
break;
case CHAR_LOWERCASE_O:
case CHAR_UPPERCASE_O:
// format as an object
tempStr = stringify(
args[++a],
// tslint:disable-next-line:no-any
new Set<any>(),
0,
// tslint:disable-next-line:triple-equals
options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH
);
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) {
str += first.slice(lastPos, i - 1);
}

str += tempStr;
lastPos = i + 1;
} else if (nextChar === CHAR_PERCENT) {
str += first.slice(lastPos, i);
lastPos = i + 1;
}
}
}

if (lastPos !== 0) {
a++;
join = " ";
if (lastPos < first.length) {
str += first.slice(lastPos);
}
}
}

while (a < args.length) {
const value = args[a];
str += join;
if (typeof value === "string") {
str += value;
} else {
out.push(
// use default maximum depth for null or undefined argument
stringify(
a,
// tslint:disable-next-line:no-any
new Set<any>(),
0,
// tslint:disable-next-line:triple-equals
options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH
)
// use default maximum depth for null or undefined argument
str += stringify(
value,
// tslint:disable-next-line:no-any
new Set<any>(),
0,
// tslint:disable-next-line:triple-equals
options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH
);
}
join = " ";
a++;
}
let outstr = out.join(" ");

const { collapsedAt, indentLevel } = options;
if (
!isCollapsed(collapsedAt, indentLevel) &&
indentLevel != null &&
indentLevel > 0
) {
const groupIndent = " ".repeat(indentLevel);
if (outstr.indexOf("\n") !== -1) {
outstr = outstr.replace(/\n/g, `\n${groupIndent}`);
if (str.indexOf("\n") !== -1) {
str = str.replace(/\n/g, `\n${groupIndent}`);
}
outstr = groupIndent + outstr;
str = groupIndent + str;
}
return outstr;

return str;
}

type PrintFunc = (x: string, isErr?: boolean, printsNewline?: boolean) => void;
Expand Down
74 changes: 74 additions & 0 deletions js/console_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,80 @@ test(function consoleTestStringifyWithDepth() {
);
});

test(function consoleTestWithIntegerFormatSpecifier() {
assertEqual(stringify("%i"), "%i");
assertEqual(stringify("%i", 42.0), "42");
assertEqual(stringify("%i", 42), "42");
assertEqual(stringify("%i", "42"), "42");
assertEqual(stringify("%i", "42.0"), "42");
assertEqual(stringify("%i", 1.5), "1");
assertEqual(stringify("%i", -0.5), "0");
assertEqual(stringify("%i", ""), "NaN");
assertEqual(stringify("%i", Symbol()), "NaN");
assertEqual(stringify("%i %d", 42, 43), "42 43");
assertEqual(stringify("%d %i", 42), "42 %i");
assertEqual(stringify("%d", 12345678901234567890123), "1");
assertEqual(
stringify("%i", 12345678901234567890123n),
"12345678901234567890123n"
);
});

test(function consoleTestWithFloatFormatSpecifier() {
assertEqual(stringify("%f"), "%f");
assertEqual(stringify("%f", 42.0), "42");
assertEqual(stringify("%f", 42), "42");
assertEqual(stringify("%f", "42"), "42");
assertEqual(stringify("%f", "42.0"), "42");
assertEqual(stringify("%f", 1.5), "1.5");
assertEqual(stringify("%f", -0.5), "-0.5");
assertEqual(stringify("%f", Math.PI), "3.141592653589793");
assertEqual(stringify("%f", ""), "NaN");
assertEqual(stringify("%f", Symbol("foo")), "NaN");
assertEqual(stringify("%f", 5n), "5");
assertEqual(stringify("%f %f", 42, 43), "42 43");
assertEqual(stringify("%f %f", 42), "42 %f");
});

test(function consoleTestWithStringFormatSpecifier() {
assertEqual(stringify("%s"), "%s");
assertEqual(stringify("%s", undefined), "undefined");
assertEqual(stringify("%s", "foo"), "foo");
assertEqual(stringify("%s", 42), "42");
assertEqual(stringify("%s", "42"), "42");
assertEqual(stringify("%s %s", 42, 43), "42 43");
assertEqual(stringify("%s %s", 42), "42 %s");
assertEqual(stringify("%s", Symbol("foo")), "Symbol(foo)");
});

test(function consoleTestWithObjectFormatSpecifier() {
assertEqual(stringify("%o"), "%o");
assertEqual(stringify("%o", 42), "42");
assertEqual(stringify("%o", "foo"), "foo");
assertEqual(stringify("o: %o, a: %O", {}, []), "o: {}, a: []");
assertEqual(stringify("%o", { a: 42 }), "{ a: 42 }");
assertEqual(
stringify("%o", { a: { b: { c: { d: new Set([1]) } } } }),
"{ a: { b: { c: { d: [Set] } } } }"
);
});

test(function consoleTestWithVariousOrInvalidFormatSpecifier() {
assertEqual(stringify("%s:%s"), "%s:%s");
assertEqual(stringify("%i:%i"), "%i:%i");
assertEqual(stringify("%d:%d"), "%d:%d");
assertEqual(stringify("%%s%s", "foo"), "%sfoo");
assertEqual(stringify("%s:%s", undefined), "undefined:%s");
assertEqual(stringify("%s:%s", "foo", "bar"), "foo:bar");
assertEqual(stringify("%s:%s", "foo", "bar", "baz"), "foo:bar baz");
assertEqual(stringify("%%%s%%", "hi"), "%hi%");
assertEqual(stringify("%d:%d", 12), "12:%d");
assertEqual(stringify("%i:%i", 12), "12:%i");
assertEqual(stringify("%f:%f", 12), "12:%f");
assertEqual(stringify("o: %o, a: %o", {}), "o: {}, a: %o");
assertEqual(stringify("abc%", 1), "abc% 1");
});

test(function consoleTestCallToStringOnLabel() {
const methods = ["count", "countReset", "time", "timeLog", "timeEnd"];

Expand Down

0 comments on commit 6904628

Please sign in to comment.