summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/content/abbreviations.js2
-rw-r--r--common/content/autocommands.js4
-rw-r--r--common/content/buffer.js43
-rw-r--r--common/content/commandline.js21
-rw-r--r--common/content/commands.js222
-rw-r--r--common/content/contexts.js318
-rw-r--r--common/content/dactyl.js16
-rw-r--r--common/content/mappings.js136
-rw-r--r--common/content/mow.js8
-rw-r--r--common/content/options.js2
-rw-r--r--common/modules/addons.jsm4
-rw-r--r--common/modules/base.jsm14
-rw-r--r--common/modules/io.jsm11
-rw-r--r--common/modules/overlay.jsm6
-rw-r--r--common/modules/storage.jsm2
-rw-r--r--common/modules/util.jsm11
16 files changed, 472 insertions, 348 deletions
diff --git a/common/content/abbreviations.js b/common/content/abbreviations.js
index c87c3bf9..030adc90 100644
--- a/common/content/abbreviations.js
+++ b/common/content/abbreviations.js
@@ -241,7 +241,7 @@ var Abbreviations = Module("abbreviations", {
abbreviations.list(modes, lhs || "");
else {
if (args["-javascript"])
- rhs = Command.bindMacro({ literalArg: rhs }, "-javascript", ["editor"]);
+ rhs = contexts.bindMacro({ literalArg: rhs }, "-javascript", ["editor"]);
abbreviations.add(modes, lhs, rhs);
}
}, {
diff --git a/common/content/autocommands.js b/common/content/autocommands.js
index a60b64a5..ed962466 100644
--- a/common/content/autocommands.js
+++ b/common/content/autocommands.js
@@ -156,7 +156,7 @@ var AutoCommands = Module("autocommands", {
if (args.length > 2) { // add new command, possibly removing all others with the same event/pattern
if (args.bang)
autocommands.remove(event, regexp);
- cmd = Command.bindMacro(args, "-ex", function (params) params);
+ cmd = contexts.bindMacro(args, "-ex", function (params) params);
autocommands.add(events, regexp, cmd);
}
else {
@@ -245,7 +245,7 @@ var AutoCommands = Module("autocommands", {
};
},
javascript: function () {
- JavaScript.setCompleter(this.get, [function () Iterator(config.autocommands)]);
+ JavaScript.setCompleter(autocommands.get, [function () Iterator(config.autocommands)]);
},
options: function () {
options.add(["eventignore", "ei"],
diff --git a/common/content/buffer.js b/common/content/buffer.js
index 62d5a69e..5388e226 100644
--- a/common/content/buffer.js
+++ b/common/content/buffer.js
@@ -262,7 +262,7 @@ var Buffer = Module("buffer", {
dactylLoadCount: 0,
// XXX: function may later be needed to detect a canceled synchronous openURL()
- onStateChange: function onStateChange(webProgress, request, flags, status) {
+ onStateChange: util.wrapCallback(function onStateChange(webProgress, request, flags, status) {
onStateChange.superapply(this, arguments);
// STATE_IS_DOCUMENT | STATE_IS_WINDOW is important, because we also
// receive statechange events for loading images and other parts of the web page
@@ -286,9 +286,9 @@ var Buffer = Module("buffer", {
statusline.updateUrl();
}
}
- },
+ }),
// for notifying the user about secure web pages
- onSecurityChange: function onSecurityChange(webProgress, request, state) {
+ onSecurityChange: util.wrapCallback(function onSecurityChange(webProgress, request, state) {
onSecurityChange.superapply(this, arguments);
if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN)
statusline.security = "broken";
@@ -300,22 +300,27 @@ var Buffer = Module("buffer", {
statusline.security = "insecure";
if (webProgress && webProgress.DOMWindow)
webProgress.DOMWindow.document.dactylSecurity = statusline.security;
- },
- onStatusChange: function onStatusChange(webProgress, request, status, message) {
+ }),
+ onStatusChange: util.wrapCallback(function onStatusChange(webProgress, request, status, message) {
onStatusChange.superapply(this, arguments);
statusline.updateUrl(message);
- },
- onProgressChange: function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
- onProgressChange.superapply(this, arguments);
- if (webProgress.DOMWindow)
- webProgress.DOMWindow.dactylProgress = curTotalProgress / maxTotalProgress;
- statusline.progress = curTotalProgress / maxTotalProgress;
- },
+ }),
+ onProgressChange: util.wrapCallback(function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
+ try {
+ onProgressChange.superapply(this, arguments);
+ if (webProgress && webProgress.DOMWindow)
+ webProgress.DOMWindow.dactylProgress = curTotalProgress / maxTotalProgress;
+ statusline.progress = curTotalProgress / maxTotalProgress;
+ }
+ catch (e) {
+ util.reportError(e);
+ }
+ }),
// happens when the users switches tabs
- onLocationChange: function onLocationChange(webProgress, request, uri) {
+ onLocationChange: util.wrapCallback(function onLocationChange(webProgress, request, uri) {
onLocationChange.superapply(this, arguments);
- delete mappings.hives;
+ delete contexts.groups;
statusline.updateUrl();
statusline.progress = "";
@@ -352,13 +357,13 @@ var Buffer = Module("buffer", {
if (loaded.commandline)
commandline.clear();
}, 500);
- },
+ }),
// called at the very end of a page load
- asyncUpdateUI: function asyncUpdateUI() {
+ asyncUpdateUI: util.wrapCallback(function asyncUpdateUI() {
asyncUpdateUI.superapply(this, arguments);
util.timeout(function () { statusline.updateUrl(); }, 100);
- },
- setOverLink: function setOverLink(link, b) {
+ }),
+ setOverLink: util.wrapCallback(function setOverLink(link, b) {
setOverLink.superapply(this, arguments);
switch (options["showstatuslinks"]) {
case "status":
@@ -371,7 +376,7 @@ var Buffer = Module("buffer", {
commandline.clear();
break;
}
- },
+ }),
},
/**
diff --git a/common/content/commandline.js b/common/content/commandline.js
index 4dd50eee..0e052bf4 100644
--- a/common/content/commandline.js
+++ b/common/content/commandline.js
@@ -352,8 +352,8 @@ var CommandMode = Class("CommandMode", {
if (this.history)
this.history.save();
- commandline.hideCompletions();
this.resetCompletions();
+ commandline.hideCompletions();
modes.delay(function () {
if (!this.keepCommand || commandline.silent || commandline.quiet)
@@ -423,11 +423,13 @@ var CommandExMode = Class("CommandExMode", CommandMode, {
},
onSubmit: function onSubmit(command) {
- io.withSavedValues(["readHeredoc", "sourcing"], function () {
- this.sourcing = { file: "[Command Line]", line: 1 };
+ io.withSavedValues(["readHeredoc"], function () {
this.readHeredoc = commandline.readHeredoc;
- commands.repeat = command;
- dactyl.execute(command);
+ contexts.withSavedValues(["context"], function () {
+ this.context = { file: "[Command Line]", line: 1 };
+ commands.repeat = command;
+ dactyl.execute(command);
+ });
});
}
});
@@ -504,11 +506,6 @@ var CommandLine = Module("commandline", {
}, message));
}
}; //}}}
-
- this._silent = false;
- this._quiet = false;
- this._lastEcho = null;
-
},
/**
@@ -546,12 +543,14 @@ var CommandLine = Module("commandline", {
get completionContext() this._completions.context,
+ _silent: false,
get silent() this._silent,
set silent(val) {
this._silent = val;
this.quiet = this.quiet;
},
+ _quite: false,
get quiet() this._quiet,
set quiet(val) {
this._quiet = val;
@@ -665,6 +664,8 @@ var CommandLine = Module("commandline", {
}
},
+ _lastEcho: null,
+
/**
* Output the given string onto the command line. With no flags, the
* message will be shown in the status line if it's short enough to
diff --git a/common/content/commands.js b/common/content/commands.js
index 131589e0..3c1a66fb 100644
--- a/common/content/commands.js
+++ b/common/content/commands.js
@@ -139,8 +139,9 @@ var Command = Class("Command", {
* @param {Object} modifiers Any modifiers to be passed to {@link #action}.
*/
execute: function (args, modifiers) {
- if (this.deprecated && !set.add(this.complained, io.sourcing ? io.sourcing.file : "[Command Line]")) {
- let loc = io.sourcing ? io.sourcing.file + ":" + io.sourcing.line + ": " : "";
+ let context = args.context;
+ if (this.deprecated && !set.add(this.complained, context ? context.file : "[Command Line]")) {
+ let loc = contexts.context ? context.file + ":" + context.line + ": " : "";
dactyl.echoerr(loc + ":" + this.name + " is deprecated" +
(isString(this.deprecated) ? ": " + this.deprecated : ""));
}
@@ -155,7 +156,7 @@ var Command = Class("Command", {
return !dactyl.trapErrors(function exec(command) {
if (this.always)
this.always(args, modifiers);
- if (!io.sourcing || !io.sourcing.noExecute)
+ if (!contexts.context || !contexts.context.noExecute)
this.action(args, modifiers);
}, this);
},
@@ -271,9 +272,15 @@ var Command = Class("Command", {
.toObject(),
{
__iterator__: function () array.iterItems(this),
+
command: this,
+
+ get context() contexts.context,
+
explicitOpts: Class.memoize(function () ({})),
+
get literalArg() this.command.literal != null && this[this.command.literal] || "",
+
// TODO: string: Class.memoize(function () { ... }),
verify: function verify() {
if (this.command.argCount) {
@@ -321,51 +328,6 @@ var Command = Class("Command", {
*/
replacementText: null
}, {
- bindMacro: function (args, default_, params) {
- let process = util.identity;
-
- if (callable(params))
- var makeParams = function makeParams(self, args)
- iter.toObject([k, process(v)]
- for ([k, v] in iter(params.apply(self, args))));
- else if (params)
- makeParams = function makeParams(self, args)
- iter.toObject([name, process(args[i])]
- for ([i, name] in Iterator(params)));
-
- let rhs = args.literalArg;
- let type = ["-builtin", "-ex", "-javascript", "-keys"].reduce(function (a, b) args[b] ? b : a, default_);
- switch (type) {
- case "-builtin":
- let noremap = true;
- /* fallthrough */
- case "-keys":
- let silent = args["-silent"];
- rhs = events.canonicalKeys(rhs, true);
- var action = function action() events.feedkeys(action.macro(makeParams(this, arguments)),
- noremap, silent);
- action.macro = util.compileMacro(rhs, true);
- break;
- case "-ex":
- action = function action() commands.execute(action.macro, makeParams(this, arguments),
- false, null, action.sourcing);
- action.macro = util.compileMacro(rhs, true);
- action.sourcing = io.sourcing && update({}, io.sourcing);
- break;
- case "-javascript":
- if (callable(params))
- action = dactyl.userEval("(function action() { with (action.makeParams(this, arguments)) {" + args.literalArg + "} })");
- else
- action = dactyl.userFunc.apply(dactyl, params.concat(args.literalArg).array);
- process = function (param) isObject(param) && param.valueOf ? param.valueOf() : param;
- action.makeParams = makeParams;
- break;
- }
- action.toString = function toString() (type === default_ ? "" : type + " ") + rhs;
- args = null;
- return action;
- },
-
// TODO: do we really need more than longNames as a convenience anyway?
/**
* Converts command name abbreviation specs of the form
@@ -576,60 +538,62 @@ var Commands = Module("commands", {
* interpolated into the command string.
* @param {object} args Optional arguments object to be passed to
* command actions.
- * @param {object} sourcing An object containing information about
+ * @param {object} context An object containing information about
* the file that is being or has been sourced to obtain the
* command string.
*/
- execute: function (string, tokens, silent, args, sourcing) {
- io.withSavedValues(["readHeredoc", "sourcing"], function () {
- sourcing = sourcing || this.sourcing || { file: "[Command Line]", line: 1 };
- this.sourcing = update({}, sourcing);
-
- args = update({}, args || {});
-
- if (tokens && !callable(string))
- string = util.compileMacro(string, true);
- if (callable(string))
- string = string(tokens || {});
-
- let lines = string.split(/\r\n|[\r\n]/);
-
- this.readHeredoc = function (end) {
- let res = [];
- this.sourcing.line++;
- while (++i < lines.length) {
- if (lines[i] === end)
- return res.join("\n");
- res.push(lines[i]);
- }
- dactyl.assert(false, "Unexpected end of file waiting for " + end);
- };
+ execute: function (string, tokens, silent, args, context) {
+ contexts.withSavedValues(["context"], function () {
+ context = update({}, context || this.context || { file: "[Command Line]", line: 1 });
+ this.context = context;
+
+ io.withSavedValues(["readHeredoc"], function () {
+ this.readHeredoc = function (end) {
+ let res = [];
+ contexts.context.line++;
+ while (++i < lines.length) {
+ if (lines[i] === end)
+ return res.join("\n");
+ res.push(lines[i]);
+ }
+ dactyl.assert(false, "Unexpected end of file waiting for " + end);
+ };
- for (var i = 0; i < lines.length && !this.sourcing.finished; i++) {
- // Deal with editors from Silly OSs.
- let line = lines[i].replace(/\r$/, "");
+ args = update({}, args || {});
- this.sourcing.line = sourcing.line + i;
+ if (tokens && !callable(string))
+ string = util.compileMacro(string, true);
+ if (callable(string))
+ string = string(tokens || {});
- // Process escaped new lines
- while (i < lines.length && /^\s*\\/.test(lines[i + 1]))
- line += "\n" + lines[++i].replace(/^\s*\\/, "");
+ let lines = string.split(/\r\n|[\r\n]/);
- try {
- dactyl.execute(line, args);
- }
- catch (e) {
- if (!silent || silent === "loud") {
- if (silent !== "loud")
- e.message = this.sourcing.file + ":" + this.sourcing.line + ": " + e.message;
- else {
- dactyl.echoerr("Error detected while processing " + this.sourcing.file);
- dactyl.echomsg("line\t" + this.sourcing.line + ":");
+ for (var i = 0; i < lines.length && !context.finished; i++) {
+ // Deal with editors from Silly OSs.
+ let line = lines[i].replace(/\r$/, "");
+
+ context.line = context.line + i;
+
+ // Process escaped new lines
+ while (i < lines.length && /^\s*\\/.test(lines[i + 1]))
+ line += "\n" + lines[++i].replace(/^\s*\\/, "");
+
+ try {
+ dactyl.execute(line, args);
+ }
+ catch (e) {
+ if (!silent || silent === "loud") {
+ if (silent !== "loud")
+ e.message = context.file + ":" + context.line + ": " + e.message;
+ else {
+ dactyl.echoerr("Error detected while processing " + context.file);
+ dactyl.echomsg("line\t" + context.line + ":");
+ }
+ dactyl.reportError(e, true);
}
- dactyl.reportError(e, true);
}
}
- }
+ });
});
},
@@ -1172,12 +1136,12 @@ var Commands = Module("commands", {
* @param {nsIStackFrame} frame
*/
getCaller: function (frame) {
- if (io.sourcing)
+ if (contexts.context)
return {
__proto__: frame,
- filename: io.sourcing.file[0] == "[" ? io.sourcing.file :
- services.io.newFileURI(File(io.sourcing.file)).spec,
- lineNumber: io.sourcing.line
+ filename: contexts.context.file[0] == "[" ? contexts.context.file :
+ services.io.newFileURI(File(contexts.context.file)).spec,
+ lineNumber: contexts.context.line
};
return frame;
},
@@ -1302,11 +1266,11 @@ var Commands = Module("commands", {
if (/^custom,/.test(completer)) {
completer = completer.substr(7);
- let sourcing = update({}, io.sourcing || {});
+ let context = update({}, contexts.context || {});
completerFunc = function (context) {
try {
- var result = io.withSavedValues(["sourcing"], function () {
- io.sourcing = sourcing;
+ var result = contextswithSavedValues(["context"], function () {
+ contexts.context = context;
return dactyl.userEval(completer);
});
}
@@ -1328,7 +1292,7 @@ var Commands = Module("commands", {
let added = commands.addUserCommand(cmd.split(","),
args["-description"],
- Command.bindMacro(args, "-ex",
+ contexts.bindMacro(args, "-ex",
function makeParams(args, modifiers) ({
args: {
__proto__: args,
@@ -1345,7 +1309,7 @@ var Commands = Module("commands", {
literal: args["-literal"],
persist: !args["-nopersist"],
replacementText: args.literalArg,
- sourcing: io.sourcing && update({}, io.sourcing)
+ context: contexts.context && update({}, contexts.context)
}, args.bang);
if (!added)
@@ -1484,62 +1448,6 @@ var Commands = Module("commands", {
}
});
- function checkStack(cmd) {
- util.assert(io.sourcing && io.sourcing.stack &&
- io.sourcing.stack[cmd] && io.sourcing.stack[cmd].length,
- "Invalid use of conditional");
- }
- function pop(cmd) {
- checkStack(cmd);
- return io.sourcing.stack[cmd].pop();
- }
- function push(cmd, value) {
- util.assert(io.sourcing, "Invalid use of conditional");
- if (arguments.length < 2)
- value = io.sourcing.noExecute;
- io.sourcing.stack = io.sourcing.stack || {};
- io.sourcing.stack[cmd] = (io.sourcing.stack[cmd] || []).concat([value]);
- }
-
- commands.add(["if"],
- "Execute commands until the next :elseif, :else, or :endif only if the argument returns true",
- function (args) { io.sourcing.noExecute = !dactyl.userEval(args[0]); },
- {
- always: function (args) { push("if"); },
- argCount: "1",
- literal: 0
- });
- commands.add(["elsei[f]", "elif"],
- "Execute commands until the next :elseif, :else, or :endif only if the argument returns true",
- function (args) {},
- {
- always: function (args) {
- checkStack("if");
- io.sourcing.noExecute = io.sourcing.stack.if.slice(-1)[0] ||
- !io.sourcing.noExecute || !dactyl.userEval(args[0]);
- },
- argCount: "1",
- literal: 0
- });
- commands.add(["el[se]"],
- "Execute commands until the next :endif only if the previous conditionals were not executed",
- function (args) {},
- {
- always: function (args) {
- checkStack("if");
- io.sourcing.noExecute = io.sourcing.stack.if.slice(-1)[0] ||
- !io.sourcing.noExecute;
- },
- argCount: "0"
- });
- commands.add(["en[dif]", "fi"],
- "End a string of :if/:elseif/:else conditionals",
- function (args) {},
- {
- always: function (args) { io.sourcing.noExecute = pop("if"); },
- argCount: "0"
- });
-
commands.add(["y[ank]"],
"Yank the output of the given command to the clipboard",
function (args) {
diff --git a/common/content/contexts.js b/common/content/contexts.js
new file mode 100644
index 00000000..2b81fd02
--- /dev/null
+++ b/common/content/contexts.js
@@ -0,0 +1,318 @@
+// Copyright (c) 2010-2011 by Kris Maglione <maglione.k@gmail.com>
+//
+// This work is licensed for reuse under an MIT license. Details are
+// given in the LICENSE.txt file included with this file.
+"use strict";
+
+var Group = Class("Group", {
+ init: function init(name, description, filter, persist) {
+ const self = this;
+ this.name = name;
+ this.description = description;
+ this.filter = filter || function (uri) true;
+ this.persist = persist || false;
+
+ this.subGroups = { __proto__: this.subGroups, owner: this };
+ this.subGroups.instance = this.subGroups;
+ },
+
+ get toStringParams() [this.name],
+
+ get builtin() dactyl.builtinGroups.indexOf(this) >= 0,
+
+ subGroups: {}
+
+}, {
+ subGroup: {},
+
+ subGroups: {},
+
+ SubGroup: Class("SubGroup", Class.Property, {
+ init: function init(name, constructor) {
+ const self = this;
+ if (this.Group)
+ return {
+ enumerable: true,
+
+ get: function () array(contexts.groups[self.name])
+ };
+
+
+ this.Group = constructor;
+ this.name = name;
+ memoize(Group.prototype.subGroups, name,
+ function () constructor(this.owner.name, this.owner.description,
+ this.owner.filter, this.owner.persist));
+
+ memoize(Group.subGroup, name,
+ function () Object.create({ _subGroup: name, __proto__: contexts.subGroupProto }));
+
+ memoize(Group.subGroups, name,
+ function () [g.subGroups[name] for (g in values(this.groups)) if (set.has(g.subGroups, name))]);
+ }
+ })
+});
+
+var Contexts = Module("contexts", {
+ init: function () {
+ this.groupList = [];
+ this.groupMap = {};
+ this.subGroupProto = {};
+ this.builtinGroups = [this.addGroup("builtin", "Builtin items"),
+ this.addGroup("user", "User-defined items", null, true)];
+ },
+
+ context: null,
+
+ groups: Class.memoize(function () ({
+ __proto__: Group.subGroups,
+ groups: this.groupList.filter(function (g) g.filter(buffer.uri))
+ })),
+
+ allGroups: Class.memoize(function () ({
+ __proto__: Group.subGroups,
+ groups: this.groupList
+ })),
+
+ get subGroup() Group.subGroup,
+
+ addGroup: function addGroup(name, description, filter, persist) {
+ this.removeGroup(name);
+
+ let group = Group(name, description, filter, persist);
+ this.groupList.unshift(group);
+ this.groupMap[name] = group;
+ this.subGroupProto.__defineGetter__(name, function () group.subGroups[this._subGroup]);
+ return group;
+ },
+
+ removeGroup: function removeGroup(name, filter) {
+ let group = this.getGroup(name);
+ dactyl.assert(!group || !group.builtin, "Cannot remove builtin group");
+
+ if (group)
+ this.groupList.splice(this.groupList.indexOf(group), 1);
+
+ if (this.context && this.context.group === group)
+ this.context.group = null;
+
+ delete this.groupMap[name];
+ delete this.subGroupProto[name];
+ delete this.groups;
+ return group;
+ },
+
+ getGroup: function getGroup(name, subGroup) {
+ let group = array.nth(this.groupList, function (h) h.name == name, 0) || null;
+ if (group && subGroup)
+ return group.subGroups[subGroup];
+ return group;
+ },
+
+ bindMacro: function (args, default_, params) {
+ let process = util.identity;
+
+ if (callable(params))
+ var makeParams = function makeParams(self, args)
+ iter.toObject([k, process(v)]
+ for ([k, v] in iter(params.apply(self, args))));
+ else if (params)
+ makeParams = function makeParams(self, args)
+ iter.toObject([name, process(args[i])]
+ for ([i, name] in Iterator(params)));
+
+ let rhs = args.literalArg;
+ let type = ["-builtin", "-ex", "-javascript", "-keys"].reduce(function (a, b) args[b] ? b : a, default_);
+ switch (type) {
+ case "-builtin":
+ let noremap = true;
+ /* fallthrough */
+ case "-keys":
+ let silent = args["-silent"];
+ rhs = events.canonicalKeys(rhs, true);
+ var action = function action() events.feedkeys(action.macro(makeParams(this, arguments)),
+ noremap, silent);
+ action.macro = util.compileMacro(rhs, true);
+ break;
+ case "-ex":
+ action = function action() commands.execute(action.macro, makeParams(this, arguments),
+ false, null, action.context);
+ action.macro = util.compileMacro(rhs, true);
+ action.context = this.context && update({}, this.context);
+ break;
+ case "-javascript":
+ if (callable(params))
+ action = dactyl.userEval("(function action() { with (action.makeParams(this, arguments)) {" + args.literalArg + "} })");
+ else
+ action = dactyl.userFunc.apply(dactyl, params.concat(args.literalArg).array);
+ process = function (param) isObject(param) && param.valueOf ? param.valueOf() : param;
+ action.makeParams = makeParams;
+ break;
+ }
+ action.toString = function toString() (type === default_ ? "" : type + " ") + rhs;
+ args = null;
+ return action;
+ }
+}, {
+}, {
+ commands: function initCommands() {
+
+ commands.add(["gr[oup]", "mapg[roup]"],
+ "Create or select a group",
+ function (args) {
+ dactyl.assert(args.length <= 2, "Trailing characters");
+
+ if (args.length == 0)
+ return void completion.listCompleter("group", "");
+
+ let name = Option.dequote(args[0]);
+ dactyl.assert(commands.validName.test(name), "Invalid group name");
+
+ let group = contexts.getGroup(name);
+
+ if (args.length == 2) {
+ dactyl.assert(!group || args.bang, "Group exists");
+
+ let filter = function siteFilter(uri)
+ siteFilter.filters.every(function (f) f(uri) == f.result);
+
+ update(filter, {
+ toString: function () this.filters.join(","),
+ filters: Option.splitList(args[1], true).map(function (pattern) {
+ let [, res, filter] = /^(!?)(.*)/.exec(pattern);
+
+ return update(Styles.matchFilter(Option.dequote(filter)), {
+ result: !res,
+ toString: function () pattern
+ });
+ })
+ });
+
+ group = contexts.addGroup(name, args["-description"], filter, !args["-nopersist"]);
+ }
+
+ dactyl.assert(group, "No such group: " + name);
+ dactyl.assert(group.name != "builtin", "Cannot modify builtin group");
+ if (args.context)
+ args.context.group = group;
+ },
+ {
+ argCount: "*",
+ bang: true,
+ completer: function (context, args) {
+ if (args.length == 1)
+ completion.group(context);
+ else {
+ Option.splitList(context.filter);
+ context.advance(Option._splitAt);
+
+ context.compare = CompletionContext.Sort.unsorted;
+ context.completions = [
+ [buffer.uri.host, "Current Host"],
+ [buffer.uri.spec, "Current Page"]
+ ];
+ }
+ },
+ keepQuotes: true,
+ options: [
+ {
+ names: ["-description", "-desc", "-d"],
+ description: "A description of this group",
+ type: CommandOption.STRING
+ },
+ {
+ names: ["-nopersist", "-n"],
+ description: "Do not save this group to an auto-generated RC file"
+ }
+ ]
+ });
+
+ commands.add(["delg[roup]", "delmapg[roup]"],
+ "Delete a group",
+ function (args) {
+ dactyl.assert(contexts.getGroup(args[0]), "No such group: " + args[0]);
+ contexts.removeGroup(args[0]);
+ },
+ {
+ argCount: "1",
+ completer: function (context, args) {
+ completion.group(context);
+ context.filters.push(function ({ item }) !item.builtin);
+ }
+ });
+
+
+ commands.add(["fini[sh]"],
+ "Stop sourcing a script file",
+ function (args) {
+ dactyl.assert(args.context, "E168: :finish used outside of a sourced file");
+ args.context.finished = true;
+ },
+ { argCount: "0" });
+
+
+ function checkStack(cmd) {
+ util.assert(contexts.context && contexts.context.stack &&
+ contexts.context.stack[cmd] && contexts.context.stack[cmd].length,
+ "Invalid use of conditional");
+ }
+ function pop(cmd) {
+ checkStack(cmd);
+ return contexts.context.stack[cmd].pop();
+ }
+ function push(cmd, value) {
+ util.assert(contexts.context, "Invalid use of conditional");
+ if (arguments.length < 2)
+ value = contexts.context.noExecute;
+ contexts.context.stack = contexts.context.stack || {};
+ contexts.context.stack[cmd] = (contexts.context.stack[cmd] || []).concat([value]);
+ }
+
+ commands.add(["if"],
+ "Execute commands until the next :elseif, :else, or :endif only if the argument returns true",
+ function (args) { args.context.noExecute = !dactyl.userEval(args[0]); },
+ {
+ always: function (args) { push("if"); },
+ argCount: "1",
+ literal: 0
+ });
+ commands.add(["elsei[f]", "elif"],
+ "Execute commands until the next :elseif, :else, or :endif only if the argument returns true",
+ function (args) {},
+ {
+ always: function (args) {
+ checkStack("if");
+ args.context.noExecute = args.context.stack.if.slice(-1)[0] ||
+ !args.context.noExecute || !dactyl.userEval(args[0]);
+ },
+ argCount: "1",
+ literal: 0
+ });
+ commands.add(["el[se]"],
+ "Execute commands until the next :endif only if the previous conditionals were not executed",
+ function (args) {},
+ {
+ always: function (args) {
+ checkStack("if");
+ args.context.noExecute = args.context.stack.if.slice(-1)[0] ||
+ !args.context.noExecute;
+ },
+ argCount: "0"
+ });
+ commands.add(["en[dif]", "fi"],
+ "End a string of :if/:elseif/:else conditionals",
+ function (args) {},
+ {
+ always: function (args) { args.context.noExecute = pop("if"); },
+ argCount: "0"
+ });
+ },
+ completion: function initCompletion() {
+ completion.group = function group(context, modes) {
+ context.title = ["Group"];
+ context.keys = { text: "name", description: function (h) h.description || h.filter };
+ context.completions = contexts.groupList.slice(0, -1);
+ };
+ }
+});
+
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index 1defb6cf..00d0452d 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -12,7 +12,6 @@ default xml namespace = XHTML;
XML.ignoreWhitespace = false;
XML.prettyPrinting = false;
-var plugins = { __proto__: modules };
var userContext = { __proto__: modules };
var _userContext = newContext(userContext);
@@ -53,7 +52,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
destroy: function () {
autocommands.trigger("LeavePre", {});
- storage.saveAll();
dactyl.triggerObserver("shutdown", null);
util.dump("All dactyl modules destroyed\n");
autocommands.trigger("Leave", {});
@@ -337,7 +335,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
dactyl.reportError(str);
if (typeof str == "object" && "echoerr" in str)
str = str.echoerr;
- else if (isinstance(str, ["Error"]))
+ else if (isinstance(str, ["Error"]) && str.fileName)
str = <>{str.fileName.replace(/^.* -> /, "")}: {str.lineNumber}: {str}</>;
if (options["errorbells"])
@@ -395,9 +393,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
if (jsmodules.__proto__ != window)
str = "with (window) { with (modules) { (this.eval || eval)(" + str.quote() + ") } }";
+ let info = contexts.context;
if (fileName == null)
- if (io.sourcing && io.sourcing.file[0] !== "[")
- ({ file: fileName, line: lineNumber, context: ctxt }) = io.sourcing;
+ if (info && info.file[0] !== "[")
+ ({ file: fileName, line: lineNumber, context: ctxt }) = info;
else try {
if (!context)
context = userContext || ctxt;
@@ -408,8 +407,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
this.loadScript("resource://dactyl-content/eval.js", context);
if (context[EVAL_ERROR]) {
try {
- context[EVAL_ERROR].fileName = io.sourcing.file;
- context[EVAL_ERROR].lineNumber += io.sourcing.line;
+ context[EVAL_ERROR].fileName = context.file;
+ context[EVAL_ERROR].lineNumber += context.line;
}
catch (e) {}
throw context[EVAL_ERROR];
@@ -1356,7 +1355,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
reportError: function reportError(error, echo) {
if (error instanceof FailedAssertion || error.message === "Interrupted") {
- let prefix = io.sourcing ? io.sourcing.file + ":" + io.sourcing.line + ": " : "";
+ let context = contexts.context;
+ let prefix = context ? context.file + ":" + context.line + ": " : "";
if (error.message && error.message.indexOf(prefix) !== 0)
error.message = prefix + error.message;
diff --git a/common/content/mappings.js b/common/content/mappings.js
index dd19bda4..db25c858 100644
--- a/common/content/mappings.js
+++ b/common/content/mappings.js
@@ -107,6 +107,9 @@ var Map = Class("Map", {
.map(function ([i, prop]) [prop, this[i]], arguments)
.toObject();
+ if (!args.context)
+ args.context = contexts.context;
+
let self = this;
function repeat() self.action(args)
if (this.names[0] != ".") // FIXME: Kludge.
@@ -299,16 +302,15 @@ var MapHive = Class("MapHive", {
*/
var Mappings = Module("mappings", {
init: function () {
- this.user = MapHive("user", "User-defined mappings");
- this.builtin = MapHive("builtin", "Builtin mappings");
-
- this.builtinHives = array([this.user, this.builtin]);
- this.allHives = [this.user, this.builtin];
+ this.user = contexts.subGroup.mappings.user;
+ this.builtin = contexts.subGroup.mappings.builtin;
},
repeat: Modes.boundProperty(),
- hives: Class.memoize(function () array(this.allHives.filter(function (h) h.filter(buffer.uri)))),
+ hives: Group.SubGroup("mappings", MapHive),
+
+ get allHives() contexts.allGroups.mappings,
get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
@@ -365,29 +367,6 @@ var Mappings = Module("mappings", {
return map;
},
- addHive: function addHive(name, filter, description) {
- this.removeHive(name);
-
- let hive = MapHive(name, description, filter);
- this.allHives.unshift(hive);
- return hive;
- },
-
- removeHive: function removeHive(name, filter) {
- let hive = this.getHive(name);
- dactyl.assert(!hive || !hive.builtin, "Cannot remove builtin group");
- if (hive)
- this.allHives.splice(this.allHives.indexOf(hive), 1);
-
- if (io.sourcing && io.sourcing.mapHive == hive)
- io.sourcing.mapHive = null;
-
- delete this.hives;
- return hive;
- },
-
- getHive: function getHive(name) array.nth(this.allHives, function (h) h.name == name, 0) || null,
-
/**
* Returns the map from *mode* named *cmd*.
*
@@ -482,7 +461,7 @@ var Mappings = Module("mappings", {
else {
args["-group"].add(mapmodes, [lhs],
args["-description"],
- Command.bindMacro(args, "-keys", function (params) params),
+ contexts.bindMacro(args, "-keys", function (params) params),
{
arg: args["-arg"],
count: args["-count"],
@@ -650,96 +629,12 @@ var Mappings = Module("mappings", {
});
}
- commands.add(["mapg[roup]"],
- "Create or select a mapping group",
- function (args) {
- dactyl.assert(args.length <= 2, "Trailing characters");
-
- if (args.length == 0)
- return void completion.listCompleter("mapGroup", "");
-
- let name = Option.dequote(args[0]);
- let hive = mappings.getHive(name);
-
- if (args.length == 2) {
- dactyl.assert(!hive || args.bang, "Group exists");
-
- let filter = function siteFilter(uri)
- siteFilter.filters.every(function (f) f(uri) == f.result);
-
- update(filter, {
- toString: function () this.filters.join(","),
- filters: Option.splitList(args[1], true).map(function (pattern) {
- let [, res, filter] = /^(!?)(.*)/.exec(pattern);
-
- return update(Styles.matchFilter(Option.dequote(filter)), {
- result: !res,
- toString: function () pattern
- });
- })
- });
-
- hive = mappings.addHive(name, filter, args["-description"]);
- if (args["-nopersist"])
- hive.noPersist = true;
- }
-
- dactyl.assert(hive, "No mapping group: " + name);
- dactyl.assert(hive.name != "builtin", "Can't map to builtin hive");
- if (io.sourcing)
- io.sourcing.mapHive = hive;
- },
- {
- argCount: "*",
- bang: true,
- completer: function (context, args) {
- if (args.length == 1)
- completion.mapGroup(context);
- else {
- Option.splitList(context.filter);
- context.advance(Option._splitAt);
-
- context.compare = CompletionContext.Sort.unsorted;
- context.completions = [
- [buffer.uri.host, "Current Host"],
- [buffer.uri.spec, "Current Page"]
- ];
- }
- },
- keepQuotes: true,
- options: [
- {
- names: ["-description", "-desc", "-d"],
- description: "A description of this mapping group",
- type: CommandOption.STRING
- },
- {
- names: ["-nopersist", "-n"],
- description: "Do not save this mapping group to an auto-generated RC file"
- }
- ]
- });
-
- commands.add(["delmapg[roup]"],
- "Delete a mapping group",
- function (args) {
- dactyl.assert(mappings.getHive(args[0]), "No mapping group: " + args[0]);
- mappings.removeHive(args[0]);
- },
- {
- argCount: "1",
- completer: function (context, args) {
- completion.mapGroup(context);
- context.filters.push(function ({ item }) !item.builtin);
- }
- });
-
let groupFlag = {
names: ["-group", "-g"],
description: "Mapping group to which to add this mapping",
- type: ArgType("map-group", function (group) isString(group) ? mappings.getHive(group) : group),
- get default() io.sourcing && io.sourcing.mapHive || mappings.user,
- completer: function (context) completion.mapGroup(context)
+ type: ArgType("map-group", function (group) isString(group) ? contexts.getGroup(group, "mappings") : group),
+ get default() (contexts.context && contexts.context.group || contexts.user).subGroups.mappings,
+ completer: function (context) completion.group(context)
};
let modeFlag = {
names: ["-mode", "-m"],
@@ -848,11 +743,6 @@ var Mappings = Module("mappings", {
[mode.name.toLowerCase()]);
},
completion: function () {
- completion.mapGroup = function mapGroup(context, modes) {
- context.title = ["Map group"];
- context.keys = { text: "name", description: function (h) h.description || h.filter };
- context.completions = mappings.userHives;
- };
completion.userMapping = function userMapping(context, modes, hive) {
// FIXME: have we decided on a 'standard' way to handle this clash? --djk
hive = hive || mappings.user;
@@ -862,7 +752,7 @@ var Mappings = Module("mappings", {
};
},
javascript: function () {
- JavaScript.setCompleter(this.get,
+ JavaScript.setCompleter(mappings.get,
[
null,
function (context, obj, args) {
diff --git a/common/content/mow.js b/common/content/mow.js
index 1cea5fb5..8a130fbb 100644
--- a/common/content/mow.js
+++ b/common/content/mow.js
@@ -4,6 +4,7 @@
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
+"use strict";
var MOW = Module("mow", {
init: function () {
@@ -71,7 +72,6 @@ var MOW = Module("mow", {
if (!commandline.commandVisible)
commandline.hide();
- this._startHints = false;
if (modes.main != modes.OUTPUT_MULTILINE) {
modes.push(modes.OUTPUT_MULTILINE, null, {
onKeyPress: this.closure.onKeyPress,
@@ -170,6 +170,7 @@ var MOW = Module("mow", {
event.preventDefault();
}
},
+
contextEvents: {
popupshowing: function (event) {
let menu = commandline.widgets.contextMenu;
@@ -261,8 +262,7 @@ var MOW = Module("mow", {
if (!value && elem && elem.contentWindow == document.commandDispatcher.focusedWindow)
document.commandDispatcher.focusedWindow = content;
}
- }),
-
+ })
}, {
}, {
mappings: function () {
@@ -277,7 +277,7 @@ var MOW = Module("mow", {
mow.echo(mow.lastOutput, "Normal");
});
- bind = function bind(keys, description, action, test, default_) {
+ let bind = function bind(keys, description, action, test, default_) {
mappings.add([modes.OUTPUT_MULTILINE],
keys, description,
function (command) {
diff --git a/common/content/options.js b/common/content/options.js
index de3a713c..5ac9218e 100644
--- a/common/content/options.js
+++ b/common/content/options.js
@@ -1295,7 +1295,7 @@ var Options = Module("options", {
};
},
javascript: function () {
- JavaScript.setCompleter(this.get, [function () ([o.name, o.description] for (o in options))]);
+ JavaScript.setCompleter(options.get, [function () ([o.name, o.description] for (o in options))]);
},
sanitizer: function () {
sanitizer.addItem("options", {
diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm
index 9e0d04fb..3824f149 100644
--- a/common/modules/addons.jsm
+++ b/common/modules/addons.jsm
@@ -35,12 +35,12 @@ var AddonListener = Class("AddonListener", {
onExternalInstall: function (addon, existingAddon, needsRestart) {},
onDownloadStarted: listener("download", "started"),
onDownloadEnded: listener("download", "complete"),
- onDownloadCancelled: listener("download", "cancelled"),
+ onDownloadCancelled: listener("download", "canceled"),
onDownloadFailed: listener("download", "failed"),
onDownloadProgress: function (install) {},
onInstallStarted: function (install) {},
onInstallEnded: listener("installation", "complete"),
- onInstallCancelled: listener("installation", "cancelled"),
+ onInstallCancelled: listener("installation", "canceled"),
onInstallFailed: listener("installation", "failed")
});
diff --git a/common/modules/base.jsm b/common/modules/base.jsm
index c3da6d2c..f147ebf1 100644
--- a/common/modules/base.jsm
+++ b/common/modules/base.jsm
@@ -616,12 +616,12 @@ function update(target) {
if (desc.value instanceof Class.Property)
desc = desc.value.init(k) || desc.value;
if (typeof desc.value == "function" && Object.getPrototypeOf(target)) {
- let func = desc.value;
- desc.value.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
- desc.value.superapply = function superapply(self, args)
+ let func = desc.value.wrapped || desc.value;
+ func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
+ func.superapply = function superapply(self, args)
let (meth = Object.getPrototypeOf(target)[k])
meth && meth.apply(self, args);
- desc.value.supercall = function supercall(self)
+ func.supercall = function supercall(self)
func.superapply(self, Array.slice(arguments, 1));
}
Object.defineProperty(target, k, desc);
@@ -663,7 +663,7 @@ function Class() {
superclass = args.shift();
var Constructor = eval(String.replace(<![CDATA[
- (function constructor() {
+ (function constructor(PARAMS) {
var self = Object.create(Constructor.prototype, {
constructor: { value: Constructor },
});
@@ -671,7 +671,9 @@ function Class() {
var res = self.init.apply(self, arguments);
return res !== undefined ? res : self;
})]]>,
- "constructor", (name || superclass.className).replace(/\W/g, "_")));
+ "constructor", (name || superclass.className).replace(/\W/g, "_"))
+ .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]));
+
Constructor.className = name || superclass.className || superclass.name;
if ("init" in superclass.prototype)
diff --git a/common/modules/io.jsm b/common/modules/io.jsm
index 3e85b02d..e0fdc00f 100644
--- a/common/modules/io.jsm
+++ b/common/modules/io.jsm
@@ -546,7 +546,7 @@ var IO = Module("io", {
PATH_SEP: deprecated("File.PATH_SEP", { get: function PATH_SEP() File.PATH_SEP })
}, {
init: function init(dactyl, modules, window) {
- modules.plugins.contexts = {};
+ Class.replaceProperty(modules.plugins, "contexts", {});
modules.Script = function Script(file) {
const { io, plugins } = modules;
@@ -615,15 +615,6 @@ var IO = Module("io", {
literal: 0
});
- // NOTE: this command is only used in :source
- commands.add(["fini[sh]"],
- "Stop sourcing a script file",
- function () {
- dactyl.assert(io.sourcing, "E168: :finish used outside of a sourced file");
- io.sourcing.finished = true;
- },
- { argCount: "0" });
-
commands.add(["pw[d]"],
"Print the current directory name",
function () { dactyl.echomsg(io.cwd.path); },
diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm
index 7b73be7c..45aaf449 100644
--- a/common/modules/overlay.jsm
+++ b/common/modules/overlay.jsm
@@ -144,6 +144,7 @@ var Overlay = Module("Overlay", {
return sandbox;
}
});
+ modules.plugins = create(modules);
modules.modules = modules;
window.dactyl = { modules: modules };
@@ -163,12 +164,13 @@ var Overlay = Module("Overlay", {
"util"
].forEach(function (name) defineModule.time("load", name, require, null, jsmodules, name));
- ["dactyl",
+ ["contexts",
+ "dactyl",
"modes",
+ "commandline",
"abbreviations",
"autocommands",
"buffer",
- "commandline",
"commands",
"editor",
"events",
diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm
index b41e86cf..64a7d7f2 100644
--- a/common/modules/storage.jsm
+++ b/common/modules/storage.jsm
@@ -152,6 +152,8 @@ var Storage = Module("Storage", {
},
cleanup: function () {
+ this.saveAll();
+
for (let key in keys(this.keys)) {
if (this[key].timer)
this[key].timer.flush();
diff --git a/common/modules/util.jsm b/common/modules/util.jsm
index 6b423c66..9bf6e2ee 100644
--- a/common/modules/util.jsm
+++ b/common/modules/util.jsm
@@ -32,8 +32,8 @@ memoize(this, "Commands", function () {
var FailedAssertion = Class("FailedAssertion", ErrorBase);
var Point = Struct("x", "y");
-var wrapCallback = function wrapCallback(fn)
- fn.wrapper = (function wrappedCallback () {
+var wrapCallback = function wrapCallback(fn) {
+ fn.wrapper = function wrappedCallback () {
try {
return fn.apply(this, arguments);
}
@@ -41,7 +41,10 @@ var wrapCallback = function wrapCallback(fn)
util.reportError(e);
return undefined;
}
- })
+ };
+ fn.wrapper.wrapped = fn;
+ return fn.wrapper;
+}
var getAttr = function getAttr(elem, ns, name)
elem.hasAttributeNS(ns, name) ? elem.getAttributeNS(ns, name) : null;
@@ -1585,6 +1588,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
})();
},
+ wrapCallback: wrapCallback,
+
/**
* Traps errors in the called function, possibly reporting them.
*