1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-22 10:27:58 +01:00

Add operator mode per Vim. Wrap most editor commands in transactions. And stuff. Closes issue #439.

This commit is contained in:
Kris Maglione
2011-08-05 16:50:00 -04:00
parent 55f5f9292b
commit f502617a84
7 changed files with 201 additions and 193 deletions

View File

@@ -1688,12 +1688,12 @@ var Buffer = Module("buffer", {
function () { ex.stop(); }); function () { ex.stop(); });
// scrolling // scrolling
mappings.add([modes.COMMAND], ["j", "<Down>", "<C-e>", "<scroll-down-line>"], mappings.add([modes.NORMAL], ["j", "<Down>", "<C-e>", "<scroll-down-line>"],
"Scroll document down", "Scroll document down",
function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); }, function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["k", "<Up>", "<C-y>", "<scroll-up-line>"], mappings.add([modes.NORMAL], ["k", "<Up>", "<C-y>", "<scroll-up-line>"],
"Scroll document up", "Scroll document up",
function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); }, function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); },
{ count: true }); { count: true });
@@ -1703,30 +1703,30 @@ var Buffer = Module("buffer", {
function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); }, function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], dactyl.has("mail") ? ["l", "<scroll-right-column>"] : ["l", "<Right>", "<scroll-right-column>"], mappings.add([modes.NORMAL], dactyl.has("mail") ? ["l", "<scroll-right-column>"] : ["l", "<Right>", "<scroll-right-column>"],
"Scroll document to the right", "Scroll document to the right",
function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); }, function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["0", "^", "<scroll-begin>"], mappings.add([modes.NORMAL], ["0", "^", "<scroll-begin>"],
"Scroll to the absolute left of the document", "Scroll to the absolute left of the document",
function () { buffer.scrollToPercent(0, null); }); function () { buffer.scrollToPercent(0, null); });
mappings.add([modes.COMMAND], ["$", "<scroll-end>"], mappings.add([modes.NORMAL], ["$", "<scroll-end>"],
"Scroll to the absolute right of the document", "Scroll to the absolute right of the document",
function () { buffer.scrollToPercent(100, null); }); function () { buffer.scrollToPercent(100, null); });
mappings.add([modes.COMMAND], ["gg", "<Home>", "<scroll-top>"], mappings.add([modes.NORMAL], ["gg", "<Home>", "<scroll-top>"],
"Go to the top of the document", "Go to the top of the document",
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); }, function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["G", "<End>", "<scroll-bottom>"], mappings.add([modes.NORMAL], ["G", "<End>", "<scroll-bottom>"],
"Go to the end of the document", "Go to the end of the document",
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); }, function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["%", "<scroll-percent>"], mappings.add([modes.NORMAL], ["%", "<scroll-percent>"],
"Scroll to {count} percent of the document", "Scroll to {count} percent of the document",
function (args) { function (args) {
dactyl.assert(args.count > 0 && args.count <= 100); dactyl.assert(args.count > 0 && args.count <= 100);
@@ -1734,22 +1734,22 @@ var Buffer = Module("buffer", {
}, },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["<C-d>", "<scroll-down>"], mappings.add([modes.NORMAL], ["<C-d>", "<scroll-down>"],
"Scroll window downwards in the buffer", "Scroll window downwards in the buffer",
function (args) { buffer._scrollByScrollSize(args.count, true); }, function (args) { buffer._scrollByScrollSize(args.count, true); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["<C-u>", "<scroll-up>"], mappings.add([modes.NORMAL], ["<C-u>", "<scroll-up>"],
"Scroll window upwards in the buffer", "Scroll window upwards in the buffer",
function (args) { buffer._scrollByScrollSize(args.count, false); }, function (args) { buffer._scrollByScrollSize(args.count, false); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-up-page>"], mappings.add([modes.NORMAL], ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-up-page>"],
"Scroll up a full page", "Scroll up a full page",
function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); }, function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["<Space>"], mappings.add([modes.NORMAL], ["<Space>"],
"Scroll down a full page", "Scroll down a full page",
function (args) { function (args) {
if (isinstance(content.document.activeElement, [HTMLInputElement, HTMLButtonElement])) if (isinstance(content.document.activeElement, [HTMLInputElement, HTMLButtonElement]))
@@ -1758,7 +1758,7 @@ var Buffer = Module("buffer", {
}, },
{ count: true }); { count: true });
mappings.add([modes.COMMAND], ["<C-f>", "<PageDown>", "<scroll-down-page>"], mappings.add([modes.NORMAL], ["<C-f>", "<PageDown>", "<scroll-down-page>"],
"Scroll down a full page", "Scroll down a full page",
function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); }, function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
{ count: true }); { count: true });
@@ -1969,7 +1969,7 @@ var Buffer = Module("buffer", {
try { try {
config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset = val; config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val); PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); window.getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
} }
catch (e) { dactyl.reportError(e); } catch (e) { dactyl.reportError(e); }
return null; return null;

View File

@@ -92,68 +92,6 @@ var Editor = Module("editor", {
} }
}, },
// cmd = y, d, c
// motion = b, 0, gg, G, etc.
selectMotion: function selectMotion(cmd, motion, count) {
// XXX: better as a precondition
if (count == null)
count = 1;
if (cmd == motion) {
motion = "j";
count--;
}
if (modes.main != modes.VISUAL)
modes.push(modes.VISUAL);
switch (motion) {
case "j":
this.executeCommand("cmd_beginLine", 1);
this.executeCommand("cmd_selectLineNext", count + 1);
break;
case "k":
this.executeCommand("cmd_beginLine", 1);
this.executeCommand("cmd_lineNext", 1);
this.executeCommand("cmd_selectLinePrevious", count + 1);
break;
case "h":
this.executeCommand("cmd_selectCharPrevious", count);
break;
case "l":
this.executeCommand("cmd_selectCharNext", count);
break;
case "e":
case "w":
this.executeCommand("cmd_selectWordNext", count);
break;
case "b":
this.executeCommand("cmd_selectWordPrevious", count);
break;
case "0":
case "^":
this.executeCommand("cmd_selectBeginLine", 1);
break;
case "$":
this.executeCommand("cmd_selectEndLine", 1);
break;
case "gg":
this.executeCommand("cmd_endLine", 1);
this.executeCommand("cmd_selectTop", 1);
this.executeCommand("cmd_selectBeginLine", 1);
break;
case "G":
this.executeCommand("cmd_beginLine", 1);
this.executeCommand("cmd_selectBottom", 1);
this.executeCommand("cmd_selectEndLine", 1);
break;
default:
dactyl.beep();
return;
}
},
// This function will move/select up to given "pos" // This function will move/select up to given "pos"
// Simple setSelectionRange() would be better, but we want to maintain the correct // Simple setSelectionRange() would be better, but we want to maintain the correct
// order of selectionStart/End (a Gecko bug always makes selectionStart <= selectionEnd) // order of selectionStart/End (a Gecko bug always makes selectionStart <= selectionEnd)
@@ -427,11 +365,27 @@ var Editor = Module("editor", {
}, { }, {
mappings: function () { mappings: function () {
Map.types["editor"] = {
preExecute: function preExecute(args) {
Editor.getEditor(null).beginTransaction();
},
postExecute: function preExecute(args) {
Editor.getEditor(null).endTransaction();
},
};
Map.types["operator"] = {
postExecute: function preExecute(args) {
if (modes.main == modes.OPERATOR)
modes.pop();
},
};
// add mappings for commands like h,j,k,l,etc. in CARET, VISUAL and TEXT_EDIT mode // add mappings for commands like h,j,k,l,etc. in CARET, VISUAL and TEXT_EDIT mode
function addMovementMap(keys, description, hasCount, caretModeMethod, caretModeArg, textEditCommand, visualTextEditCommand) { function addMovementMap(keys, description, hasCount, caretModeMethod, caretModeArg, textEditCommand, visualTextEditCommand) {
let extraInfo = {}; let extraInfo = {
if (hasCount) count: !!hasCount,
extraInfo.count = true; type: "operator"
};
function caretExecute(arg, again) { function caretExecute(arg, again) {
function fixSelection() { function fixSelection() {
@@ -486,7 +440,7 @@ var Editor = Module("editor", {
}, },
extraInfo); extraInfo);
mappings.add([modes.TEXT_EDIT], keys, description, mappings.add([modes.OPERATOR], keys, description,
function ({ count }) { function ({ count }) {
if (!count) if (!count)
count = 1; count = 1;
@@ -503,7 +457,8 @@ var Editor = Module("editor", {
commands.forEach(function (cmd) commands.forEach(function (cmd)
editor.executeCommand(cmd, 1)); editor.executeCommand(cmd, 1));
modes.push(modes.INSERT); modes.push(modes.INSERT);
}); },
{ type: "editor" });
} }
function selectPreviousLine() { function selectPreviousLine() {
@@ -582,33 +537,53 @@ var Editor = Module("editor", {
addBeginInsertModeMap(["S"], ["cmd_deleteToEndOfLine", "cmd_deleteToBeginningOfLine"], "Delete the current line and start insert"); addBeginInsertModeMap(["S"], ["cmd_deleteToEndOfLine", "cmd_deleteToBeginningOfLine"], "Delete the current line and start insert");
addBeginInsertModeMap(["C"], ["cmd_deleteToEndOfLine"], "Delete from the cursor to the end of the line and start insert"); addBeginInsertModeMap(["C"], ["cmd_deleteToEndOfLine"], "Delete from the cursor to the end of the line and start insert");
function addMotionMap(key, desc, cmd, mode) { function addMotionMap(key, desc, select, cmd, mode) {
mappings.add([modes.TEXT_EDIT], [key], mappings.add([modes.OPERATOR], [key],
desc, desc,
function ({ count, motion }) { function ({ count, motion, command }) {
editor.selectMotion(key, motion, Math.max(count, 1)); modes.push(modes.OPERATOR, null, {
if (callable(cmd)) leave: function leave(stack) {
cmd.call(events, Editor.getEditor(null)); if (stack.push)
else { return;
editor.executeCommand(cmd, 1);
modes.pop(modes.TEXT_EDIT); try {
editor.beginTransaction();
let range = RangeFind.union(start, sel.getRangeAt(0));
sel.removeAllRanges();
sel.addRange(select ? range : start);
cmd(editor, range);
} }
finally {
editor.endTransaction();
}
modes.delay(function () {
if (mode) if (mode)
modes.push(mode); modes.push(mode);
});
}
});
let editor = Editor.getEditor(null);
let sel = editor.selection;
let start = sel.getRangeAt(0).cloneRange();
}, },
{ count: true, motion: true }); { count: true, type: "motion" });
} }
addMotionMap("d", "Delete motion", "cmd_delete"); addMotionMap("d", "Delete motion", true, function (editor) { editor.cut(); });
addMotionMap("c", "Change motion", "cmd_delete", modes.INSERT); addMotionMap("c", "Change motion", true, function (editor) { editor.cut(); }, modes.INSERT);
addMotionMap("y", "Yank motion", "cmd_copy"); addMotionMap("y", "Yank motion", false, function (editor, range) { dactyl.clipboardWrite(util.domToString(range)) });
mappings.add([modes.INPUT], let bind = function bind(names, description, action, params)
["<C-w>"], "Delete previous word", mappings.add([modes.INPUT], names, description,
action, update({ type: "editor" }, params));
bind(["<C-w>"], "Delete previous word",
function () { editor.executeCommand("cmd_deleteWordBackward", 1); }); function () { editor.executeCommand("cmd_deleteWordBackward", 1); });
mappings.add([modes.INPUT], bind(["<C-u>"], "Delete until beginning of current line",
["<C-u>"], "Delete until beginning of current line",
function () { function () {
// Deletes the whole line. What the hell. // Deletes the whole line. What the hell.
// editor.executeCommand("cmd_deleteToBeginningOfLine", 1); // editor.executeCommand("cmd_deleteToBeginningOfLine", 1);
@@ -618,36 +593,28 @@ var Editor = Module("editor", {
editor.executeCommand("cmd_delete", 1); editor.executeCommand("cmd_delete", 1);
}); });
mappings.add([modes.INPUT], bind(["<C-k>"], "Delete until end of current line",
["<C-k>"], "Delete until end of current line",
function () { editor.executeCommand("cmd_deleteToEndOfLine", 1); }); function () { editor.executeCommand("cmd_deleteToEndOfLine", 1); });
mappings.add([modes.INPUT], bind(["<C-a>"], "Move cursor to beginning of current line",
["<C-a>"], "Move cursor to beginning of current line",
function () { editor.executeCommand("cmd_beginLine", 1); }); function () { editor.executeCommand("cmd_beginLine", 1); });
mappings.add([modes.INPUT], bind(["<C-e>"], "Move cursor to end of current line",
["<C-e>"], "Move cursor to end of current line",
function () { editor.executeCommand("cmd_endLine", 1); }); function () { editor.executeCommand("cmd_endLine", 1); });
mappings.add([modes.INPUT], bind(["<C-h>"], "Delete character to the left",
["<C-h>"], "Delete character to the left",
function () { events.feedkeys("<BS>", true); }); function () { events.feedkeys("<BS>", true); });
mappings.add([modes.INPUT], bind(["<C-d>"], "Delete character to the right",
["<C-d>"], "Delete character to the right",
function () { editor.executeCommand("cmd_deleteCharForward", 1); }); function () { editor.executeCommand("cmd_deleteCharForward", 1); });
mappings.add([modes.INPUT], bind(["<S-Insert>"], "Insert clipboard/selection",
["<S-Insert>"], "Insert clipboard/selection",
function () { editor.pasteClipboard(); }); function () { editor.pasteClipboard(); });
mappings.add([modes.INPUT, modes.TEXT_EDIT], bind(["<C-i>"], "Edit text field with an external editor",
["<C-i>"], "Edit text field with an external editor",
function () { editor.editFieldExternally(); }); function () { editor.editFieldExternally(); });
mappings.add([modes.INPUT], bind(["<C-t>"], "Edit text field in Vi mode",
["<C-t>"], "Edit text field in Vi mode",
function () { function () {
dactyl.assert(dactyl.focusedElement); dactyl.assert(dactyl.focusedElement);
dactyl.assert(!editor.isTextEdit); dactyl.assert(!editor.isTextEdit);
@@ -673,6 +640,10 @@ var Editor = Module("editor", {
["<C-]>", "<C-5>"], "Expand Insert mode abbreviation", ["<C-]>", "<C-5>"], "Expand Insert mode abbreviation",
function () { editor.expandAbbreviation(modes.INSERT); }); function () { editor.expandAbbreviation(modes.INSERT); });
let bind = function bind(names, description, action, params)
mappings.add([modes.TEXT_EDIT], names, description,
action, update({ type: "editor" }, params));
// text edit mode // text edit mode
mappings.add([modes.TEXT_EDIT], mappings.add([modes.TEXT_EDIT],
["u"], "Undo changes", ["u"], "Undo changes",
@@ -690,8 +661,7 @@ var Editor = Module("editor", {
}, },
{ count: true }); { count: true });
mappings.add([modes.TEXT_EDIT], bind(["D"], "Delete the characters under the cursor until the end of the line",
["D"], "Delete the characters under the cursor until the end of the line",
function () { editor.executeCommand("cmd_deleteToEndOfLine"); }); function () { editor.executeCommand("cmd_deleteToEndOfLine"); });
mappings.add([modes.TEXT_EDIT], mappings.add([modes.TEXT_EDIT],
@@ -711,13 +681,11 @@ var Editor = Module("editor", {
editor.executeCommand("cmd_linePrevious", 1); editor.executeCommand("cmd_linePrevious", 1);
}); });
mappings.add([modes.TEXT_EDIT], bind(["X"], "Delete character to the left",
["X"], "Delete character to the left",
function (args) { editor.executeCommand("cmd_deleteCharBackward", Math.max(args.count, 1)); }, function (args) { editor.executeCommand("cmd_deleteCharBackward", Math.max(args.count, 1)); },
{ count: true }); { count: true });
mappings.add([modes.TEXT_EDIT], bind(["x"], "Delete character to the right",
["x"], "Delete character to the right",
function (args) { editor.executeCommand("cmd_deleteCharForward", Math.max(args.count, 1)); }, function (args) { editor.executeCommand("cmd_deleteCharForward", Math.max(args.count, 1)); },
{ count: true }); { count: true });
@@ -764,8 +732,7 @@ var Editor = Module("editor", {
dactyl.clipboardWrite(buffer.currentWord, true); dactyl.clipboardWrite(buffer.currentWord, true);
}); });
mappings.add([modes.VISUAL, modes.TEXT_EDIT], bind(["p"], "Paste clipboard contents",
["p"], "Paste clipboard contents",
function ({ count }) { function ({ count }) {
dactyl.assert(!editor.isCaret); dactyl.assert(!editor.isCaret);
editor.executeCommand("cmd_paste", count || 1); editor.executeCommand("cmd_paste", count || 1);
@@ -773,42 +740,50 @@ var Editor = Module("editor", {
}, },
{ count: true }); { count: true });
let bind = function bind(names, description, action, params)
mappings.add([modes.OPERATOR], names, description,
action, update({ type: "editor" }, params));
// finding characters // finding characters
mappings.add([modes.TEXT_EDIT, modes.VISUAL], function offset(backward, before, pos) {
["f"], "Move to a character on the current line after the cursor", if (!backward && modes.main != modes.TEXT_EDIT)
pos += 1;
if (before)
pos += backward ? +1 : -1;
return pos;
}
bind(["f"], "Move to a character on the current line after the cursor",
function ({ arg, count }) { function ({ arg, count }) {
let pos = editor.findChar(arg, Math.max(count, 1)); let pos = editor.findChar(arg, Math.max(count, 1));
if (pos >= 0) if (pos >= 0)
editor.moveToPosition(pos, true, modes.main == modes.VISUAL); editor.moveToPosition(offset(false, false, pos), true, modes.main == modes.VISUAL);
}, },
{ arg: true, count: true }); { arg: true, count: true, type: "operator" });
mappings.add([modes.TEXT_EDIT, modes.VISUAL], bind(["F"], "Move to a character on the current line before the cursor",
["F"], "Move to a character on the current line before the cursor",
function ({ arg, count }) { function ({ arg, count }) {
let pos = editor.findChar(arg, Math.max(count, 1), true); let pos = editor.findChar(arg, Math.max(count, 1), true);
if (pos >= 0) if (pos >= 0)
editor.moveToPosition(pos, false, modes.main == modes.VISUAL); editor.moveToPosition(offset(true, false, pos), false, modes.main == modes.VISUAL);
}, },
{ arg: true, count: true }); { arg: true, count: true, type: "operator" });
mappings.add([modes.TEXT_EDIT, modes.VISUAL], bind(["t"], "Move before a character on the current line",
["t"], "Move before a character on the current line",
function ({ arg, count }) { function ({ arg, count }) {
let pos = editor.findChar(arg, Math.max(count, 1)); let pos = editor.findChar(arg, Math.max(count, 1));
if (pos >= 0) if (pos >= 0)
editor.moveToPosition(pos - 1, true, modes.main == modes.VISUAL); editor.moveToPosition(offset(false, true, pos), true, modes.main == modes.VISUAL);
}, },
{ arg: true, count: true }); { arg: true, count: true, type: "operator" });
mappings.add([modes.TEXT_EDIT, modes.VISUAL], bind(["T"], "Move before a character on the current line, backwards",
["T"], "Move before a character on the current line, backwards",
function ({ arg, count }) { function ({ arg, count }) {
let pos = editor.findChar(arg, Math.max(count, 1), true); let pos = editor.findChar(arg, Math.max(count, 1), true);
if (pos >= 0) if (pos >= 0)
editor.moveToPosition(pos + 1, false, modes.main == modes.VISUAL); editor.moveToPosition(offset(true, true, pos), false, modes.main == modes.VISUAL);
}, },
{ arg: true, count: true }); { arg: true, count: true, type: "operator" });
// text edit and visual mode // text edit and visual mode
mappings.add([modes.TEXT_EDIT, modes.VISUAL], mappings.add([modes.TEXT_EDIT, modes.VISUAL],
@@ -834,7 +809,7 @@ var Editor = Module("editor", {
}, },
{ count: true }); { count: true });
function bind() mappings.add.apply(mappings, let bind = function bind() mappings.add.apply(mappings,
[[modes.AUTOCOMPLETE]].concat(Array.slice(arguments))) [[modes.AUTOCOMPLETE]].concat(Array.slice(arguments)))
bind(["<Esc>"], "Return to Insert mode", bind(["<Esc>"], "Return to Insert mode",

View File

@@ -17,7 +17,7 @@
* *action*. * *action*.
* @param {string} description A short one line description of the key mapping. * @param {string} description A short one line description of the key mapping.
* @param {function} action The action invoked by each key sequence. * @param {function} action The action invoked by each key sequence.
* @param {Object} extraInfo An optional extra configuration hash. The * @param {Object} info An optional extra configuration hash. The
* following properties are supported. * following properties are supported.
* arg - see {@link Map#arg} * arg - see {@link Map#arg}
* count - see {@link Map#count} * count - see {@link Map#count}
@@ -29,7 +29,7 @@
* @private * @private
*/ */
var Map = Class("Map", { var Map = Class("Map", {
init: function (modes, keys, description, action, extraInfo) { init: function (modes, keys, description, action, info) {
this.id = ++Map.id; this.id = ++Map.id;
this.modes = modes; this.modes = modes;
this._keys = keys; this._keys = keys;
@@ -38,8 +38,11 @@ var Map = Class("Map", {
Object.freeze(this.modes); Object.freeze(this.modes);
if (extraInfo) if (info) {
this.update(extraInfo); if (Set.has(Map.types, info.type))
this.update(Map.types[info.type]);
this.update(info);
}
}, },
name: Class.memoize(function () this.names[0]), name: Class.memoize(function () this.names[0]),
@@ -69,12 +72,24 @@ var Map = Class("Map", {
* as an argument. * as an argument.
*/ */
motion: false, motion: false,
/** @property {boolean} Whether the RHS of the mapping should expand mappings recursively. */ /** @property {boolean} Whether the RHS of the mapping should expand mappings recursively. */
noremap: false, noremap: false,
/** @property {function(object)} A function to be executed before this mapping. */
preExecute: function preExecute(args) {},
/** @property {function(object)} A function to be executed after this mapping. */
postExecute: function postExecute(args) {},
/** @property {boolean} Whether any output from the mapping should be echoed on the command line. */ /** @property {boolean} Whether any output from the mapping should be echoed on the command line. */
silent: false, silent: false,
/** @property {string} The literal RHS expansion of this mapping. */ /** @property {string} The literal RHS expansion of this mapping. */
rhs: null, rhs: null,
/** @property {string} The type of this mapping. */
type: "",
/** /**
* @property {boolean} Specifies whether this is a user mapping. User * @property {boolean} Specifies whether this is a user mapping. User
* mappings may be created by plugins, or directly by users. Users and * mappings may be created by plugins, or directly by users. Users and
@@ -118,6 +133,7 @@ var Map = Class("Map", {
dactyl.assert(!this.executing, _("map.recursive", args.command)); dactyl.assert(!this.executing, _("map.recursive", args.command));
try { try {
this.preExecute(args);
this.executing = true; this.executing = true;
var res = repeat(); var res = repeat();
} }
@@ -127,12 +143,15 @@ var Map = Class("Map", {
} }
finally { finally {
this.executing = false; this.executing = false;
this.postExecute(args);
} }
return res; return res;
} }
}, { }, {
id: 0 id: 0,
types: {}
}); });
var MapHive = Class("MapHive", Contexts.Hive, { var MapHive = Class("MapHive", Contexts.Hive, {

View File

@@ -57,11 +57,16 @@ var Modes = Module("modes", {
description: "Active when nothing is focused", description: "Active when nothing is focused",
bases: [this.COMMAND] bases: [this.COMMAND]
}); });
this.addMode("OPERATOR", {
char: "o",
description: "Mappings which move the cursor",
bases: []
});
this.addMode("VISUAL", { this.addMode("VISUAL", {
char: "v", char: "v",
description: "Active when text is selected", description: "Active when text is selected",
display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : ""), display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : ""),
bases: [this.COMMAND], bases: [this.COMMAND, this.OPERATOR],
ownsFocus: true ownsFocus: true
}, { }, {
leave: function (stack, newMode) { leave: function (stack, newMode) {
@@ -97,7 +102,7 @@ var Modes = Module("modes", {
this.addMode("TEXT_EDIT", { this.addMode("TEXT_EDIT", {
char: "t", char: "t",
description: "Vim-like editing of input elements", description: "Vim-like editing of input elements",
bases: [this.COMMAND], bases: [this.OPERATOR, this.COMMAND],
ownsFocus: true ownsFocus: true
}, { }, {
onKeyPress: function (eventList) { onKeyPress: function (eventList) {
@@ -489,7 +494,7 @@ var Modes = Module("modes", {
init: function init(name, options, params) { init: function init(name, options, params) {
if (options.bases) if (options.bases)
util.assert(options.bases.every(function (m) m instanceof this, this.constructor), util.assert(options.bases.every(function (m) m instanceof this, this.constructor),
_("mode.invalidBases"), true); _("mode.invalidBases"), false);
this.update({ this.update({
id: 1 << Modes.Mode._id++, id: 1 << Modes.Mode._id++,
@@ -647,7 +652,7 @@ var Modes = Module("modes", {
options.add(["showmode", "smd"], options.add(["showmode", "smd"],
"Show the current mode in the command line when it matches this expression", "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); opts);
}, },
prefs: function initPrefs() { prefs: function initPrefs() {

View File

@@ -56,6 +56,7 @@
<dt>i</dt> <dd>Insert mode: When interacting with text fields on a website</dd> <dt>i</dt> <dd>Insert mode: When interacting with text fields on a website</dd>
<dt>t</dt> <dd>Text Edit mode: When editing text fields in Vim-like Normal mode</dd> <dt>t</dt> <dd>Text Edit mode: When editing text fields in Vim-like Normal mode</dd>
<dt>c</dt> <dd>Command Line mode: When typing into the &dactyl.appName; command line</dd> <dt>c</dt> <dd>Command Line mode: When typing into the &dactyl.appName; command line</dd>
<dt>o</dt> <dd>Operator mode: When moving the cursor</dd>
</dl> </dl>
<p> <p>

View File

@@ -774,7 +774,14 @@ var RangeFind = Class("RangeFind", {
} }
return true; return true;
}, },
selectNodePath: ["a", "xhtml:a", "*[@onclick]"].map(function (p) "ancestor-or-self::" + p).join(" | ") selectNodePath: ["a", "xhtml:a", "*[@onclick]"].map(function (p) "ancestor-or-self::" + p).join(" | "),
union: function union(a, b) {
let start = a.compareBoundaryPoints(a.START_TO_START, b) < 0 ? a : b;
let end = a.compareBoundaryPoints(a.END_TO_END, b) > 0 ? a : b;
let res = start.cloneRange();
res.setEnd(end.startContainer, end.endOffset);
return res;
}
}); });
} catch(e){ if (typeof e === "string") e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } } catch(e){ if (typeof e === "string") e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }

View File

@@ -74,6 +74,7 @@
• Mapping changes: • Mapping changes:
- It's now possible to map keys in many more modes, including - It's now possible to map keys in many more modes, including
Hint, Multi-line Output, and Menu. [b4] Hint, Multi-line Output, and Menu. [b4]
- Added Operator mode for motion maps, per Vim. [b8]
- Added site-specific mapping groups and related command - Added site-specific mapping groups and related command
changes. [b6] changes. [b6]
- Added 'timeout' and 'timeoutlen' options. [b6] - Added 'timeout' and 'timeoutlen' options. [b6]