/*
* This Source Code is subject to the terms of the Mozilla Public License
* version 2.0 (the "License"). You can obtain a copy of the License at
* http://mozilla.org/MPL/2.0/.
*/
/*
* This is similar to the [JavaScript Shell]()
* (or as available in [Komodo Developer
* Extension](http://community.activestate.com/xpi/komodo-developer-extension))
* or the [JavaScript Environment]()
* bookmarklets, but lets you use the power of the Komodo editor to modify the
* code.
*
* Additionally, line numbers should work for both errors and print statements
* (and are integrated into the command output tab).
*
* There are three special global functions in the context: print, props and
* clear:
* print(aString)
* Displays aString with the line number of the print statement.
* props(aObject)
* Displays the properties of an object and the inheritance chain.
* clear()
* Removes all statements printed to the command window.
*
* Author: Patrick Cloke
* URL: https://bitbucket.org/clokep/komodo-tools
* Version: 0.1
* Last Updated: 2012-05-07 0834
*/
let Ci = Components.interfaces;
let scimoz = ko.views.manager.currentView.scimoz;
// Get the current document we're running on.
let currentDoc = ko.views.manager.currentView.koDoc;
let output = ko.run.output;
function start() {
// The output should be of the form: ": ".
const outputParser = "^(?P\\d+): (?P.*)$";
// Open the output session with some nice names, clearing the content window
// on open.
let filename = currentDoc.baseName;
let file = currentDoc.displayPath.file;
let path = file ? file.dirName : "";
output.startSession(filename, true, outputParser, path, filename, true);
}
start();
// Ensure the tab is showing.
output.show();
// Get the output terminal to use.
let term = output.getTerminal().QueryInterface(Ci.koITreeOutputHandler);
// Define some nice functions to use when debugging, note that these are an
// attempt to emulate the JavaScript Shell.
let hasPrinted = false;
function print(aString) {
let lineNumber = Components.stack.caller.lineNumber + offset;
let string = (hasPrinted ? "\n" : "") + lineNumber + ": " + aString;
term.proxyAddText(string.length, string, "");
hasPrinted = true;
}
function clear() {
// Close the current session and start a new one, term.clear() doesn't seem to
// work.
term.endSession(0);
start();
}
/*
* This function is based on the JavaScript Shell bookmarklet:
* [http://www.squarefree.com/shell/]. Available as MPl 1.1/GPL/LGPL
* tri-license.
*/
function props(aObject) {
if (aObject === null) {
error("props called with null argument");
return;
}
if (aObject === undefined) {
error("props called with undefined argument");
return;
}
let output = "";
// The output object.
let as = {"Methods": [], "Fields": [], "Unreachables": []};
let p, j, i; // loop variables, several used multiple times
// For each __proto__, add an empty array for each namespace.
let protoLevels = 0;
for (p = aObject; p; p = p.__proto__) {
//for (i = 0; i < ns.length; ++i)
for (i in as)
as[i][protoLevels] = [];
++protoLevels;
}
// Iterate over each property of an object.
for (let a in aObject) {
// Shortcoming: doesn't check that VALUES are the same in object and
// prototype.
// Find the level that this property comes from.
let protoLevel = -1;
try {
for (p = aObject; p && (a in p); p = p.__proto__)
++protoLevel;
} catch(er) {
// "in" operator throws when param to props() is a string
protoLevel = 0;
}
let type = "Fields";
try {
if ((typeof aObject[a]) == "function")
type = "Methods";
} catch (er) {
type = "Unreachables";
}
// Add the current property as the correct type to it's __proto__ level.
as[type][protoLevel].push(a);
}
function times(s, n) (n ? s + times(s, n-1) : "");
// For each prototype level, print out each type of property that exists.
for (j = 0; j < protoLevels; ++j) {
for (i in as) {
if (as[i][j].length) {
output += i + times(" of prototype", j) + ": " +
as[i][j].sort().join(", ") + "\n";
}
}
}
// Display the output.
print(output.slice(0, -1));
}
// Get the selected text from the current document, if no text is selected, then
// use the whole document. If text is selected, account for this offset in line
// numbers.
let text = scimoz.selText;
let offset = 1;
if (!text.length)
text = scimoz.text;
else
offset += scimoz.lineFromPosition(Math.min(scimoz.anchor, scimoz.currentPos));
let exitState = 0;
try {
// Evaluate the document in an empty sandbox (except for the functions we want
// added!).
let sandbox = Components.utils.Sandbox("about:blank");
sandbox.print = print;
sandbox.clear = clear;
sandbox.props = props;
// Evaluate in the sandbox, using JavaScript version 1.8, the current path
// name and consider the first line to be the offset.
// TODO automatically get the highest JavaScript version available.
Components.utils.evalInSandbox(text, sandbox, "1.8", currentDoc.displayPath,
offset);
} catch (e) {
let string = (hasPrinted ? "\n" : "") + e.lineNumber + ": " + e.toString();
term.proxyAddText(string.length, string, "");
hasPrinted = true;
exitState = 1;
}
output.endSession(exitState);