diff options
author | Kris Maglione <maglione.k@gmail.com> | 2011-08-05 16:50:00 -0400 |
---|---|---|
committer | Kris Maglione <maglione.k@gmail.com> | 2011-08-05 16:50:00 -0400 |
commit | f502617a8460ba807ae726577195656707185882 (patch) | |
tree | 96aed1adbed89cc93f8d8947da6ce16b91fbecd0 | |
parent | 55f5f9292b18a7b6ff2f6d70cb45a530b81e3862 (diff) | |
download | pentadactyl-f502617a8460ba807ae726577195656707185882.tar.gz |
Add operator mode per Vim. Wrap most editor commands in transactions. And stuff. Closes issue #439.
-rw-r--r-- | common/content/buffer.js | 28 | ||||
-rw-r--r-- | common/content/editor.js | 317 | ||||
-rw-r--r-- | common/content/mappings.js | 29 | ||||
-rw-r--r-- | common/content/modes.js | 13 | ||||
-rw-r--r-- | common/locale/en-US/map.xml | 1 | ||||
-rw-r--r-- | common/modules/finder.jsm | 9 | ||||
-rw-r--r-- | pentadactyl/NEWS | 1 |
7 files changed, 203 insertions, 195 deletions
diff --git a/common/content/buffer.js b/common/content/buffer.js index d1577935..ace57af8 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -1688,12 +1688,12 @@ var Buffer = Module("buffer", { function () { ex.stop(); }); // 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", function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); }, { 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", function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); }, { count: true }); @@ -1703,30 +1703,30 @@ var Buffer = Module("buffer", { function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); }, { 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", function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); }, { count: true }); - mappings.add([modes.COMMAND], ["0", "^", "<scroll-begin>"], + mappings.add([modes.NORMAL], ["0", "^", "<scroll-begin>"], "Scroll to the absolute left of the document", 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", 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", function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); }, { 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", function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); }, { count: true }); - mappings.add([modes.COMMAND], ["%", "<scroll-percent>"], + mappings.add([modes.NORMAL], ["%", "<scroll-percent>"], "Scroll to {count} percent of the document", function (args) { dactyl.assert(args.count > 0 && args.count <= 100); @@ -1734,22 +1734,22 @@ var Buffer = Module("buffer", { }, { count: true }); - mappings.add([modes.COMMAND], ["<C-d>", "<scroll-down>"], + mappings.add([modes.NORMAL], ["<C-d>", "<scroll-down>"], "Scroll window downwards in the buffer", function (args) { buffer._scrollByScrollSize(args.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", function (args) { buffer._scrollByScrollSize(args.count, false); }, { 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", function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); }, { count: true }); - mappings.add([modes.COMMAND], ["<Space>"], + mappings.add([modes.NORMAL], ["<Space>"], "Scroll down a full page", function (args) { if (isinstance(content.document.activeElement, [HTMLInputElement, HTMLButtonElement])) @@ -1758,7 +1758,7 @@ var Buffer = Module("buffer", { }, { 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", function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); }, { count: true }); @@ -1969,7 +1969,7 @@ var Buffer = Module("buffer", { try { config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset = 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); } return null; diff --git a/common/content/editor.js b/common/content/editor.js index d2fe3224..1ea8a272 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -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" // Simple setSelectionRange() would be better, but we want to maintain the correct // order of selectionStart/End (a Gecko bug always makes selectionStart <= selectionEnd) @@ -427,11 +365,27 @@ var Editor = Module("editor", { }, { 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 function addMovementMap(keys, description, hasCount, caretModeMethod, caretModeArg, textEditCommand, visualTextEditCommand) { - let extraInfo = {}; - if (hasCount) - extraInfo.count = true; + let extraInfo = { + count: !!hasCount, + type: "operator" + }; function caretExecute(arg, again) { function fixSelection() { @@ -486,7 +440,7 @@ var Editor = Module("editor", { }, extraInfo); - mappings.add([modes.TEXT_EDIT], keys, description, + mappings.add([modes.OPERATOR], keys, description, function ({ count }) { if (!count) count = 1; @@ -503,7 +457,8 @@ var Editor = Module("editor", { commands.forEach(function (cmd) editor.executeCommand(cmd, 1)); modes.push(modes.INSERT); - }); + }, + { type: "editor" }); } function selectPreviousLine() { @@ -582,77 +537,89 @@ var Editor = Module("editor", { 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"); - function addMotionMap(key, desc, cmd, mode) { - mappings.add([modes.TEXT_EDIT], [key], + function addMotionMap(key, desc, select, cmd, mode) { + mappings.add([modes.OPERATOR], [key], desc, - function ({ count, motion }) { - editor.selectMotion(key, motion, Math.max(count, 1)); - if (callable(cmd)) - cmd.call(events, Editor.getEditor(null)); - else { - editor.executeCommand(cmd, 1); - modes.pop(modes.TEXT_EDIT); - } - if (mode) - modes.push(mode); + function ({ count, motion, command }) { + modes.push(modes.OPERATOR, null, { + leave: function leave(stack) { + if (stack.push) + return; + + 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) + 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("c", "Change motion", "cmd_delete", modes.INSERT); - addMotionMap("y", "Yank motion", "cmd_copy"); + addMotionMap("d", "Delete motion", true, function (editor) { editor.cut(); }); + addMotionMap("c", "Change motion", true, function (editor) { editor.cut(); }, modes.INSERT); + addMotionMap("y", "Yank motion", false, function (editor, range) { dactyl.clipboardWrite(util.domToString(range)) }); - mappings.add([modes.INPUT], - ["<C-w>"], "Delete previous word", - function () { editor.executeCommand("cmd_deleteWordBackward", 1); }); + let bind = function bind(names, description, action, params) + mappings.add([modes.INPUT], names, description, + action, update({ type: "editor" }, params)); - mappings.add([modes.INPUT], - ["<C-u>"], "Delete until beginning of current line", - function () { - // Deletes the whole line. What the hell. - // editor.executeCommand("cmd_deleteToBeginningOfLine", 1); + bind(["<C-w>"], "Delete previous word", + function () { editor.executeCommand("cmd_deleteWordBackward", 1); }); - editor.executeCommand("cmd_selectBeginLine", 1); - if (Editor.getController().isCommandEnabled("cmd_delete")) - editor.executeCommand("cmd_delete", 1); - }); + bind(["<C-u>"], "Delete until beginning of current line", + function () { + // Deletes the whole line. What the hell. + // editor.executeCommand("cmd_deleteToBeginningOfLine", 1); - mappings.add([modes.INPUT], - ["<C-k>"], "Delete until end of current line", - function () { editor.executeCommand("cmd_deleteToEndOfLine", 1); }); + editor.executeCommand("cmd_selectBeginLine", 1); + if (Editor.getController().isCommandEnabled("cmd_delete")) + editor.executeCommand("cmd_delete", 1); + }); - mappings.add([modes.INPUT], - ["<C-a>"], "Move cursor to beginning of current line", - function () { editor.executeCommand("cmd_beginLine", 1); }); + bind(["<C-k>"], "Delete until end of current line", + function () { editor.executeCommand("cmd_deleteToEndOfLine", 1); }); - mappings.add([modes.INPUT], - ["<C-e>"], "Move cursor to end of current line", - function () { editor.executeCommand("cmd_endLine", 1); }); + bind(["<C-a>"], "Move cursor to beginning of current line", + function () { editor.executeCommand("cmd_beginLine", 1); }); - mappings.add([modes.INPUT], - ["<C-h>"], "Delete character to the left", - function () { events.feedkeys("<BS>", true); }); + bind(["<C-e>"], "Move cursor to end of current line", + function () { editor.executeCommand("cmd_endLine", 1); }); - mappings.add([modes.INPUT], - ["<C-d>"], "Delete character to the right", - function () { editor.executeCommand("cmd_deleteCharForward", 1); }); + bind(["<C-h>"], "Delete character to the left", + function () { events.feedkeys("<BS>", true); }); - mappings.add([modes.INPUT], - ["<S-Insert>"], "Insert clipboard/selection", - function () { editor.pasteClipboard(); }); + bind(["<C-d>"], "Delete character to the right", + function () { editor.executeCommand("cmd_deleteCharForward", 1); }); - mappings.add([modes.INPUT, modes.TEXT_EDIT], - ["<C-i>"], "Edit text field with an external editor", - function () { editor.editFieldExternally(); }); + bind(["<S-Insert>"], "Insert clipboard/selection", + function () { editor.pasteClipboard(); }); - mappings.add([modes.INPUT], - ["<C-t>"], "Edit text field in Vi mode", - function () { - dactyl.assert(dactyl.focusedElement); - dactyl.assert(!editor.isTextEdit); - modes.push(modes.TEXT_EDIT); - }); + bind(["<C-i>"], "Edit text field with an external editor", + function () { editor.editFieldExternally(); }); + + bind(["<C-t>"], "Edit text field in Vi mode", + function () { + dactyl.assert(dactyl.focusedElement); + dactyl.assert(!editor.isTextEdit); + modes.push(modes.TEXT_EDIT); + }); // Ugh. mappings.add([modes.INPUT, modes.CARET], @@ -673,6 +640,10 @@ var Editor = Module("editor", { ["<C-]>", "<C-5>"], "Expand Insert mode abbreviation", 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 mappings.add([modes.TEXT_EDIT], ["u"], "Undo changes", @@ -690,9 +661,8 @@ var Editor = Module("editor", { }, { count: true }); - mappings.add([modes.TEXT_EDIT], - ["D"], "Delete the characters under the cursor until the end of the line", - function () { editor.executeCommand("cmd_deleteToEndOfLine"); }); + bind(["D"], "Delete the characters under the cursor until the end of the line", + function () { editor.executeCommand("cmd_deleteToEndOfLine"); }); mappings.add([modes.TEXT_EDIT], ["o"], "Open line below current", @@ -711,14 +681,12 @@ var Editor = Module("editor", { editor.executeCommand("cmd_linePrevious", 1); }); - mappings.add([modes.TEXT_EDIT], - ["X"], "Delete character to the left", - function (args) { editor.executeCommand("cmd_deleteCharBackward", Math.max(args.count, 1)); }, + bind(["X"], "Delete character to the left", + function (args) { editor.executeCommand("cmd_deleteCharBackward", Math.max(args.count, 1)); }, { count: true }); - mappings.add([modes.TEXT_EDIT], - ["x"], "Delete character to the right", - function (args) { editor.executeCommand("cmd_deleteCharForward", Math.max(args.count, 1)); }, + bind(["x"], "Delete character to the right", + function (args) { editor.executeCommand("cmd_deleteCharForward", Math.max(args.count, 1)); }, { count: true }); // visual mode @@ -764,51 +732,58 @@ var Editor = Module("editor", { dactyl.clipboardWrite(buffer.currentWord, true); }); - mappings.add([modes.VISUAL, modes.TEXT_EDIT], - ["p"], "Paste clipboard contents", - function ({ count }) { + bind(["p"], "Paste clipboard contents", + function ({ count }) { dactyl.assert(!editor.isCaret); editor.executeCommand("cmd_paste", count || 1); modes.pop(modes.TEXT_EDIT); }, { count: true }); - // finding characters - mappings.add([modes.TEXT_EDIT, modes.VISUAL], - ["f"], "Move to a character on the current line after the cursor", - function ({ arg, count }) { - let pos = editor.findChar(arg, Math.max(count, 1)); - if (pos >= 0) - editor.moveToPosition(pos, true, modes.main == modes.VISUAL); - }, - { arg: true, count: true }); + let bind = function bind(names, description, action, params) + mappings.add([modes.OPERATOR], names, description, + action, update({ type: "editor" }, params)); - mappings.add([modes.TEXT_EDIT, modes.VISUAL], - ["F"], "Move to a character on the current line before the cursor", - function ({ arg, count }) { - let pos = editor.findChar(arg, Math.max(count, 1), true); - if (pos >= 0) - editor.moveToPosition(pos, false, modes.main == modes.VISUAL); - }, - { arg: true, count: true }); - - mappings.add([modes.TEXT_EDIT, modes.VISUAL], - ["t"], "Move before a character on the current line", - function ({ arg, count }) { - let pos = editor.findChar(arg, Math.max(count, 1)); - if (pos >= 0) - editor.moveToPosition(pos - 1, true, modes.main == modes.VISUAL); - }, - { arg: true, count: true }); + // finding characters + function offset(backward, before, pos) { + if (!backward && modes.main != modes.TEXT_EDIT) + pos += 1; + if (before) + pos += backward ? +1 : -1; + return pos; + } - mappings.add([modes.TEXT_EDIT, modes.VISUAL], - ["T"], "Move before a character on the current line, backwards", - function ({ arg, count }) { - let pos = editor.findChar(arg, Math.max(count, 1), true); - if (pos >= 0) - editor.moveToPosition(pos + 1, false, modes.main == modes.VISUAL); - }, - { arg: true, count: true }); + bind(["f"], "Move to a character on the current line after the cursor", + function ({ arg, count }) { + let pos = editor.findChar(arg, Math.max(count, 1)); + if (pos >= 0) + editor.moveToPosition(offset(false, false, pos), true, modes.main == modes.VISUAL); + }, + { arg: true, count: true, type: "operator" }); + + bind(["F"], "Move to a character on the current line before the cursor", + function ({ arg, count }) { + let pos = editor.findChar(arg, Math.max(count, 1), true); + if (pos >= 0) + editor.moveToPosition(offset(true, false, pos), false, modes.main == modes.VISUAL); + }, + { arg: true, count: true, type: "operator" }); + + bind(["t"], "Move before a character on the current line", + function ({ arg, count }) { + let pos = editor.findChar(arg, Math.max(count, 1)); + if (pos >= 0) + editor.moveToPosition(offset(false, true, pos), true, modes.main == modes.VISUAL); + }, + { arg: true, count: true, type: "operator" }); + + bind(["T"], "Move before a character on the current line, backwards", + function ({ arg, count }) { + let pos = editor.findChar(arg, Math.max(count, 1), true); + if (pos >= 0) + editor.moveToPosition(offset(true, true, pos), false, modes.main == modes.VISUAL); + }, + { arg: true, count: true, type: "operator" }); // text edit and visual mode mappings.add([modes.TEXT_EDIT, modes.VISUAL], @@ -834,8 +809,8 @@ var Editor = Module("editor", { }, { count: true }); - function bind() mappings.add.apply(mappings, - [[modes.AUTOCOMPLETE]].concat(Array.slice(arguments))) + let bind = function bind() mappings.add.apply(mappings, + [[modes.AUTOCOMPLETE]].concat(Array.slice(arguments))) bind(["<Esc>"], "Return to Insert mode", function () Events.PASS_THROUGH); diff --git a/common/content/mappings.js b/common/content/mappings.js index 155849e4..c58ba3eb 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -17,7 +17,7 @@ * *action*. * @param {string} description A short one line description of the key mapping. * @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. * arg - see {@link Map#arg} * count - see {@link Map#count} @@ -29,7 +29,7 @@ * @private */ var Map = Class("Map", { - init: function (modes, keys, description, action, extraInfo) { + init: function (modes, keys, description, action, info) { this.id = ++Map.id; this.modes = modes; this._keys = keys; @@ -38,8 +38,11 @@ var Map = Class("Map", { Object.freeze(this.modes); - if (extraInfo) - this.update(extraInfo); + if (info) { + if (Set.has(Map.types, info.type)) + this.update(Map.types[info.type]); + this.update(info); + } }, name: Class.memoize(function () this.names[0]), @@ -69,12 +72,24 @@ var Map = Class("Map", { * as an argument. */ motion: false, + /** @property {boolean} Whether the RHS of the mapping should expand mappings recursively. */ 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. */ silent: false, + /** @property {string} The literal RHS expansion of this mapping. */ rhs: null, + + /** @property {string} The type of this mapping. */ + type: "", + /** * @property {boolean} Specifies whether this is a user mapping. User * 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)); try { + this.preExecute(args); this.executing = true; var res = repeat(); } @@ -127,12 +143,15 @@ var Map = Class("Map", { } finally { this.executing = false; + this.postExecute(args); } return res; } }, { - id: 0 + id: 0, + + types: {} }); var MapHive = Class("MapHive", Contexts.Hive, { diff --git a/common/content/modes.js b/common/content/modes.js index c4988309..1bc25320 100644 --- a/common/content/modes.js +++ b/common/content/modes.js @@ -57,11 +57,16 @@ var Modes = Module("modes", { description: "Active when nothing is focused", bases: [this.COMMAND] }); + this.addMode("OPERATOR", { + char: "o", + description: "Mappings which move the cursor", + bases: [] + }); this.addMode("VISUAL", { char: "v", description: "Active when text is selected", display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : ""), - bases: [this.COMMAND], + bases: [this.COMMAND, this.OPERATOR], ownsFocus: true }, { leave: function (stack, newMode) { @@ -97,7 +102,7 @@ var Modes = Module("modes", { this.addMode("TEXT_EDIT", { char: "t", description: "Vim-like editing of input elements", - bases: [this.COMMAND], + bases: [this.OPERATOR, this.COMMAND], ownsFocus: true }, { onKeyPress: function (eventList) { @@ -489,7 +494,7 @@ var Modes = Module("modes", { 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++, @@ -647,7 +652,7 @@ var Modes = Module("modes", { 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() { diff --git a/common/locale/en-US/map.xml b/common/locale/en-US/map.xml index 8921e811..ba7c88a7 100644 --- a/common/locale/en-US/map.xml +++ b/common/locale/en-US/map.xml @@ -56,6 +56,7 @@ <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>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> <p> diff --git a/common/modules/finder.jsm b/common/modules/finder.jsm index e2d24bb0..97930350 100644 --- a/common/modules/finder.jsm +++ b/common/modules/finder.jsm @@ -774,7 +774,14 @@ var RangeFind = Class("RangeFind", { } 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); } diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS index af997052..8615b541 100644 --- a/pentadactyl/NEWS +++ b/pentadactyl/NEWS @@ -74,6 +74,7 @@ • Mapping changes: - It's now possible to map keys in many more modes, including Hint, Multi-line Output, and Menu. [b4] + - Added Operator mode for motion maps, per Vim. [b8] - Added site-specific mapping groups and related command changes. [b6] - Added 'timeout' and 'timeoutlen' options. [b6] |