//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
-"use strict";
+/* use strict */
/** @scope modules */
description: "Active when nothing is focused",
bases: [this.COMMAND]
});
- this.addMode("VISUAL", {
- char: "v",
- description: "Active when text is selected",
- display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : ""),
- bases: [this.COMMAND],
- ownsFocus: true
- }, {
- leave: function (stack, newMode) {
- if (newMode.main == modes.CARET) {
- let selection = content.getSelection();
- if (selection && !selection.isCollapsed)
- selection.collapseToStart();
- }
- else if (stack.pop)
- editor.unselectText();
- }
- });
this.addMode("CARET", {
+ char: "caret",
description: "Active when the caret is visible in the web content",
bases: [this.NORMAL]
}, {
modes.pop();
else if (!stack.pop && !this.pref)
this.pref = true;
+ if (!stack.pop)
+ buffer.resetCaret();
},
leave: function (stack) {
this.pref = false;
}
});
- this.addMode("TEXT_EDIT", {
- char: "t",
- description: "Vim-like editing of input elements",
- bases: [this.COMMAND],
- ownsFocus: true
- }, {
- onKeyPress: function (eventList) {
- const KILL = false, PASS = true;
-
- // Hack, really.
- if (eventList[0].charCode || /^<(?:.-)*(?:BS|Del|C-h|C-w|C-u|C-k)>$/.test(events.toString(eventList[0]))) {
- dactyl.beep();
- return KILL;
- }
- return PASS;
- }
- });
- this.addMode("OUTPUT_MULTILINE", {
- description: "Active when the multi-line output buffer is open",
- bases: [this.NORMAL]
- });
this.addMode("INPUT", {
char: "I",
bases: [this.MAIN],
insert: true
});
- this.addMode("INSERT", {
- char: "i",
- description: "Active when an input element is focused",
- insert: true,
- ownsFocus: true
- });
- this.addMode("AUTOCOMPLETE", {
- description: "Active when an input autocomplete pop-up is active",
- display: function () "AUTOCOMPLETE (insert)",
- bases: [this.INSERT]
- });
this.addMode("EMBED", {
description: "Active when an <embed> or <object> element is focused",
+ bases: [modes.MAIN],
insert: true,
ownsFocus: true,
passthrough: true
}
});
-
- function makeTree() {
- let list = modes.all.filter(function (m) m.name !== m.description);
-
- let tree = {};
-
- for (let mode in values(list))
- tree[mode.name] = {};
-
- for (let mode in values(list))
- for (let base in values(mode.bases))
- tree[base.name][mode.name] = tree[mode.name];
-
- let roots = iter([m.name, tree[m.name]] for (m in values(list)) if (!m.bases.length)).toObject();
-
- default xml namespace = NS;
- function rec(obj) {
- XML.ignoreWhitespace = XML.prettyPrinting = false;
-
- let res = <ul dactyl:highlight="Dense" xmlns:dactyl={NS}/>;
- Object.keys(obj).sort().forEach(function (name) {
- let mode = modes.getMode(name);
- res.* += <li><em>{mode.displayName}</em>: {mode.description}{
- rec(obj[name])
- }</li>;
- });
-
- if (res.*.length())
- return res;
- return <></>;
- }
-
- return rec(roots);
- }
-
- util.timeout(function () {
- // Waits for the add-on to become available, if necessary.
- config.addon;
- config.version;
-
- services["dactyl:"].pages["modes.dtd"] = services["dactyl:"].pages["modes.dtd"]();
- });
-
- services["dactyl:"].pages["modes.dtd"] = function () [null,
- util.makeDTD(iter({ "modes.tree": makeTree() },
- config.dtd))];
},
+
cleanup: function cleanup() {
modes.reset();
},
+ signals: {
+ "io.source": function ioSource(context, file, modTime) {
+ cache.flushEntry("modes.dtd", modTime);
+ }
+ },
+
_getModeMessage: function _getModeMessage() {
// when recording a macro
let macromode = "";
if (this.recording)
- macromode = "recording";
+ macromode = "recording " + this.recording + " ";
else if (this.replaying)
macromode = "replaying";
- let val = this._modeMap[this._main].display();
- if (val)
- return "-- " + val + " --" + macromode;
- return macromode;
+ if (!options.get("showmode").getKey(this.main.allBases, false))
+ return macromode;
+
+ let modeName = this._modeMap[this._main].display();
+ if (!modeName)
+ return macromode;
+
+ if (macromode)
+ macromode = " " + macromode;
+ return "-- " + modeName + " --" + macromode;
},
NONE: 0,
dactyl.triggerObserver("modes.add", mode);
},
+ removeMode: function removeMode(mode) {
+ this.remove(mode);
+ if (this[mode.name] == mode)
+ delete this[mode.name];
+ if (this._modeMap[mode.name] == mode)
+ delete this._modeMap[mode.name];
+ if (this._modeMap[mode.mode] == mode)
+ delete this._modeMap[mode.mode];
+
+ this._mainModes = this._mainModes.filter(function (m) m != mode);
+ },
+
dumpStack: function dumpStack() {
util.dump("Mode stack:");
for (let [i, mode] in array.iterItems(this._modeStack))
if (!loaded.modes)
return;
- let msg = null;
- if (options.get("showmode").getKey(this.main.allBases, false))
- msg = this._getModeMessage();
+ let msg = this._getModeMessage();
if (msg || loaded.commandline)
commandline.widgets.mode = msg || null;
if (!(id in this.boundProperties))
for (let elem in array.iterValues(this._modeStack))
elem.saved[id] = { obj: obj, prop: prop, value: obj[prop], test: test };
- this.boundProperties[id] = { obj: Cu.getWeakReference(obj), prop: prop, test: test };
+ this.boundProperties[id] = { obj: util.weakReference(obj), prop: prop, test: test };
},
inSet: false,
- // helper function to set both modes in one go
set: function set(mainMode, extendedMode, params, stack) {
var delayed, oldExtended, oldMain, prev, push;
},
push: function push(mainMode, extendedMode, params) {
+ if (this.main == this.IGNORE)
+ this.pop();
+
this.set(mainMode, extendedMode, params, { push: this.topOfStack });
},
while (this._modeStack.length > 1 && this.main != mode) {
let a = this._modeStack.pop();
this.set(this.topOfStack.main, this.topOfStack.extended, this.topOfStack.params,
- update({ pop: a },
- args || {}));
+ update({ pop: a }, args));
if (mode == null)
return;
init: function init(name, options, params) {
if (options.bases)
util.assert(options.bases.every(function (m) m instanceof this, this.constructor),
- _("mode.invalidBases"), true);
+ _("mode.invalidBases"), false);
this.update({
id: 1 << Modes.Mode._id++,
description: Messages.Localized(""),
- displayName: Class.memoize(function () this.name.split("_").map(util.capitalize).join(" ")),
+ displayName: Class.Memoize(function () this.name.split("_").map(util.capitalize).join(" ")),
isinstance: function isinstance(obj)
this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
- allBases: Class.memoize(function () {
+ allBases: Class.Memoize(function () {
let seen = {}, res = [], queue = [this].concat(this.bases);
for (let mode in array.iterValues(queue))
if (!Set.add(seen, mode)) {
get count() !this.insert,
- _display: Class.memoize(function _display() this.name.replace("_", " ", "g")),
+ _display: Class.Memoize(function _display() this.name.replace("_", " ", "g")),
display: function display() this._display,
hidden: false,
- input: Class.memoize(function input() this.insert || this.bases.length && this.bases.some(function (b) b.input)),
+ input: Class.Memoize(function input() this.insert || this.bases.length && this.bases.some(function (b) b.input)),
- insert: Class.memoize(function insert() this.bases.length && this.bases.some(function (b) b.insert)),
+ insert: Class.Memoize(function insert() this.bases.length && this.bases.some(function (b) b.insert)),
- ownsFocus: Class.memoize(function ownsFocus() this.bases.length && this.bases.some(function (b) b.ownsFocus)),
+ ownsFocus: Class.Memoize(function ownsFocus() this.bases.length && this.bases.some(function (b) b.ownsFocus)),
passEvent: function passEvent(event) this.input && event.charCode && !(event.ctrlKey || event.altKey || event.metaKey),
- passUnknown: Class.memoize(function () options.get("passunknown").getKey(this.name)),
+ passUnknown: Class.Memoize(function () options.get("passunknown").getKey(this.name)),
get mask() this,
}, desc));
}
}, {
+ cache: function initCache() {
+ function makeTree() {
+ let list = modes.all.filter(function (m) m.name !== m.description);
+
+ let tree = {};
+
+ for (let mode in values(list))
+ tree[mode.name] = {};
+
+ for (let mode in values(list))
+ for (let base in values(mode.bases))
+ tree[base.name][mode.name] = tree[mode.name];
+
+ let roots = iter([m.name, tree[m.name]] for (m in values(list)) if (!m.bases.length)).toObject();
+
+ default xml namespace = NS;
+ function rec(obj) {
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
+
+ let res = <ul dactyl:highlight="Dense" xmlns:dactyl={NS}/>;
+ Object.keys(obj).sort().forEach(function (name) {
+ let mode = modes.getMode(name);
+ res.* += <li><em>{mode.displayName}</em>: {mode.description}{
+ rec(obj[name])
+ }</li>;
+ });
+
+ if (res.*.length())
+ return res;
+ return <></>;
+ }
+
+ return rec(roots);
+ }
+
+ cache.register("modes.dtd", function ()
+ util.makeDTD(iter({ "modes.tree": makeTree() },
+ config.dtd)));
+ },
mappings: function initMappings() {
mappings.add([modes.BASE, modes.NORMAL],
["<Esc>", "<C-[>"],
"Return to Normal mode",
function () { modes.reset(); });
- mappings.add([modes.INPUT, modes.COMMAND, modes.PASS_THROUGH, modes.QUOTE],
+ mappings.add([modes.INPUT, modes.COMMAND, modes.OPERATOR, modes.PASS_THROUGH, modes.QUOTE],
["<Esc>", "<C-[>"],
"Return to the previous mode",
- function () { modes.pop(); });
+ function () { modes.pop(null, { fromEscape: true }); });
- mappings.add([modes.MENU], ["<C-c>"],
- "Leave Menu mode",
+ mappings.add([modes.AUTOCOMPLETE, modes.MENU], ["<C-c>"],
+ "Leave Autocomplete or Menu mode",
function () { modes.pop(); });
mappings.add([modes.MENU], ["<Esc>"],
"Close the current popup",
- function () { return Events.PASS_THROUGH; });
+ function () {
+ if (events.popups.active.length)
+ return Events.PASS_THROUGH;
+ modes.pop();
+ });
mappings.add([modes.MENU], ["<C-[>"],
"Close the current popup",
options.add(["passunknown", "pu"],
"Pass through unknown keys in these modes",
- "stringlist", "!text_edit,base",
+ "stringlist", "!text_edit,!visual,base",
opts);
options.add(["showmode", "smd"],
"Show the current mode in the command line when it matches this expression",
- "stringlist", "caret,output_multiline,!normal,base",
+ "stringlist", "caret,output_multiline,!normal,base,operator",
opts);
},
prefs: function initPrefs() {
- prefs.watch("accessibility.browsewithcaret", function () modes.onCaretChange.apply(modes, arguments));
+ prefs.watch("accessibility.browsewithcaret",
+ function () { modes.onCaretChange.apply(modes, arguments) });
}
});