diff --git a/common/content/dactyl.js b/common/content/dactyl.js index c3eee956..763e07d5 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -402,7 +402,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (info && info.file[0] !== "[") ({ file: fileName, line: lineNumber, context: ctxt }) = info; - if (!context) + if (!context && fileName && fileName[0] !== "[") context = _userContext || ctxt; if (isinstance(context, ["Sandbox"])) diff --git a/common/locale/en-US/eval.xml b/common/locale/en-US/eval.xml index 40b059bc..a29e8f62 100644 --- a/common/locale/en-US/eval.xml +++ b/common/locale/en-US/eval.xml @@ -90,7 +90,6 @@ :js :javas :javascript :javascript cmd :javascript <<endpattern\ncmd\nendpattern - :javascript!

Evaluates the given cmd as JavaScript. Behaves exactly as @@ -137,6 +136,32 @@ + + REPL + :javascript! context + +

+ Starts the JavaScript Read Eval Print Loop, where JavaScript + statements are entered and evaluated, their results printed, and the + input modified and entered again. Within the REPL, the results of a + given evaluation are available as variables named for the given + prompt. +

+ +

+ If context is given, then statements are executed in that + global context. +

+ + +js1> ({ foo: bar })[object Object]:: +foo: "bar"js2> js1.foobar +
+ +

Global Variables

diff --git a/common/modules/javascript.jsm b/common/modules/javascript.jsm index e8318182..b6abdb4d 100644 --- a/common/modules/javascript.jsm +++ b/common/modules/javascript.jsm @@ -12,6 +12,8 @@ defineModule("javascript", { use: ["services", "template", "util"] }, this); +let isPrototypeOf = Object.prototype.isPrototypeOf; + // TODO: Clean this up. var JavaScript = Module("javascript", { @@ -40,6 +42,14 @@ var JavaScript = Module("javascript", { }, }), + globals: Class.memoize(function () [ + [this.modules.userContext, "Global Variables"], + [this.modules, "modules"], + [this.window, "window"] + ]), + + toplevel: Class.memoize(function () this.modules.jsmodules), + lazyInit: true, newContext: function () this.modules.newContext(this.modules.userContext), @@ -83,7 +93,7 @@ var JavaScript = Module("javascript", { return []; if (isinstance(obj, ["Sandbox"]) && !toplevel) // Temporary hack. return []; - if (this.modules.jsmodules.isPrototypeOf(obj) && !toplevel) + if (isPrototypeOf.call(this.toplevel, obj) && !toplevel) return []; let completions = [k for (k in this.iter(obj, toplevel))]; @@ -310,11 +320,7 @@ var JavaScript = Module("javascript", { let end = (frame == -1 ? this._lastIdx : this._get(frame + 1).offset); this._cacheKey = null; - let obj = [[this.cache.evalContext, "Local Variables"], - [this.replContext, "REPL Variables"], - [this.modules.userContext, "Global Variables"], - [this.modules, "modules"], - [this.window, "window"]]; // Default objects; + let obj = [[this.cache.evalContext, "Local Variables"]].concat(this.globals); // Is this an object dereference? if (dot < statement) // No. dot = statement - 1; @@ -751,15 +757,34 @@ var JavaScript = Module("javascript", { }); modules.CommandREPLMode = Class("CommandREPLMode", modules.CommandMode, { - open: function open() { - let self = this; + init: function init(context) { + init.supercall(this); - this.context = modules.newContext(modules.userContext, true); + let self = this; + let sandbox = isinstance(context, ["Sandbox"]); + + this.context = modules.newContext(context, !sandbox); this.js = modules.JavaScript(); this.js.replContext = this.context; - this.js.newContext = function newContext() modules.newContext(self.context, true); - this.repl = REPL(this.context); + this.js.newContext = function newContext() modules.newContext(self.context, !sandbox); + this.js.globals = [ + [this.context, "REPL Variables"], + [context, "REPL Global"] + ].concat(this.js.globals.filter(function ([global]) isPrototypeOf.call(global, context))); + + if (!isPrototypeOf.call(modules.jsmodules, context)) + this.js.toplevel = context; + + if (!isPrototypeOf.call(window, context)) + this.js.window = context; + + if (this.js.globals.slice(2).some(function ([global]) global === context)) + this.js.globals.splice(1); + + this.repl = REPL(this.context); + }, + open: function open(context) { this.updatePrompt(); modules.mow.echo(this.repl); @@ -803,14 +828,14 @@ var JavaScript = Module("javascript", { commands.add(["javas[cript]", "js"], "Evaluate a JavaScript string", function (args) { - if (args.bang) // open JavaScript console - dactyl.open("chrome://global/content/console.xul", - { from: "javascript" }); - else if (args[0]) + modules.commandline; + + if (args[0] && !args.bang) dactyl.userEval(args[0]); else { modules.commandline; - modules.CommandREPLMode().open(); + modules.CommandREPLMode(args[0] ? dactyl.userEval(args[0]) : modules.userContext) + .open(); } }, { bang: true, diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS index b5bfedd0..e8d2ca69 100644 --- a/pentadactyl/NEWS +++ b/pentadactyl/NEWS @@ -86,6 +86,7 @@ and linking to source code locations). [b4] - :downloads now opens a download list in the multi-line output buffer. [b6] + - :javascript! now opens a Read Eval Print Loop. [b6] - Added -arg flag to :map. [b6] - :extensions has been replaced with a more powerful :addons. - Added -literal flag to :command. [b6]