diff --git a/common/content/editor.js b/common/content/editor.js index 28611ebf..9a6c7076 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -26,22 +26,6 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { }); }, - signals: { - "mappings.willExecute": function mappings_willExecute(map) { - if (this.currentRegister && !(this._currentMap && this._wait == util.yielders)) { - this._currentMap = map; - this._wait = util.yielders; - } - }, - "mappings.executed": function mappings_executed(map) { - if (this._currentMap == map) { - this.currentRegister = null; - this._wait = util.yielders; - this._currentMap = null; - } - } - }, - get registers() storage.newMap("registers", { privateData: true, store: true }), get registerRing() storage.newArray("register-ring", { privateData: true, store: true }), @@ -50,6 +34,18 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { // Fixme: Move off this object. currentRegister: null, + /** + * Temporarily set the default register for the span of the next + * mapping. + */ + pushRegister: function pushRegister(arg) { + let restore = this.currentRegister; + this.currentRegister = arg; + mappings.afterCommands(2, function () { + this.currentRegister = restore; + }, this); + }, + defaultRegister: "*", selectionRegisters: { @@ -97,7 +93,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { * @param {string|Range|Selection|Node} value The value to save to * the register. */ - setRegister: function setRegister(name, value) { + setRegister: function setRegister(name, value, verbose) { if (name == null) name = editor.currentRegister || editor.defaultRegister; @@ -110,7 +106,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { if (name == "_") ; else if (Set.has(this.selectionRegisters, name)) - dactyl.clipboardWrite(value.text, false, this.selectionRegisters[name]); + dactyl.clipboardWrite(value.text, verbose, this.selectionRegisters[name]); else if (!/^[0-9]$/.test(name)) this.registers.set(name, value); else { @@ -608,21 +604,21 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { } }), - extendRange: function extendRange(range, forward, re, sameWord, root) { + extendRange: function extendRange(range, forward, re, sameWord, root, end) { function advance(positive) { while (true) { while (idx == text.length && (node = iterator.getNext())) { if (node == iterator.start) - idx = range.endOffset; + idx = range[offset]; - offset = text.length; + start = text.length; text += node.textContent; - range.setEnd(node, idx - offset); + range[set](node, idx - start); } if (idx >= text.length || re.test(text[idx]) != positive) break; - range.setEnd(range.endContainer, ++idx - offset); + range[set](range[container], ++idx - start); } } function retreat(positive) { @@ -630,21 +626,26 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { while (idx == 0 && (node = iterator.getPrev())) { let str = node.textContent; if (node == iterator.start) - idx = range.startOffset; + idx = range[offset]; else idx = str.length; text = str + text; - range.setStart(node, idx); + range[set](node, idx); } if (idx == 0 || re.test(text[idx - 1]) != positive) break; - range.setStart(range.startContainer, --idx); + range[set](range[container], --idx); } } + if (end == null) + end = forward ? "end" : "start"; + let [container, offset, set] = [end + "Container", end + "Offset", + "set" + util.capitalize(end)]; + if (!root) - for (root = range.startContainer; + for (root = range[container]; root.parentNode instanceof Element && !DOM(root).isEditable; root = root.parentNode) ; @@ -653,13 +654,13 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { if (root instanceof Ci.nsIEditor) root = root.rootElement; - let node = range[forward ? "endContainer" : "startContainer"]; + let node = range[container]; let iterator = Editor.TextsIterator(RangeFind.nodeContents(root), node, !forward); let text = ""; let idx = 0; - let offset = 0; + let start = 0; if (forward) { advance(true); @@ -886,11 +887,17 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { } function updateRange(editor, forward, re, modify, sameWord) { - let range = Editor.extendRange(editor.selection.getRangeAt(0), - forward, re, sameWord, editor.rootElement); + let sel = editor.selection; + let range = sel.getRangeAt(0); + + let end = range.endContainer == sel.focusNode && range.endOffset == sel.focusOffset; + if (range.collapsed) + end = forward; + + Editor.extendRange(range, forward, re, sameWord, + editor.rootElement, end ? "end" : "start"); modify(range); - editor.selection.removeAllRanges(); - editor.selection.addRange(range); + editor.selectionController.repaintSelection(editor.selectionController.SELECTION_NORMAL); } function clear(forward, re) @@ -979,29 +986,35 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { function ({ command, count, motion }) { let start = editor.selectedRange.cloneRange(); - editor._currentMap = null; + mappings.pushCommand(); modes.push(modes.OPERATOR, null, { forCommand: command, count: count, leave: function leave(stack) { - if (stack.push || stack.fromEscape) - return; + try { + if (stack.push || stack.fromEscape) + return; - editor.withSavedValues(["inEditMap"], function () { - this.inEditMap = true; + editor.withSavedValues(["inEditMap"], function () { + this.inEditMap = true; - let range = RangeFind.union(start, editor.selectedRange); - editor.selectedRange = select ? range : start; - doTxn(range, editor); - }); + let range = RangeFind.union(start, editor.selectedRange); + editor.selectedRange = select ? range : start; + doTxn(range, editor); + }); - editor.currentRegister = null; - modes.delay(function () { - if (mode) - modes.push(mode); - }); + editor.currentRegister = null; + modes.delay(function () { + if (mode) + modes.push(mode); + }); + } + finally { + if (!stack.push) + mappings.popCommand(); + } } }); }, @@ -1011,7 +1024,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { desc, function ({ count, motion }) { dactyl.assert(caretOk || editor.isTextEdit); - if (modes.isTextEdit) + if (editor.isTextEdit) doTxn(editor.selectedRange, editor); else cmd(editor, buffer.selection.getRangeAt(0)); @@ -1232,14 +1245,14 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { mappings.add([modes.COMMAND], ['"'], "Bind a register to the next command", function ({ arg }) { - editor.currentRegister = arg; + editor.pushRegister(arg); }, { arg: true }); mappings.add([modes.INPUT], ["", ''], "Bind a register to the next command", function ({ arg }) { - editor.currentRegister = arg; + editor.pushRegister(arg); }, { arg: true }); diff --git a/common/content/mappings.js b/common/content/mappings.js index f1293e3f..c6bb5230 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -135,6 +135,7 @@ var Map = Class("Map", { try { dactyl.triggerObserver("mappings.willExecute", this, args); + mappings.pushCommand(); this.preExecute(args); this.executing = true; var res = repeat(); @@ -145,6 +146,7 @@ var Map = Class("Map", { } finally { this.executing = false; + mappings.popCommand(); this.postExecute(args); dactyl.triggerObserver("mappings.executed", this, args); } @@ -320,6 +322,31 @@ var MapHive = Class("MapHive", Contexts.Hive, { */ var Mappings = Module("mappings", { init: function () { + this.watches = []; + this._watchStack = 0; + this._yielders = 0; + }, + + afterCommands: function afterCommands(count, cmd, self) { + this.watches.push([cmd, self, Math.max(this._watchStack - 1, 0), count || 1]); + }, + + pushCommand: function pushCommand(cmd) { + this._watchStack++; + this._yielders = util.yielders; + }, + popCommand: function popCommand(cmd) { + this._watchStack = Math.max(this._watchStack - 1, 0); + if (util.yielders > this._yielders) + this._watchStack = 0; + + this.watches = this.watches.filter(function (elem) { + if (elem[2] <= this._watchStack) + elem[3]--; + if (elem[3] <= 0) + elem[0].call(elem[1] || null); + return elem[3] > 0; + }, this); }, repeat: Modes.boundProperty(), diff --git a/common/content/tabs.js b/common/content/tabs.js index 0aaa41c9..6df15a8a 100644 --- a/common/content/tabs.js +++ b/common/content/tabs.js @@ -54,16 +54,9 @@ var Tabs = Module("tabs", { enter: function enter() { if (window.TabsInTitlebar) window.TabsInTitlebar.allowedBy("dactyl", true); - }, - - "mappings.executed": function mappings_executed() { - if (this._mappingCount && !--this._mappingCount) - dactyl.forceTarget = null; - }, + } }, - _mappingCount: 0, - _alternates: Class.Memoize(function () [config.tabbrowser.mCurrentTab, null]), cleanup: function cleanup() { @@ -1116,8 +1109,10 @@ var Tabs = Module("tabs", { mappings.add([modes.COMMAND], ["", ""], "Execute the next mapping in a new tab", function ({ count }) { - tabs._mappingCount = (count || 1) + 1; dactyl.forceTarget = dactyl.NEW_TAB; + mappings.afterCommands((count || 1) + 1, function () { + dactyl.forceTarget = null; + }); }, { count: true }); diff --git a/common/modules/buffer.jsm b/common/modules/buffer.jsm index 184d9cda..19c5cb3d 100644 --- a/common/modules/buffer.jsm +++ b/common/modules/buffer.jsm @@ -1247,7 +1247,7 @@ var Buffer = Module("Buffer", { return ""; let range = selection.getRangeAt(0).cloneRange(); - if (range.collapsed && range.startContainer instanceof Ci.nsIDOMText) { + if (range.collapsed) { let re = options.get("iskeyword").regexp; Editor.extendRange(range, true, re, true); Editor.extendRange(range, false, re, true); @@ -1824,7 +1824,7 @@ var Buffer = Module("Buffer", { events.listen(config.browser, "scroll", buffer.closure._updateBufferPosition, false); }, mappings: function initMappings(dactyl, modules, window) { - let { Editor, Events, buffer, events, ex, mappings, modes, options, tabs } = modules; + let { Editor, Events, buffer, editor, events, ex, mappings, modes, options, tabs } = modules; mappings.add([modes.NORMAL], ["y", ""], "Yank current location to the clipboard", @@ -2094,7 +2094,7 @@ var Buffer = Module("Buffer", { function () { let sel = buffer.currentWord; dactyl.assert(sel); - dactyl.clipboardWrite(sel, true); + editor.setRegister(null, sel, true); }); // zooming