Browse Source

util: Divide inspect() into some subroutines

koichik 13 years ago
parent
commit
98b64422bb
  1. 184
      lib/util.js

184
lib/util.js

@ -95,12 +95,19 @@ var error = exports.error = function(x) {
* output. Default is false (no coloring). * output. Default is false (no coloring).
*/ */
function inspect(obj, showHidden, depth, colors) { function inspect(obj, showHidden, depth, colors) {
var seen = []; var ctx = {
showHidden: showHidden,
seen: [],
stylize: colors ? stylizeWithColor : stylizeNoColor,
};
return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth));
}
exports.inspect = inspect;
var stylize = function(str, styleType) {
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
var styles = var colors = {
{ 'bold' : [1, 22], 'bold' : [1, 22],
'italic' : [3, 23], 'italic' : [3, 23],
'underline' : [4, 24], 'underline' : [4, 24],
'inverse' : [7, 27], 'inverse' : [7, 27],
@ -112,10 +119,11 @@ function inspect(obj, showHidden, depth, colors) {
'green' : [32, 39], 'green' : [32, 39],
'magenta' : [35, 39], 'magenta' : [35, 39],
'red' : [31, 39], 'red' : [31, 39],
'yellow' : [33, 39] }; 'yellow' : [33, 39]
};
var style = var styles = {
{ 'special': 'cyan', 'special': 'cyan',
'number': 'blue', 'number': 'blue',
'boolean': 'yellow', 'boolean': 'yellow',
'undefined': 'grey', 'undefined': 'grey',
@ -123,20 +131,28 @@ function inspect(obj, showHidden, depth, colors) {
'string': 'green', 'string': 'green',
'date': 'magenta', 'date': 'magenta',
// "name": intentionally not styling // "name": intentionally not styling
'regexp': 'red' }[styleType]; 'regexp': 'red'
};
function stylizeWithColor(str, styleType) {
var style = styles[styleType];
if (style) { if (style) {
return '\033[' + styles[style][0] + 'm' + str + return '\033[' + colors[style][0] + 'm' + str +
'\033[' + styles[style][1] + 'm'; '\033[' + colors[style][1] + 'm';
} else { } else {
return str; return str;
} }
}; }
if (! colors) {
stylize = function(str, styleType) { return str; };
}
function format(value, recurseTimes) { function stylizeNoColor(str, styleType) {
return str;
}
function formatValue(ctx, value, recurseTimes) {
// Provide a hook for user-specified inspect functions. // Provide a hook for user-specified inspect functions.
// Check that value is an object with an inspect function on it // Check that value is an object with an inspect function on it
if (value && typeof value.inspect === 'function' && if (value && typeof value.inspect === 'function' &&
@ -148,63 +164,41 @@ function inspect(obj, showHidden, depth, colors) {
} }
// Primitive types cannot have properties // Primitive types cannot have properties
switch (typeof value) { var primitive = formatPrimitive(ctx, value);
case 'undefined': if (primitive) {
return stylize('undefined', 'undefined'); return primitive;
case 'string':
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
.replace(/'/g, "\\'")
.replace(/\\"/g, '"') + '\'';
return stylize(simple, 'string');
case 'number':
return stylize('' + value, 'number');
case 'boolean':
return stylize('' + value, 'boolean');
}
// For some reason typeof null is "object", so special case here.
if (value === null) {
return stylize('null', 'null');
} }
// Look up the keys of the object. // Look up the keys of the object.
var visible_keys = Object.keys(value); var visibleKeys = Object.keys(value);
var keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys; var keys = ctx.showHidden ? Object.getOwnPropertyNames(value) : visibleKeys;
// Functions without properties can be shortcutted. // Some type of object without properties can be shortcutted.
if (typeof value === 'function' && keys.length === 0) { if (keys.length === 0) {
if (typeof value === 'function') {
var name = value.name ? ': ' + value.name : ''; var name = value.name ? ': ' + value.name : '';
return stylize('[Function' + name + ']', 'special'); return ctx.stylize('[Function' + name + ']', 'special');
} }
if (isRegExp(value)) {
// RegExp without properties can be shortcutted return ctx.stylize('' + value, 'regexp');
if (isRegExp(value) && keys.length === 0) {
return stylize('' + value, 'regexp');
} }
if (isDate(value)) {
// Dates without properties can be shortcutted return ctx.stylize(value.toUTCString(), 'date');
if (isDate(value) && keys.length === 0) {
return stylize(value.toUTCString(), 'date');
} }
}
var base = '', array = false, braces = ['{', '}'];
var base, type, braces; // Make Array say that they are Array
// Determine the object type
if (isArray(value)) { if (isArray(value)) {
type = 'Array'; array = true;
braces = ['[', ']']; braces = ['[', ']'];
} else {
type = 'Object';
braces = ['{', '}'];
} }
// Make functions say that they are functions // Make functions say that they are functions
if (typeof value === 'function') { if (typeof value === 'function') {
var n = value.name ? ': ' + value.name : ''; var n = value.name ? ': ' + value.name : '';
base = ' [Function' + n + ']'; base = ' [Function' + n + ']';
} else {
base = '';
} }
// Make RegExps say that they are RegExps // Make RegExps say that they are RegExps
@ -223,41 +217,75 @@ function inspect(obj, showHidden, depth, colors) {
if (recurseTimes < 0) { if (recurseTimes < 0) {
if (isRegExp(value)) { if (isRegExp(value)) {
return stylize('' + value, 'regexp'); return ctx.stylize('' + value, 'regexp');
} else { } else {
return stylize('[Object]', 'special'); return ctx.stylize('[Object]', 'special');
} }
} }
seen.push(value); ctx.seen.push(value);
var output = keys.map(function(key) { var output = keys.map(function(key) {
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
});
ctx.seen.pop();
return reduceToSingleString(output, base, braces);
}
function formatPrimitive(ctx, value) {
switch (typeof value) {
case 'undefined':
return ctx.stylize('undefined', 'undefined');
case 'string':
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
.replace(/'/g, "\\'")
.replace(/\\"/g, '"') + '\'';
return ctx.stylize(simple, 'string');
case 'number':
return ctx.stylize('' + value, 'number');
case 'boolean':
return ctx.stylize('' + value, 'boolean');
}
// For some reason typeof null is "object", so special case here.
if (value === null) {
return ctx.stylize('null', 'null');
}
}
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
var name, str; var name, str;
if (value.__lookupGetter__) { if (value.__lookupGetter__) {
if (value.__lookupGetter__(key)) { if (value.__lookupGetter__(key)) {
if (value.__lookupSetter__(key)) { if (value.__lookupSetter__(key)) {
str = stylize('[Getter/Setter]', 'special'); str = ctx.stylize('[Getter/Setter]', 'special');
} else { } else {
str = stylize('[Getter]', 'special'); str = ctx.stylize('[Getter]', 'special');
} }
} else { } else {
if (value.__lookupSetter__(key)) { if (value.__lookupSetter__(key)) {
str = stylize('[Setter]', 'special'); str = ctx.stylize('[Setter]', 'special');
} }
} }
} }
if (visible_keys.indexOf(key) < 0) { if (visibleKeys.indexOf(key) < 0) {
name = '[' + key + ']'; name = '[' + key + ']';
} }
if (!str) { if (!str) {
if (seen.indexOf(value[key]) < 0) { if (ctx.seen.indexOf(value[key]) < 0) {
if (recurseTimes === null) { if (recurseTimes === null) {
str = format(value[key]); str = formatValue(ctx, value[key], null);
} else { } else {
str = format(value[key], recurseTimes - 1); str = formatValue(ctx, value[key], recurseTimes - 1);
} }
if (str.indexOf('\n') > -1) { if (str.indexOf('\n') > -1) {
if (isArray(value)) { if (array) {
str = str.split('\n').map(function(line) { str = str.split('\n').map(function(line) {
return ' ' + line; return ' ' + line;
}).join('\n').substr(2); }).join('\n').substr(2);
@ -268,30 +296,30 @@ function inspect(obj, showHidden, depth, colors) {
} }
} }
} else { } else {
str = stylize('[Circular]', 'special'); str = ctx.stylize('[Circular]', 'special');
} }
} }
if (typeof name === 'undefined') { if (typeof name === 'undefined') {
if (type === 'Array' && key.match(/^\d+$/)) { if (array && key.match(/^\d+$/)) {
return str; return str;
} }
name = JSON.stringify('' + key); name = JSON.stringify('' + key);
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
name = name.substr(1, name.length - 2); name = name.substr(1, name.length - 2);
name = stylize(name, 'name'); name = ctx.stylize(name, 'name');
} else { } else {
name = name.replace(/'/g, "\\'") name = name.replace(/'/g, "\\'")
.replace(/\\"/g, '"') .replace(/\\"/g, '"')
.replace(/(^"|"$)/g, "'"); .replace(/(^"|"$)/g, "'");
name = stylize(name, 'string'); name = ctx.stylize(name, 'string');
} }
} }
return name + ': ' + str; return name + ': ' + str;
}); }
seen.pop();
function reduceToSingleString(output, base, braces) {
var numLinesEst = 0; var numLinesEst = 0;
var length = output.reduce(function(prev, cur) { var length = output.reduce(function(prev, cur) {
numLinesEst++; numLinesEst++;
@ -300,22 +328,16 @@ function inspect(obj, showHidden, depth, colors) {
}, 0); }, 0);
if (length > (require('readline').columns || 50)) { if (length > (require('readline').columns || 50)) {
output = braces[0] + return braces[0] +
(base === '' ? '' : base + '\n ') + (base === '' ? '' : base + '\n ') +
' ' + ' ' +
output.join(',\n ') + output.join(',\n ') +
' ' + ' ' +
braces[1]; braces[1];
} else {
output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
} }
return output; return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}
return format(obj, (typeof depth === 'undefined' ? 2 : depth));
} }
exports.inspect = inspect;
function isArray(ar) { function isArray(ar) {

Loading…
Cancel
Save