From a7cccae0393cefd52b72f115e2007e5b0e61d922 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 17:14:06 -0700 Subject: [PATCH 01/16] fixing emacs multiselect cut/copy/paste --- lib/ace/keyboard/emacs.js | 74 +++++++++++++++++++++++---------------- lib/ace/multi_select.js | 6 ++-- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 87423711..136ec81b 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -367,28 +367,32 @@ exports.handler.addCommands({ selectRectangularRegion: function(editor) { editor.multiSelect.toggleBlockSelection(); }, - setMark: function(editor) { - // Emulate emacs highlighting behaviour in transient-mark-mode. - // Sets mark-mode and clears current selection. - // When mark is set, keyboard cursor movement commands become - // selection modification commands. That is, - // "goto" commands become "select" commands. - // Any insertion or mouse click resets mark-mode. - // setMark twice in a row at the same place resets markmode - var markMode = editor.emacsMarkMode(); - if (markMode) { - var cp = editor.getCursorPosition(); - if (editor.selection.isEmpty() && - markMode.row == cp.row && markMode.column == cp.column) { - editor.setEmacsMarkMode(null); - // console.log("Mark mode off"); - return; + setMark: { + exec: function(editor) { + // Emulate emacs highlighting behaviour in transient-mark-mode. + // Sets mark-mode and clears current selection. + // When mark is set, keyboard cursor movement commands become + // selection modification commands. That is, + // "goto" commands become "select" commands. + // Any insertion or mouse click resets mark-mode. + // setMark twice in a row at the same place resets markmode + var markMode = editor.emacsMarkMode(); + if (markMode) { + var cp = editor.getCursorPosition(); + if (editor.selection.isEmpty() && + markMode.row == cp.row && markMode.column == cp.column) { + editor.setEmacsMarkMode(null); + // console.log("Mark mode off"); + return; + } } - } - // turn on mark mode - markMode = editor.getCursorPosition(); - editor.setEmacsMarkMode(markMode); - editor.selection.setSelectionAnchor(markMode.row, markMode.column); + // turn on mark mode + markMode = editor.getCursorPosition(); + editor.setEmacsMarkMode(markMode); + editor.selection.setSelectionAnchor(markMode.row, markMode.column); + }, + readonly: true, + multiSelectAction: "forEach" }, exchangePointAndMark: { exec: function(editor) { @@ -396,7 +400,7 @@ exports.handler.addCommands({ editor.selection.setSelectionRange(range, !editor.selection.isBackwards()); }, readonly: true, - multiselectAction: "forEach" + multiSelectAction: "forEach" }, killWord: { exec: function(editor, dir) { @@ -413,7 +417,7 @@ exports.handler.addCommands({ editor.session.remove(range); editor.clearSelection(); }, - multiselectAction: "forEach" + multiSelectAction: "forEach" }, killLine: function(editor) { editor.setEmacsMarkMode(null); @@ -438,7 +442,7 @@ exports.handler.addCommands({ editor.clearSelection(); }, yank: function(editor) { - editor.onPaste(exports.killRing.get()); + editor.onPaste(exports.killRing.get() || ''); editor.keyBinding.$data.lastCommand = "yank"; }, yankRotate: function(editor) { @@ -448,12 +452,19 @@ exports.handler.addCommands({ editor.onPaste(exports.killRing.rotate()); editor.keyBinding.$data.lastCommand = "yank"; }, - killRegion: function(editor) { - exports.killRing.add(editor.getCopyText()); - editor.commands.byName.cut.exec(editor); + killRegion: { + exec: function(editor) { + exports.killRing.add(editor.getCopyText()); + editor.commands.byName.cut.exec(editor); + }, + readonly: true, + multiSelectAction: "forEach" }, - killRingSave: function(editor) { - exports.killRing.add(editor.getCopyText()); + killRingSave: { + exec: function(editor) { + exports.killRing.add(editor.getCopyText()); + }, + readonly: true }, keyboardQuit: function(editor) { editor.selection.clearSelection(); @@ -478,8 +489,9 @@ exports.killRing = { if (this.$data.length > 30) this.$data.shift(); }, - get: function() { - return this.$data[this.$data.length - 1] || ""; + get: function(n) { + n = n || 1; + return this.$data.slice(this.$data.length-n, this.$data.length).reverse().join('\n'); }, pop: function() { if (this.$data.length > 1) diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index d00bfb05..86fc5475 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -521,7 +521,7 @@ var Editor = require("./editor").Editor; this.getCopyText = function() { var text = ""; - if (this.inMultiSelectMode) { + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { var ranges = this.multiSelect.rangeList.ranges; var buf = []; for (var i = 0; i < ranges.length; i++) { @@ -550,10 +550,10 @@ var Editor = require("./editor").Editor; var lines = text.split(/\r\n|\r|\n/); var ranges = this.selection.rangeList.ranges; - if (lines.length > ranges.length || (lines.length <= 2 && !lines[1])) + if (lines.length > ranges.length || lines.length < 2 || !lines[1]) return this.commands.exec("insertstring", this, text); - for (var i = ranges.length; i--; ) { + for (var i = ranges.length; i--;) { var range = ranges[i]; if (!range.isEmpty()) this.session.remove(range); From 259d48ea1a0c0a1989cbde046c74e513f0125aac Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 17:51:43 -0700 Subject: [PATCH 02/16] fix exitMultiSelectMode --- lib/ace/editor.js | 2 +- lib/ace/multi_select.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 5ddd14f2..62a4c37f 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -1800,7 +1800,7 @@ var Editor = function(renderer, session) { this.$blockScrolling += 1; // todo: find a way to automatically exit multiselect mode - this.exitMultiSelectMode && this.exitMultiSelectMode() + this.exitMultiSelectMode && this.exitMultiSelectMode(); this.moveCursorTo(lineNumber - 1, column || 0); this.$blockScrolling -= 1; diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index 86fc5475..2de7fc1d 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -514,7 +514,7 @@ var Editor = require("./editor").Editor; * @method Editor.exitMultiSelectMode **/ this.exitMultiSelectMode = function() { - if (this.inVirtualSelectionMode) + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) return; this.multiSelect.toSingleRange(); }; From ee76ccad197074bac96df9fd58467581b2980e5a Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 18:52:57 -0700 Subject: [PATCH 03/16] emacs: add contents of copy/cut editor events to killring --- lib/ace/keyboard/emacs.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 136ec81b..88e877e7 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -112,6 +112,8 @@ exports.handler.attach = function(editor) { editor.commands.addCommands(commands); exports.handler.platform = editor.commands.platform; editor.$emacsModeHandler = this; + editor.addEventListener('copy', this.onCopy); + editor.addEventListener('paste', this.onPaste); }; exports.handler.detach = function(editor) { @@ -122,6 +124,8 @@ exports.handler.detach = function(editor) { editor.removeEventListener("changeSession", $kbSessionChange); editor.unsetStyle("emacs-mode"); editor.commands.removeCommands(commands); + editor.removeEventListener('copy', this.onCopy); + editor.removeEventListener('paste', this.onPaste); }; var $kbSessionChange = function(e) { @@ -157,6 +161,13 @@ combinations.forEach(function(c) { eMods[hashId] = c.toLowerCase() + "-"; }); +exports.handler.onCopy = function(e, editor) { + if (editor.$handlesEmacsOnCopy) return; + editor.$handlesEmacsOnCopy = true; + exports.handler.commands.killRingSave.exec(editor); + delete editor.$handlesEmacsOnCopy; +} + exports.handler.bindKey = function(key, command) { if (!key) return; From 0a774fbb37f510f4ac2c9de1a8d77ea685050e5d Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 18:53:52 -0700 Subject: [PATCH 04/16] emacs: deactivate mark on kill-ring-save --- lib/ace/keyboard/emacs.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 88e877e7..71d4d0b7 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -474,6 +474,12 @@ exports.handler.addCommands({ killRingSave: { exec: function(editor) { exports.killRing.add(editor.getCopyText()); + (function() { + var sel = editor.selection, + range = sel.getRange(); + editor.pushEmacsMark(sel.isBackwards() ? range.end : range.start); + sel.clearSelection(); + }).delay(0); }, readonly: true }, From 1172f5cb3d7f70b08e6c1cb9ad23e9680c4c5fe1 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 18:55:38 -0700 Subject: [PATCH 05/16] emacs: adding mark-ring --- lib/ace/keyboard/emacs.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 71d4d0b7..f0df7d87 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -96,15 +96,31 @@ exports.handler.attach = function(editor) { editor.session.$useEmacsStyleLineStart = true; editor.session.$emacsMark = null; + editor.session.$emacsMarkRing = editor.session.$emacsMarkRing || []; editor.emacsMarkMode = function() { return this.session.$emacsMark; } editor.setEmacsMarkMode = function(p) { + var prevMark = this.session.$emacsMark; + if (prevMark) + this.session.$emacsMarkRing.push(prevMark); this.session.$emacsMark = p; } + editor.pushEmacsMark = function(p, activate) { + var prevMark = this.session.$emacsMark; + if (prevMark) + this.session.$emacsMarkRing.push(prevMark); + if (activate) this.session.$emacsMark = p; + else this.session.$emacsMarkRing.push(p); + } + + editor.getLastEmacsMark = function(p) { + return this.session.$emacsMark || this.session.$emacsMarkRing.reverse()[0]; + } + editor.on("click", $resetMarkMode); editor.on("changeSession", $kbSessionChange); editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates; @@ -141,6 +157,8 @@ var $kbSessionChange = function(e) { if (!e.session.hasOwnProperty('$emacsMark')) e.session.$emacsMark = null; + if (!e.session.hasOwnProperty('$emacsMarkRing')) + e.session.$emacsMarkRing = []; } var $resetMarkMode = function(e) { @@ -168,6 +186,10 @@ exports.handler.onCopy = function(e, editor) { delete editor.$handlesEmacsOnCopy; } +exports.handler.onPaste = function(e, editor) { + editor.pushEmacsMark(editor.getCursorPosition()); +} + exports.handler.bindKey = function(key, command) { if (!key) return; From 353958b98cdc21f64c77105defb5ec84c7e73124 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 18:56:13 -0700 Subject: [PATCH 06/16] emacs: exchangePointAndMark uses last mark if selection empty --- lib/ace/keyboard/emacs.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index f0df7d87..25f22a52 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -429,7 +429,13 @@ exports.handler.addCommands({ }, exchangePointAndMark: { exec: function(editor) { - var range = editor.selection.getRange(); + var sel = editor.selection; + var range = sel.getRange(); + if (range.isEmpty()) { + var lastMark = editor.getLastEmacsMark(); + sel.selectToPosition(lastMark); + return; + } editor.selection.setSelectionRange(range, !editor.selection.isBackwards()); }, readonly: true, From 6b01dde66615527813b544b9bfa386ac926e6031 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 21:01:33 -0700 Subject: [PATCH 07/16] emacs: use mark ring instead of single mark --- lib/ace/keyboard/emacs.js | 60 ++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 25f22a52..9bb30f6b 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -95,17 +95,15 @@ exports.handler.attach = function(editor) { $formerLineStart = editor.session.$useEmacsStyleLineStart; editor.session.$useEmacsStyleLineStart = true; - editor.session.$emacsMark = null; + editor.session.$emacsMark = null; // the active mark editor.session.$emacsMarkRing = editor.session.$emacsMarkRing || []; - editor.emacsMarkMode = function() { + editor.emacsMark = function() { return this.session.$emacsMark; } - editor.setEmacsMarkMode = function(p) { - var prevMark = this.session.$emacsMark; - if (prevMark) - this.session.$emacsMarkRing.push(prevMark); + editor.setEmacsMark = function(p) { + // to deactivate pass in a falsy value this.session.$emacsMark = p; } @@ -113,12 +111,18 @@ exports.handler.attach = function(editor) { var prevMark = this.session.$emacsMark; if (prevMark) this.session.$emacsMarkRing.push(prevMark); - if (activate) this.session.$emacsMark = p; + if (!p || activate) this.setEmacsMark(p) else this.session.$emacsMarkRing.push(p); } + editor.popEmacsMark = function() { + var mark = this.emacsMark(); + if (mark) { this.setEmacsMark(null); return mark; } + return this.session.$emacsMarkRing.pop(); + } + editor.getLastEmacsMark = function(p) { - return this.session.$emacsMark || this.session.$emacsMarkRing.reverse()[0]; + return this.session.$emacsMark || this.session.$emacsMarkRing.slice(-1)[0]; } editor.on("click", $resetMarkMode); @@ -209,7 +213,7 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { var editor = data.editor; // insertstring data.count times if (hashId == -1) { - editor.setEmacsMarkMode(null); + editor.pushEmacsMark(); if (data.count) { var str = Array(data.count + 1).join(key); data.count = null; @@ -263,7 +267,7 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { args = command.args; if (command.command) command = command.command; if (command === "goorselect") { - command = editor.emacsMarkMode() ? args[1] : args[0]; + command = editor.emacsMark() ? args[1] : args[0]; args = null; } } @@ -272,7 +276,7 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { if (command === "insertstring" || command === "splitline" || command === "togglecomment") { - editor.setEmacsMarkMode(null); + editor.pushEmacsMark(); } command = this.commands[command] || editor.commands.commands[command]; if (!command) return undefined; @@ -401,28 +405,38 @@ exports.handler.addCommands({ editor.multiSelect.toggleBlockSelection(); }, setMark: { - exec: function(editor) { - // Emulate emacs highlighting behaviour in transient-mark-mode. + exec: function(editor, args) { // Sets mark-mode and clears current selection. // When mark is set, keyboard cursor movement commands become // selection modification commands. That is, // "goto" commands become "select" commands. // Any insertion or mouse click resets mark-mode. // setMark twice in a row at the same place resets markmode - var markMode = editor.emacsMarkMode(); - if (markMode) { + var mark = editor.emacsMark(), + transientMarkModeActive = true; + + // if transientMarkModeActive then mark behavior is a little + // different. Deactivate the mark when setMark is run with active + // mark + if (transientMarkModeActive && (mark || !editor.selection.isEmpty())) { + editor.pushEmacsMark(editor.getCursorPosition()); + editor.pushEmacsMark(); + editor.clearSelection(); + return; + } + + if (mark) { var cp = editor.getCursorPosition(); if (editor.selection.isEmpty() && - markMode.row == cp.row && markMode.column == cp.column) { - editor.setEmacsMarkMode(null); - // console.log("Mark mode off"); + mark.row == cp.row && mark.column == cp.column) { + editor.pushEmacsMark(); return; } } // turn on mark mode - markMode = editor.getCursorPosition(); - editor.setEmacsMarkMode(markMode); - editor.selection.setSelectionAnchor(markMode.row, markMode.column); + mark = editor.getCursorPosition(); + editor.setEmacsMark(mark); + editor.selection.setSelectionAnchor(mark.row, mark.column); }, readonly: true, multiSelectAction: "forEach" @@ -459,7 +473,7 @@ exports.handler.addCommands({ multiSelectAction: "forEach" }, killLine: function(editor) { - editor.setEmacsMarkMode(null); + editor.pushEmacsMark(null); var pos = editor.getCursorPosition(); if (pos.column == 0 && editor.session.doc.getLine(pos.row).length == 0) { @@ -513,7 +527,7 @@ exports.handler.addCommands({ }, keyboardQuit: function(editor) { editor.selection.clearSelection(); - editor.setEmacsMarkMode(null); + editor.setEmacsMark(null); }, focusCommandLine: function(editor, arg) { if (editor.showCommandLine) From c084a8a34a03d364d76befc7fda791789e49faf4 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 21:02:17 -0700 Subject: [PATCH 08/16] emacs: fix copy event handler --- lib/ace/keyboard/emacs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 9bb30f6b..bb688868 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -516,12 +516,12 @@ exports.handler.addCommands({ killRingSave: { exec: function(editor) { exports.killRing.add(editor.getCopyText()); - (function() { + set(function() { var sel = editor.selection, range = sel.getRange(); editor.pushEmacsMark(sel.isBackwards() ? range.end : range.start); sel.clearSelection(); - }).delay(0); + }, 0); }, readonly: true }, From ab2021bdd8225d320f35e126700192070229f2ff Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 21:03:12 -0700 Subject: [PATCH 09/16] emacs: fix handling of universalArgument, it sets data.count to 4 --- lib/ace/keyboard/emacs.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index bb688868..4cb0e2f6 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -231,6 +231,10 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { if (count) { data.count = count; return {command: "null"}; + } else if (data.universalArgument) { + // if no number pressed use emacs defaults for universalArgument + // which is 4 + data.count = 4; } } data.universalArgument = false; @@ -288,15 +292,20 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { if (data.count) { var count = data.count; data.count = 0; - return { - args: args, - command: { - exec: function(editor, args) { - for (var i = 0; i < count; i++) - command.exec(editor, args); + if (!command || !command.handlesCount) { + return { + args: args, + command: { + exec: function(editor, args) { + for (var i = 0; i < count; i++) + command.exec(editor, args); + } } - } - }; + }; + } else { + if (!args) args = {} + if (typeof args === 'object') args.count = count; + } } return {command: command, args: args}; From e3ea71bb1262292a8c1793f3eecc1c70141acc82 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 21:04:07 -0700 Subject: [PATCH 10/16] emacs: C-u C-space fix (setMark) --- lib/ace/keyboard/emacs.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 4cb0e2f6..9b649600 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -421,6 +421,12 @@ exports.handler.addCommands({ // "goto" commands become "select" commands. // Any insertion or mouse click resets mark-mode. // setMark twice in a row at the same place resets markmode + if (args && args.count) { + var mark = editor.popEmacsMark(); + mark && editor.selection.moveCursorToPosition(mark); + return; + } + var mark = editor.emacsMark(), transientMarkModeActive = true; @@ -448,6 +454,7 @@ exports.handler.addCommands({ editor.selection.setSelectionAnchor(mark.row, mark.column); }, readonly: true, + handlesCount: true, multiSelectAction: "forEach" }, exchangePointAndMark: { From 0ea9428cd8a18c5d5e8127550f70e13fa286b33b Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 21:04:34 -0700 Subject: [PATCH 11/16] emacs: C-u C-x C-x fix (exchangePointAndMark) --- lib/ace/keyboard/emacs.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 9b649600..2c2cf34d 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -458,17 +458,25 @@ exports.handler.addCommands({ multiSelectAction: "forEach" }, exchangePointAndMark: { - exec: function(editor) { + exec: function(editor, args) { var sel = editor.selection; + if (args.count) { + var pos = editor.getCursorPosition(); + sel.clearSelection(); + sel.moveCursorToPosition(editor.popEmacsMark()); + editor.pushEmacsMark(pos); + return; + } + var lastMark = editor.getLastEmacsMark(); var range = sel.getRange(); if (range.isEmpty()) { - var lastMark = editor.getLastEmacsMark(); sel.selectToPosition(lastMark); return; } - editor.selection.setSelectionRange(range, !editor.selection.isBackwards()); + sel.setSelectionRange(range, !sel.isBackwards()); }, readonly: true, + handlesCount: true, multiSelectAction: "forEach" }, killWord: { From a16380eaea87583e37d94ca518bb4f96d7613fb9 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 21:07:22 -0700 Subject: [PATCH 12/16] emacs: fix bindKey register all partial key combos as null commands to be able to activate key combos with arbitrary length Example: if keyPart is "C-c C-l t" then "C-c C-l t" will get command assigned and "C-c" and "C-c C-l" will get a null command assigned --- lib/ace/keyboard/emacs.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 2c2cf34d..8d6b8985 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -202,12 +202,21 @@ exports.handler.bindKey = function(key, command) { key.split("|").forEach(function(keyPart) { keyPart = keyPart.toLowerCase(); ckb[keyPart] = command; - keyPart = keyPart.split(" ")[0]; - if (!ckb[keyPart]) - ckb[keyPart] = "null"; + // register all partial key combos as null commands + // to be able to activate key combos with arbitrary length + // Example: if keyPart is "C-c C-l t" then "C-c C-l t" will + // get command assigned and "C-c" and "C-c C-l" will get + // a null command assigned in this.commmandKeyBinding. For + // the lookup logic see handleKeyboard() + var keyParts = keyPart.split(" ").slice(0,-1); + keyParts.reduce(function(keyMapKeys, keyPart, i) { + var prefix = keyMapKeys[i-1] ? keyMapKeys[i-1] + ' ' : ''; + return keyMapKeys.concat([prefix + keyPart]); + }, []).forEach(function(keyPart) { + if (!ckb[keyPart]) ckb[keyPart] = "null"; + }); }, this); -}; - +} exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { var editor = data.editor; From b11e88885dc12c88d6e08676f3857e64a9d2cadc Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 22:02:12 -0700 Subject: [PATCH 13/16] emacs: allow count prefixes bigger than 9 example: C-1 C-8 C-f to move 18 chars forward --- lib/ace/keyboard/emacs.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 8d6b8985..5e07fb60 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -236,9 +236,10 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) { // CTRL + number / universalArgument for setting data.count if (modifier == "c-" || data.universalArgument) { + var prevCount = String(data.count || 0); var count = parseInt(key[key.length - 1]); - if (count) { - data.count = count; + if (typeof count === 'number' && !isNaN(count)) { + data.count = parseInt(prevCount + count); return {command: "null"}; } else if (data.universalArgument) { // if no number pressed use emacs defaults for universalArgument From 5b2ee4f789951209937b8f9d23f53e86e0bd4b44 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 26 May 2013 22:19:35 -0700 Subject: [PATCH 14/16] emacs: fix typo --- lib/ace/keyboard/emacs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 5e07fb60..ec9434e7 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -550,7 +550,7 @@ exports.handler.addCommands({ killRingSave: { exec: function(editor) { exports.killRing.add(editor.getCopyText()); - set(function() { + setTimeout(function() { var sel = editor.selection, range = sel.getRange(); editor.pushEmacsMark(sel.isBackwards() ? range.end : range.start); From 766b07e3301aa1be95bc705a2b555c86acaa5b02 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Mon, 27 May 2013 07:34:19 -0700 Subject: [PATCH 15/16] emacs: less eager pushMark --- lib/ace/keyboard/emacs.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index ec9434e7..1cb84f59 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -444,7 +444,6 @@ exports.handler.addCommands({ // different. Deactivate the mark when setMark is run with active // mark if (transientMarkModeActive && (mark || !editor.selection.isEmpty())) { - editor.pushEmacsMark(editor.getCursorPosition()); editor.pushEmacsMark(); editor.clearSelection(); return; From 5c219162cc4902494fb03a756adf8966f0eea649 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Mon, 27 May 2013 07:35:01 -0700 Subject: [PATCH 16/16] emacs: push mark of isearch start when isearch successful --- lib/ace/incremental_search.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index e4b84229..e6fa8928 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -113,6 +113,8 @@ oop.inherits(IncrementalSearch, Search); if (reset) { e.moveCursorToPosition(this.$startPos); this.$currentPos = this.$startPos; + } else { + e.pushEmacsMark && e.pushEmacsMark(this.$startPos, false); } this.highlight(null); return Range.fromPoints(this.$currentPos, this.$currentPos);