1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-23 09:08:37 +01:00

Register support with a crude kill ring.

This commit is contained in:
Kris Maglione
2011-10-06 01:02:11 -04:00
parent 09a3bfcaac
commit 1b781416c9
10 changed files with 255 additions and 50 deletions

View File

@@ -11,7 +11,7 @@
// http://developer.mozilla.org/en/docs/Editor_Embedding_Guide
/** @instance editor */
var Editor = Module("editor", {
var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
init: function init(elem) {
if (elem)
this.element = elem;
@@ -26,6 +26,97 @@ var Editor = Module("editor", {
});
},
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 }),
// Fixme: Move off this object.
currentRegister: null,
defaultRegister: "*",
selectionRegisters: {
"*": "selection",
"+": "global"
},
/**
* Get the value of the register *name*.
*
* @param {string|number} name The name of the register to get.
* @returns {string|null}
* @see #setRegister
*/
getRegister: function getRegister(name) {
if (name == null)
name = editor.currentRegister || editor.defaultRegister;
if (name == '"')
name = 0;
if (name == "_")
var res = null;
else if (Set.has(this.selectionRegisters, name))
res = { text: dactyl.clipboardRead(this.selectionRegisters[name]) || "" };
else if (!/^[0-9]$/.test(name))
res = this.registers.get(name);
else
res = this.registerRing.get(name);
return res != null ? res.text : res;
},
/**
* Sets the value of register *name* to value. The following
* registers have special semantics:
*
* * - Tied to the PRIMARY selection value on X11 systems.
* + - Tied to the primary global clipboard.
* _ - The null register. Never has any value.
* " - Equivalent to 0.
* 0-9 - These act as a kill ring. Setting any of them pushes the
* values of higher numbered registers up one slot.
*
* @param {string|number} name The name of the register to set.
* @param {string|Range|Selection|Node} value The value to save to
* the register.
*/
setRegister: function setRegister(name, value) {
if (name == null)
name = editor.currentRegister || editor.defaultRegister;
if (isinstance(value, [Ci.nsIDOMRange, Ci.nsIDOMNode, Ci.nsISelection]))
value = DOM.stringify(value);
value = { text: value, isLine: modes.extended & modes.LINE };
if (name == '"')
name = 0;
if (name == "_")
;
else if (Set.has(this.selectionRegisters, name))
dactyl.clipboardWrite(value.text, false, this.selectionRegisters[name]);
else if (!/^[0-9]$/.test(name))
this.registers.set(name, value);
else {
this.registerRing.insert(value, name);
this.registerRing.truncate(10);
}
},
get isCaret() modes.getStack(1).main == modes.CARET,
get isTextEdit() modes.getStack(1).main == modes.TEXT_EDIT,
@@ -72,30 +163,44 @@ var Editor = Module("editor", {
this.editor.setShouldTxnSetSelection(!val);
},
pasteClipboard: function pasteClipboard(clipboard) {
let elem = this.element;
copy: function copy(range, name) {
range = range || this.selection;
let text = dactyl.clipboardRead(clipboard);
if (!range.collapsed)
this.setRegister(name, range);
},
cut: function cut(range, name) {
if (range)
this.selectedRange = range;
if (!this.selection.isCollapsed)
this.setRegister(name, this.selection);
this.editor.deleteSelection(0);
},
paste: function paste(name) {
let text = this.getRegister(name);
dactyl.assert(text && this.editor instanceof Ci.nsIPlaintextEditor);
this.editor.insertText(text);
},
// count is optional, defaults to 1
executeCommand: function (cmd, count) {
let controller = this.getController(cmd);
dactyl.assert(callable(cmd) ||
controller &&
controller.supportsCommand(cmd) &&
controller.isCommandEnabled(cmd));
executeCommand: function executeCommand(cmd, count) {
if (!callable(cmd)) {
var controller = this.getController(cmd);
util.assert(controller &&
controller.supportsCommand(cmd) &&
controller.isCommandEnabled(cmd));
cmd = bind("doCommand", controller, cmd);
}
// XXX: better as a precondition
if (count == null)
count = 1;
if (!callable(cmd))
cmd = bind("doCommand", controller, cmd);
let didCommand = false;
while (count--) {
// some commands need this try/catch workaround, because a cmd_charPrevious triggered
@@ -363,7 +468,21 @@ var Editor = Module("editor", {
range.setStart(range.startContainer, range.endOffset - abbrev.lhs.length);
this.mungeRange(range, function () abbrev.expand(this.element), true);
}
}
},
// nsIEditActionListener:
WillDeleteNode: util.wrapCallback(function WillDeleteNode(node) {
if (node.textContent)
this.setRegister(0, node);
}),
WillDeleteSelection: util.wrapCallback(function WillDeleteSelection(selection) {
if (!selection.isCollapsed)
this.setRegister(0, selection);
}),
WillDeleteText: util.wrapCallback(function WillDeleteText(node, start, length) {
if (length)
this.setRegister(0, node.textContent.substr(start, length));
})
}, {
TextsIterator: Class("TextsIterator", {
init: function init(range, context, after) {
@@ -575,6 +694,38 @@ var Editor = Module("editor", {
bases: [modes.INSERT]
});
},
commands: function init_commands() {
commands.add(["reg[isters]"],
"List the contents of known registers",
function (args) {
completion.listCompleter("register", args[0]);
},
{ argCount: "*" });
},
completion: function init_completion() {
completion.register = function complete_register(context) {
context = context.fork("registers");
context.keys = { text: util.identity, description: editor.closure.getRegister };
context.match = function (r) !this.filter || ~this.filter.indexOf(r);
context.fork("clipboard", 0, this, function (ctxt) {
ctxt.match = context.match;
ctxt.title = ["Clipboard Registers"];
ctxt.completions = Object.keys(editor.selectionRegisters);
});
context.fork("kill-ring", 0, this, function (ctxt) {
ctxt.match = context.match;
ctxt.title = ["Kill Ring Registers"];
ctxt.completions = Array.slice("0123456789");
});
context.fork("user", 0, this, function (ctxt) {
ctxt.match = context.match;
ctxt.title = ["User Defined Registers"];
ctxt.completions = editor.registers.keys();
});
};
},
mappings: function init_mappings() {
Map.types["editor"] = {
@@ -676,9 +827,9 @@ var Editor = Module("editor", {
editor.executeCommand("cmd_selectLineNext");
}
function updateRange(editor, forward, re, modify) {
function updateRange(editor, forward, re, modify, sameWord) {
let range = Editor.extendRange(editor.selection.getRangeAt(0),
forward, re, false, editor.rootElement);
forward, re, sameWord, editor.rootElement);
modify(range);
editor.selection.removeAllRanges();
editor.selection.addRange(range);
@@ -694,10 +845,11 @@ var Editor = Module("editor", {
parent.input();
}
function move(forward, re)
function move(forward, re, sameWord)
function _move(editor) {
updateRange(editor, forward, re,
function (range) { range.collapse(!forward); });
function (range) { range.collapse(!forward); },
sameWord);
}
function select(forward, re)
function _select(editor) {
@@ -706,7 +858,7 @@ var Editor = Module("editor", {
}
function beginLine(editor_) {
editor.executeCommand("cmd_beginLine");
move(true, /\S/)(editor_);
move(true, /\s/, true)(editor_);
}
// COUNT CARET TEXT_EDIT VISUAL_TEXT_EDIT
@@ -769,6 +921,7 @@ var Editor = Module("editor", {
function ({ command, count, motion }) {
let start = editor.selectedRange.cloneRange();
editor._currentMap = null;
modes.push(modes.OPERATOR, null, {
forCommand: command,
@@ -786,6 +939,7 @@ var Editor = Module("editor", {
doTxn(range, editor);
});
editor.currentRegister = null;
modes.delay(function () {
if (mode)
modes.push(mode);
@@ -804,9 +958,9 @@ var Editor = Module("editor", {
{ count: true, type: "motion" });
}
addMotionMap(["d", "x"], "Delete text", true, function (editor) { editor.editor.cut(); });
addMotionMap(["c"], "Change text", true, function (editor) { editor.editor.cut(); }, modes.INSERT);
addMotionMap(["y"], "Yank text", false, function (editor, range) { dactyl.clipboardWrite(String(range)) });
addMotionMap(["d", "x"], "Delete text", true, function (editor) { editor.cut(); });
addMotionMap(["c"], "Change text", true, function (editor) { editor.cut(); }, modes.INSERT);
addMotionMap(["y"], "Yank text", false, function (editor, range) { editor.copy(range); });
addMotionMap(["gu"], "Lowercase text", false,
function (editor, range) {
@@ -822,13 +976,13 @@ var Editor = Module("editor", {
["c", "d", "y"], "Select the entire line",
function ({ command, count }) {
dactyl.assert(command == modes.getStack(0).params.forCommand);
editor.executeCommand("cmd_beginLine", 1);
editor.executeCommand("cmd_selectLineNext", count || 1);
let range = editor.selectedRange;
if (command == "c" && !range.collapsed) // Hack.
if (range.endContainer instanceof Text &&
range.endContainer.textContent[range.endOffset - 1] == "\n")
editor.executeCommand("cmd_selectCharPrevious", 1);
let sel = editor.selection;
sel.modify("move", "backward", "lineboundary");
sel.modify("extend", "forward", "lineboundary");
if (command != "c")
sel.modify("extend", "forward", "character");
},
{ count: true, type: "operator" });
@@ -875,7 +1029,7 @@ var Editor = Module("editor", {
function () { editor.executeCommand("cmd_deleteCharForward", 1); });
bind(["<S-Insert>"], "Insert clipboard/selection",
function () { editor.pasteClipboard(); });
function () { editor.paste(); });
mappings.add([modes.INPUT],
["<C-i>"], "Edit text field with an external editor",
@@ -1005,11 +1159,25 @@ var Editor = Module("editor", {
bind(["p"], "Paste clipboard contents",
function ({ count }) {
dactyl.assert(!editor.isCaret);
editor.executeCommand("cmd_paste", count || 1);
modes.pop(modes.TEXT_EDIT);
editor.executeCommand(modules.bind("paste", editor, null),
count || 1);
},
{ count: true });
mappings.add([modes.TEXT_EDIT, modes.VISUAL],
['"'], "Bind a register to the next command",
function ({ arg }) {
editor.currentRegister = arg;
},
{ arg: true });
mappings.add([modes.INPUT],
["<C-'>", '<C-">'], "Bind a register to the next command",
function ({ arg }) {
editor.currentRegister = arg;
},
{ arg: true });
let bind = function bind(names, description, action, params)
mappings.add([modes.TEXT_EDIT, modes.OPERATOR, modes.VISUAL],
names, description,