isearch: now supports yanking, and killing text from edit buffer

This commit is contained in:
Robert Krahn 2014-03-04 17:39:30 -08:00
commit 58404df349
4 changed files with 72 additions and 18 deletions

View file

@ -94,14 +94,14 @@ exports.iSearchCommands = [{
}, { }, {
name: "extendSearchTerm", name: "extendSearchTerm",
exec: function(iSearch, string) { exec: function(iSearch, string) {
iSearch.addChar(string); iSearch.addString(string);
}, },
readOnly: true, readOnly: true,
isIncrementalSearchCommand: true isIncrementalSearchCommand: true
}, { }, {
name: "extendSearchTermSpace", name: "extendSearchTermSpace",
bindKey: "space", bindKey: "space",
exec: function(iSearch) { iSearch.addChar(' '); }, exec: function(iSearch) { iSearch.addString(' '); },
readOnly: true, readOnly: true,
isIncrementalSearchCommand: true isIncrementalSearchCommand: true
}, { }, {
@ -134,6 +134,28 @@ exports.iSearchCommands = [{
}, },
readOnly: true, readOnly: true,
isIncrementalSearchCommand: true isIncrementalSearchCommand: true
}, {
name: "yankNextWord",
bindKey: "Ctrl-w",
exec: function(iSearch) {
var ed = iSearch.$editor,
range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorWordRight(); }),
string = ed.session.getTextRange(range);
iSearch.addString(string);
},
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: "yankNextChar",
bindKey: "Ctrl-Alt-y",
exec: function(iSearch) {
var ed = iSearch.$editor,
range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorRight(); }),
string = ed.session.getTextRange(range);
iSearch.addString(string);
},
readOnly: true,
isIncrementalSearchCommand: true
}]; }];
function IncrementalSearchKeyboardHandler(iSearch) { function IncrementalSearchKeyboardHandler(iSearch) {
@ -163,6 +185,8 @@ oop.inherits(IncrementalSearchKeyboardHandler, HashHandler);
var handleKeyboard$super = this.handleKeyboard; var handleKeyboard$super = this.handleKeyboard;
this.handleKeyboard = function(data, hashId, key, keyCode) { this.handleKeyboard = function(data, hashId, key, keyCode) {
if (((hashId === 1/*ctrl*/ || hashId === 8/*command*/) && key === 'v')
|| (hashId === 1/*ctrl*/ && key === 'y')) return null;
var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode); var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
if (cmd.command) { return cmd; } if (cmd.command) { return cmd; }
if (hashId == -1) { if (hashId == -1) {

View file

@ -72,7 +72,11 @@ oop.inherits(IncrementalSearch, Search);
this.$options.needle = ''; this.$options.needle = '';
this.$options.backwards = backwards; this.$options.backwards = backwards;
ed.keyBinding.addKeyboardHandler(this.$keyboardHandler); ed.keyBinding.addKeyboardHandler(this.$keyboardHandler);
// we need to completely intercept paste, just registering an event handler does not work
this.$originalEditorOnPaste = ed.onPaste;
ed.onPaste = this.onPaste.bind(this);
this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this)); this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this));
this.$onPasteHandler = ed.addEventListener('paste', this.onPaste.bind(this));
this.selectionFix(ed); this.selectionFix(ed);
this.statusMessage(true); this.statusMessage(true);
} }
@ -84,6 +88,7 @@ oop.inherits(IncrementalSearch, Search);
this.$editor.removeEventListener('mousedown', this.$mousedownHandler); this.$editor.removeEventListener('mousedown', this.$mousedownHandler);
delete this.$mousedownHandler; delete this.$mousedownHandler;
} }
this.$editor.onPaste = this.$originalEditorOnPaste;
this.message(''); this.message('');
} }
@ -150,9 +155,9 @@ oop.inherits(IncrementalSearch, Search);
return found; return found;
} }
this.addChar = function(c) { this.addString = function(s) {
return this.highlightAndFindWithNeedle(false, function(needle) { return this.highlightAndFindWithNeedle(false, function(needle) {
return needle + c; return needle + s;
}); });
} }
@ -181,6 +186,10 @@ oop.inherits(IncrementalSearch, Search);
return true; return true;
} }
this.onPaste = function(text) {
this.addString(text);
}
this.statusMessage = function(found) { this.statusMessage = function(found) {
var options = this.$options, msg = ''; var options = this.$options, msg = '';
msg += options.backwards ? 'reverse-' : ''; msg += options.backwards ? 'reverse-' : '';

View file

@ -95,17 +95,17 @@ module.exports = {
"test: find simple text incrementally" : function() { "test: find simple text incrementally" : function() {
iSearch.activate(editor); iSearch.activate(editor);
var range = iSearch.addChar('1'), // "1" var range = iSearch.addString('1'), // "1"
highlightRanges = callHighlighterUpdate(editor.session); highlightRanges = callHighlighterUpdate(editor.session);
testRanges("Range: [0/3] -> [0/4]", [range], "range"); testRanges("Range: [0/3] -> [0/4]", [range], "range");
testRanges("Range: [0/3] -> [0/4],Range: [1/3] -> [1/4]", highlightRanges, "highlight"); testRanges("Range: [0/3] -> [0/4],Range: [1/3] -> [1/4]", highlightRanges, "highlight");
range = iSearch.addChar('2'); // "12" range = iSearch.addString('2'); // "12"
highlightRanges = callHighlighterUpdate(editor.session); highlightRanges = callHighlighterUpdate(editor.session);
testRanges("Range: [0/3] -> [0/5]", [range], "range"); testRanges("Range: [0/3] -> [0/5]", [range], "range");
testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight"); testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
range = iSearch.addChar('3'); // "123" range = iSearch.addString('3'); // "123"
highlightRanges = callHighlighterUpdate(editor.session); highlightRanges = callHighlighterUpdate(editor.session);
testRanges("Range: [0/3] -> [0/6]", [range], "range"); testRanges("Range: [0/3] -> [0/6]", [range], "range");
testRanges("Range: [0/3] -> [0/6]", highlightRanges, "highlight"); testRanges("Range: [0/3] -> [0/6]", highlightRanges, "highlight");
@ -118,7 +118,7 @@ module.exports = {
"test: forward / backward" : function() { "test: forward / backward" : function() {
iSearch.activate(editor); iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2'); iSearch.addString('1'); iSearch.addString('2');
var range = iSearch.next(); var range = iSearch.next();
testRanges("Range: [1/3] -> [1/5]", [range], "range"); testRanges("Range: [1/3] -> [1/5]", [range], "range");
@ -131,18 +131,18 @@ module.exports = {
"test: cancelSearch" : function() { "test: cancelSearch" : function() {
iSearch.activate(editor); iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2'); iSearch.addString('1'); iSearch.addString('2');
var range = iSearch.cancelSearch(true); var range = iSearch.cancelSearch(true);
testRanges("Range: [0/0] -> [0/0]", [range], "range"); testRanges("Range: [0/0] -> [0/0]", [range], "range");
iSearch.addChar('1'); range = iSearch.addChar('2'); iSearch.addString('1'); range = iSearch.addString('2');
testRanges("Range: [0/3] -> [0/5]", [range], "range"); testRanges("Range: [0/3] -> [0/5]", [range], "range");
}, },
"test: failing search keeps pos" : function() { "test: failing search keeps pos" : function() {
iSearch.activate(editor); iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2'); iSearch.addString('1'); iSearch.addString('2');
var range = iSearch.addChar('x'); var range = iSearch.addString('x');
testRanges("", [range], "range"); testRanges("", [range], "range");
assert.position(editor.getCursorPosition(), 0, 5); assert.position(editor.getCursorPosition(), 0, 5);
}, },
@ -150,14 +150,14 @@ module.exports = {
"test: backwards search" : function() { "test: backwards search" : function() {
editor.moveCursorTo(1,0); editor.moveCursorTo(1,0);
iSearch.activate(editor, true); iSearch.activate(editor, true);
iSearch.addChar('1'); var range = iSearch.addChar('2');; iSearch.addString('1'); var range = iSearch.addString('2');;
testRanges("Range: [0/5] -> [0/3]", [range], "range"); testRanges("Range: [0/5] -> [0/3]", [range], "range");
assert.position(editor.getCursorPosition(), 0, 3); assert.position(editor.getCursorPosition(), 0, 3);
}, },
"test: forwards then backwards, same result, reoriented range" : function() { "test: forwards then backwards, same result, reoriented range" : function() {
iSearch.activate(editor); iSearch.activate(editor);
iSearch.addChar('1'); var range = iSearch.addChar('2');; iSearch.addString('1'); var range = iSearch.addString('2');;
testRanges("Range: [0/3] -> [0/5]", [range], "range"); testRanges("Range: [0/3] -> [0/5]", [range], "range");
assert.position(editor.getCursorPosition(), 0, 5); assert.position(editor.getCursorPosition(), 0, 5);
@ -168,7 +168,7 @@ module.exports = {
"test: reuse prev search via option" : function() { "test: reuse prev search via option" : function() {
iSearch.activate(editor); iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');; iSearch.addString('1'); iSearch.addString('2');;
assert.position(editor.getCursorPosition(), 0, 5); assert.position(editor.getCursorPosition(), 0, 5);
iSearch.deactivate(); iSearch.deactivate();
@ -179,14 +179,14 @@ module.exports = {
"test: don't extend selection range if selection is empty" : function() { "test: don't extend selection range if selection is empty" : function() {
iSearch.activate(editor); iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');; iSearch.addString('1'); iSearch.addString('2');;
testRanges("Range: [0/5] -> [0/5]", [editor.getSelectionRange()], "sel range"); testRanges("Range: [0/5] -> [0/5]", [editor.getSelectionRange()], "sel range");
}, },
"test: extend selection range if selection exists" : function() { "test: extend selection range if selection exists" : function() {
iSearch.activate(editor); iSearch.activate(editor);
editor.selection.selectTo(0, 1); editor.selection.selectTo(0, 1);
iSearch.addChar('1'); iSearch.addChar('2');; iSearch.addString('1'); iSearch.addString('2');;
testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range"); testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
}, },
@ -195,7 +195,7 @@ module.exports = {
editor.keyBinding.addKeyboardHandler(emacs.handler); editor.keyBinding.addKeyboardHandler(emacs.handler);
emacs.handler.commands.setMark.exec(editor); emacs.handler.commands.setMark.exec(editor);
iSearch.activate(editor); iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');; iSearch.addString('1'); iSearch.addString('2');;
testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range"); testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
} }

View file

@ -889,6 +889,27 @@ var Selection = function(session) {
return range; return range;
} }
/**
* Saves the current cursor position and calls `func` that can change the cursor
* postion. The result is the range of the starting and eventual cursor position.
* Will reset the cursor position.
* @param {Function} The callback that should change the cursor position
* @returns {Range}
*
**/
this.getRangeOfMovements = function(func) {
var start = this.getCursor();
try {
func.call(null, this);
var end = this.getCursor();
return Range.fromPoints(start,end);
} catch(e) {
return Range.fromPoints(start,start);
} finally {
this.moveCursorToPosition(start);
}
}
this.toJSON = function() { this.toJSON = function() {
if (this.rangeCount) { if (this.rangeCount) {
var data = this.ranges.map(function(r) { var data = this.ranges.map(function(r) {