From d9678aa0ddf262c65719ecd5cfe41fcd32826db1 Mon Sep 17 00:00:00 2001 From: Jens Ayton Date: Tue, 4 Jan 2011 19:14:47 +0000 Subject: [PATCH] Output prettification for debug console (and improved :d macro). git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@4001 127b21dd-08f5-0310-b4b7-95ae10353056 --- DebugOXP/Resources/debugConfig.plist | 3 +- DebugOXP/Resources/oolite-debug-console.js | 235 +++++++++++++++++++-- 2 files changed, 213 insertions(+), 25 deletions(-) diff --git a/DebugOXP/Resources/debugConfig.plist b/DebugOXP/Resources/debugConfig.plist index 927e046e..ac3c692a 100644 --- a/DebugOXP/Resources/debugConfig.plist +++ b/DebugOXP/Resources/debugConfig.plist @@ -58,8 +58,7 @@ "resetM" = "delete console.settings.macros; macros = console.settings.macros; undefined"; // Examining (“dumping”) objects - "d" = "dumpObjectLong(eval(PARAM))"; - "ds" = "dumpObjectShort(eval(PARAM))"; + "d" = "dumpObject(eval(PARAM))"; // Clearing the console "clr" = "console.clearConsole()"; diff --git a/DebugOXP/Resources/oolite-debug-console.js b/DebugOXP/Resources/oolite-debug-console.js index 91147110..36c889cf 100644 --- a/DebugOXP/Resources/oolite-debug-console.js +++ b/DebugOXP/Resources/oolite-debug-console.js @@ -191,20 +191,7 @@ this.macros = {}; // **** Convenience functions -- copy this script and add your own here. -// List the enumerable properties of an object. -this.dumpObjectShort = function dumpObjectShort(x) -{ - consoleMessage("dumpObject", x.toString() + ":"); - for (var prop in x) - { - if (prop.hasOwnProperty(name)) - { - consoleMessage("dumpObject", " " + prop); - } - } -} - - +// Call an Objective-C method, such as a legacy script command, on the player. this.performLegacyCommand = function performLegacyCommand(x) { var [command, params] = x.getOneToken(); @@ -212,17 +199,18 @@ this.performLegacyCommand = function performLegacyCommand(x) } -// List the enumerable properties of an object, and their values. -this.dumpObjectLong = function dumpObjectLong(x) +// List the properties and values of an object. +this.dumpObject = function dumpObject(x) { - consoleMessage("dumpObject", x.toString() + ":"); - for (var prop in x) + var description; + if (typeof x == "object") { - if (prop.hasOwnProperty(name)) - { - consoleMessage("dumpObject", " " + prop + " = " + x[prop]); - } + if (Array.isArray(x)) description = prettifyArray(x); + else description = prettifyObject(x); } + else description = prettify(x); + + consoleMessage("dumpObject", description); } @@ -320,11 +308,133 @@ this.evaluate = function evaluate(command, type, PARAM) { if (result === null) result = "null"; else this.$ = result; - consoleMessage("command-result", result.toString()); + consoleMessage("command-result", prettify(result)); } } +this.prettifyArray = function prettifyArray(value, indent) +{ + // NOTE: value may be an Arguments object. + var i, length = value.length; + var result = "["; + for (i = 0; i < length; i++) + { + if (i > 0) result += ", "; + result += prettifyElement(value[i], indent); + } + result += "]"; + + return result; +} + +this.prettifyObject = function prettifyObject(value, indent) +{ + indent = indent || ""; + var subIndent = indent + "\t"; + + var appendedAny = false; + var result = "{"; + var separator = ",\n" + subIndent; + for (var key in value) + { + if (appendedAny) result += separator; + else result += "\n" + subIndent; + + /* + Highlighting inherited properties sounds desireable, but in practice + it’s likely to be confusing since most host objects’ apparent + instance properties are actually inherited accessor-based properties. + */ + // if (!value.hasOwnProperty(key)) result += ">> "; + + // Quote string if necessary. + if (isClassicIdentifier(key)) result += key; + else result += '"' + key.substituteEscapeCodes() + '"'; + + result += ": " + prettifyElement(value[key], subIndent); + appendedAny = true; + } + if (appendedAny) result += "\n" + indent; + result += "}"; + + return result; +} + + +this.prettifyFunction = function prettifyFunction(value, indent) +{ + var funcDesc = value.toString(); + if (indent) + { + funcDesc = funcDesc.replace(/\n/g, "\n" + indent); + } + return funcDesc; +} + + +this.prettify = function prettify(value, indent) +{ + try + { + if (value === undefined) return "undefined"; + if (value === null) return "null"; + + var type = typeof value; + if (type == "boolean" || + type == "number" || + type == "xml" || + value.constructor === Number || + value.constructor === Boolean) + { + return value.toString(); + } + + if (type == "string" || value.constructor === String) + { + return value; + } + + if (type == "function") + { + return prettifyFunction(value, indent); + } + + if (Array.isArray(value)) return prettifyArray(value, indent); + + var stringValue = value.toString(); + if (stringValue == "[object Object]") + { + return prettifyObject(value, indent); + } + if (stringValue == "[object Arguments]" && value.length !== undefined) + { + return prettifyArray(value, indent); + } + + return stringValue; + } + catch (e) + { + return value.toString(); + } +} + + +this.prettifyElement = function prettifyElement(value, indent) +{ + if (value === undefined) return "undefined"; + if (value === null) return "null"; + + if (typeof value == "string" || value.constructor === String) + { + return '"' + value.substituteEscapeCodes() + '"'; + } + + return prettify(value, indent); +} + + // **** Macro handling this.setMacro = function setMacro(parameters) @@ -501,6 +611,85 @@ String.prototype.substituteEscapeCodes = function substituteEscapeCodes() } +this.isClassicIdentifier = function isClassicIdentifier(string) +{ + /* + JavaScript allows any Unicode letter or digit in an indentifier. + However, JavaScript regexps don’t have shortcuts for Unicode letters + and digits. Smort! + Therefore, this function only returns true for ASCII identifiers. + */ + if (!string) return false; // Note that the empty string is a falsey value. + + if (/[^\w\$]/.test(string)) return false; // Contains non-identifier characters. + if (/\d/.test(string[0])) return false; // Starts with a digit. + + var reservedWords = + [ + // ECMAScript 5 keywords. + "break", + "case", + "catch", + "continue", + "debugger", + "default", + "delete", + "do", + "else", + "finally", + "for", + "function", + "if", + "in", + "instanceof", + "typeof", + "new", + "var", + "return", + "void", + "switch", + "while", + "this", + "with", + "throw", + "try", + + // Future reserved words. + "class", + "enum", + "extends", + "super", + "const", + "export", + "import", + + // Strict Mode future reserved words. + "implements", + "let", + "private", + "public", + "interface", + "package", + "protected", + "static", + "yield", + + // Literals. + "null", + "true", + "false", + + // Not formally reserved, but potentially confusing or with special rules. + "undefined", + "eval", + "arguments" + ]; + if (reservedWords.indexOf(string) != -1) return false; + + return true; +} + + // **** Load-time set-up // Make console globally visible as debugConsole