Merge pull request #1293

Incremental search
This commit is contained in:
nightwing 2013-03-23 16:21:35 +04:00
commit 9d4faf8911
9 changed files with 673 additions and 10 deletions

View file

@ -66,6 +66,8 @@ var bindDropdown = util.bindDropdown;
var ElasticTabstopsLite = require("ace/ext/elastic_tabstops_lite").ElasticTabstopsLite;
var IncrementalSearch = require("ace/incremental_search").IncrementalSearch;
/*********** create editor ***************************/
var container = document.getElementById("editor-container");
@ -173,7 +175,7 @@ commands.addCommand({
exec: function() {alert("Fake Save File");}
});
var keybindings = {
var keybindings = {
ace: null, // Null = use "default" keymapping
vim: require("ace/keyboard/vim").handler,
emacs: "ace/keyboard/emacs",
@ -380,7 +382,7 @@ bindDropdown("split", function(value) {
sp.setSplits(1);
} else {
var newEditor = (sp.getSplits() == 1);
sp.setOrientation(value == "below" ? sp.BELOW : sp.BESIDE);
sp.setOrientation(value == "below" ? sp.BELOW : sp.BESIDE);
sp.setSplits(2);
if (newEditor) {
@ -396,6 +398,14 @@ bindCheckbox("elastic_tabstops", function(checked) {
env.editor.setOption("useElasticTabstops", checked);
});
var iSearchCheckbox = bindCheckbox("isearch", function(checked) {
env.editor.setOption("useIncrementalSearch", checked);
});
env.editor.addEventListener('incrementalSearchSettingChanged', function(event) {
iSearchCheckbox.checked = event.isEnabled;
});
function synchroniseScrolling() {
var s1 = env.split.$editors[0].session;
@ -451,4 +461,3 @@ var StatusBar = require("./statusbar").StatusBar;
new StatusBar(env.editor, cmdLine.container);
});

View file

@ -171,6 +171,7 @@ exports.bindCheckbox = function(id, callback, noInit) {
};
el.onclick = onCheck;
noInit || onCheck();
return el;
};
exports.bindDropdown = function(id, callback, noInit) {
@ -235,4 +236,3 @@ function dropdown(values) {
});

View file

@ -250,6 +250,14 @@
<input type="checkbox" id="elastic_tabstops">
</td>
</tr>
<tr>
<td >
<label for="isearch">Incremental Search</label>
</td>
<td>
<input type="checkbox" id="isearch">
</td>
</tr>
<tr>
<td >
<label for="highlight_token">Show token info</label>

View file

@ -0,0 +1,172 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
var config = require("../config");
// These commands can be installed in a normal key handler to start iSearch:
exports.iSearchStartCommands = [{
name: "iSearch",
bindKey: {win: "Ctrl-F", mac: "Command-F"},
exec: function(editor, options) {
config.loadModule(["core", "ace/incremental_search"], function(e) {
var iSearch = e.iSearch = e.iSearch || new e.IncrementalSearch();
iSearch.activate(editor, options.backwards);
if (options.jumpToFirstMatch) iSearch.next(options);
});
},
readOnly: true
}, {
name: "iSearchBackwards",
exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {backwards: true}); },
readOnly: true
}, {
name: "iSearchAndGo",
bindKey: {win: "Ctrl-K", mac: "Command-G"},
exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {jumpToFirstMatch: true, useCurrentOrPrevSearch: true}); },
readOnly: true
}, {
name: "iSearchBackwardsAndGo",
bindKey: {win: "Ctrl-Shift-K", mac: "Command-Shift-G"},
exec: function(editor) { editor.execCommand('iSearch', {jumpToFirstMatch: true, backwards: true, useCurrentOrPrevSearch: true}); },
readOnly: true
}];
// These commands are only available when incremental search mode is active:
exports.iSearchCommands = [{
name: "restartSearch",
bindKey: {win: "Ctrl-F", mac: "Command-F"},
exec: function(iSearch) {
iSearch.cancelSearch(true);
},
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: "searchForward",
bindKey: {win: "Ctrl-S|Ctrl-K", mac: "Ctrl-S|Command-G"},
exec: function(iSearch, options) {
options.useCurrentOrPrevSearch = true;
iSearch.next(options);
},
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: "searchBackward",
bindKey: {win: "Ctrl-R|Ctrl-Shift-K", mac: "Ctrl-R|Command-Shift-G"},
exec: function(iSearch, options) {
options.useCurrentOrPrevSearch = true;
options.backwards = true;
iSearch.next(options);
},
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: "extendSearchTerm",
exec: function(iSearch, string) {
iSearch.addChar(string);
},
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: "extendSearchTermSpace",
bindKey: "space",
exec: function(iSearch) { iSearch.addChar(' '); },
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: "shrinkSearchTerm",
bindKey: "backspace",
exec: function(iSearch) {
iSearch.removeChar();
},
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: 'confirmSearch',
bindKey: 'return',
exec: function(iSearch) { iSearch.deactivate(); },
readOnly: true,
isIncrementalSearchCommand: true
}, {
name: 'cancelSearch',
bindKey: 'esc|Ctrl-G',
exec: function(iSearch) { iSearch.deactivate(true); },
readOnly: true,
isIncrementalSearchCommand: true
}];
var HashHandler = require("../keyboard/hash_handler").HashHandler;
var oop = require("../lib/oop");
function IncrementalSearchKeyboardHandler(iSearch) {
this.$iSearch = iSearch;
}
oop.inherits(IncrementalSearchKeyboardHandler, HashHandler);
;(function() {
this.attach = function(editor) {
var iSearch = this.$iSearch;
HashHandler.call(this, exports.iSearchCommands, editor.commands.platform);
this.$commandExecHandler = editor.commands.addEventListener('exec', function(e) {
if (!e.command.isIncrementalSearchCommand) return undefined;
e.stopPropagation();
e.preventDefault();
return e.command.exec(iSearch, e.args || {});
});
}
this.detach = function(editor) {
if (!this.$commandExecHandler) return;
editor.commands.removeEventListener('exec', this.$commandExecHandler);
delete this.$commandExecHandler;
}
var handleKeyboard$super = this.handleKeyboard;
this.handleKeyboard = function(data, hashId, key, keyCode) {
var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
if (cmd.command) { return cmd; }
if (hashId == -1) {
var extendCmd = this.commands.extendSearchTerm;
if (extendCmd) { return {command: extendCmd, args: key}; }
}
return {command: "null", passEvent: hashId == 0 || hashId == 4};
}
}).call(IncrementalSearchKeyboardHandler.prototype);
exports.IncrementalSearchKeyboardHandler = IncrementalSearchKeyboardHandler;
});

View file

@ -3,7 +3,7 @@
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
@ -14,7 +14,7 @@
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

View file

@ -0,0 +1,257 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
"use strict";
var oop = require("./lib/oop");
var Range = require("./range").Range;
var Search = require("./search").Search;
var SearchHighlight = require("./search_highlight").SearchHighlight;
var iSearchCommandModule = require("./commands/incremental_search_commands");
var ISearchKbd = iSearchCommandModule.IncrementalSearchKeyboardHandler;
/**
* @class IncrementalSearch
*
* Implements immediate searching while the user is typing. When incremental
* search is activated, keystrokes into the editor will be used for composing
* a search term. Immediately after every keystroke the search is updated:
* - so-far-matching characters are highlighted
* - the cursor is moved to the next match
*
**/
/**
*
*
* Creates a new `IncrementalSearch` object.
*
* @constructor
**/
function IncrementalSearch() {
this.$options = {wrap: false, skipCurrent: false};
this.$keyboardHandler = new ISearchKbd(this);
}
oop.inherits(IncrementalSearch, Search);
;(function() {
this.activate = function(ed, backwards) {
this.$editor = ed;
this.$startPos = this.$currentPos = ed.getCursorPosition();
this.$options.needle = '';
this.$options.backwards = backwards;
ed.keyBinding.addKeyboardHandler(this.$keyboardHandler);
this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this));
this.selectionFix(ed);
this.statusMessage(true);
}
this.deactivate = function(reset) {
this.cancelSearch(reset);
this.$editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler);
if (this.$mousedownHandler) {
this.$editor.removeEventListener('mousedown', this.$mousedownHandler);
delete this.$mousedownHandler;
}
this.message('');
}
this.selectionFix = function(editor) {
// Fix selection bug: When clicked inside the editor
// editor.selection.$isEmpty is false even if the mouse click did not
// open a selection. This is interpreted by the move commands to
// extend the selection. To only extend the selection when there is
// one, we clear it here
if (editor.selection.isEmpty() && !editor.session.$emacsMark) {
editor.clearSelection();
}
}
this.highlight = function(regexp) {
var sess = this.$editor.session,
hl = sess.$isearchHighlight = sess.$isearchHighlight || sess.addDynamicMarker(
new SearchHighlight(null, "ace_isearch-result", "text"));
hl.setRegexp(regexp);
sess._emit("changeBackMarker"); // force highlight layer redraw
}
this.cancelSearch = function(reset) {
var e = this.$editor;
this.$prevNeedle = this.$options.needle;
this.$options.needle = '';
if (reset) {
e.moveCursorToPosition(this.$startPos);
this.$currentPos = this.$startPos;
}
this.highlight(null);
return Range.fromPoints(this.$currentPos, this.$currentPos);
}
this.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) {
if (!this.$editor) return null;
var options = this.$options;
// get search term
if (needleUpdateFunc) {
options.needle = needleUpdateFunc.call(this, options.needle || '') || '';
}
if (options.needle.length === 0) {
this.statusMessage(true);
return this.cancelSearch(true);
};
// try to find the next occurence and enable highlighting marker
options.start = this.$currentPos;
var session = this.$editor.session,
found = this.find(session);
if (found) {
if (options.backwards) found = Range.fromPoints(found.end, found.start);
this.$editor.moveCursorToPosition(found.end);
if (moveToNext) this.$currentPos = found.end;
// highlight after cursor move, so selection works properly
this.highlight(options.re)
}
this.statusMessage(found);
return found;
}
this.addChar = function(c) {
return this.highlightAndFindWithNeedle(false, function(needle) {
return needle + c;
});
}
this.removeChar = function(c) {
return this.highlightAndFindWithNeedle(false, function(needle) {
return needle.length > 0 ? needle.substring(0, needle.length-1) : needle;
});
}
this.next = function(options) {
// try to find the next occurence of whatever we have searched for
// earlier.
// options = {[backwards: BOOL], [useCurrentOrPrevSearch: BOOL]}
options = options || {};
this.$options.backwards = !!options.backwards;
this.$currentPos = this.$editor.getCursorPosition();
return this.highlightAndFindWithNeedle(true, function(needle) {
return options.useCurrentOrPrevSearch && needle.length === 0 ?
this.$prevNeedle || '' : needle;
});
}
this.onMouseDown = function(evt) {
// when mouse interaction happens then we quit incremental search
this.deactivate();
return true;
}
this.statusMessage = function(found) {
var options = this.$options, msg = '';
msg += options.backwards ? 'reverse-' : '';
msg += 'isearch: ' + options.needle;
msg += found ? '' : ' (not found)';
this.message(msg);
}
this.message = function(msg) {
if (this.$editor.showCommandLine) {
this.$editor.showCommandLine(msg);
this.$editor.focus();
} else {
console.log(msg);
}
}
}).call(IncrementalSearch.prototype);
exports.IncrementalSearch = IncrementalSearch;
/**
*
* Config settings for enabling/disabling [[IncrementalSearch `IncrementalSearch`]].
*
**/
var dom = require('./lib/dom');
dom.importCssString && dom.importCssString("\
.ace_marker-layer .ace_isearch-result {\
position: absolute;\
z-index: 6;\
-moz-box-sizing: border-box;\
-webkit-box-sizing: border-box;\
box-sizing: border-box;\
}\
div.ace_isearch-result {\
border-radius: 4px;\
border: 8px solid rgba(255, 200, 0, 0.5);\
box-shadow: 0 0 4px rgb(255, 200, 0);\
}\
.ace_dark div.ace_isearch-result {\
border: 8px solid rgb(100, 110, 160);\
box-shadow: 0 0 4px rgb(80, 90, 140);\
}", "incremental-search-highlighting");
// support for default keyboard handler
var commands = require("./commands/command_manager");
(function() {
this.setupIncrementalSearch = function(editor, val) {
if (this.usesIncrementalSearch == val) return;
this.usesIncrementalSearch = val;
var iSearchCommands = iSearchCommandModule.iSearchStartCommands;
var method = val ? 'addCommands' : 'removeCommands';
this[method](iSearchCommands);
};
}).call(commands.CommandManager.prototype);
// incremental search config option
var Editor = require("./editor").Editor;
require("./config").defineOptions(Editor.prototype, "editor", {
useIncrementalSearch: {
set: function(val) {
this.keyBinding.$handlers.forEach(function(handler) {
if (handler.setupIncrementalSearch) {
handler.setupIncrementalSearch(this, val);
}
});
this._emit('incrementalSearchSettingChanged', {isEnabled: val});
}
}
});
});

View file

@ -0,0 +1,208 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
if (typeof process !== "undefined") {
require("amd-loader");
}
define(function(require, exports, module) {
"use strict";
var EditSession = require("./edit_session").EditSession;
var Editor = require("./editor").Editor;
var MockRenderer = require("./test/mockrenderer").MockRenderer;
var Range = require("./range").Range;
var assert = require("./test/assertions");
var IncrementalSearch = require("./incremental_search").IncrementalSearch;
var editor, iSearch;
function testRanges(str, ranges) {
ranges = ranges || editor.selection.getAllRanges();
assert.equal(ranges + "", str + "");
}
// force "rerender"
function callHighlighterUpdate() {
var session = editor.session,
ranges = [],
mockMarkerLayer = {
drawSingleLineMarker: function(_, markerRanges) {
ranges = ranges.concat(markerRanges);
}
}
session.$isearchHighlight.update([], mockMarkerLayer, session, {
firstRow: 0, lastRow: session.getRowLength()});
return ranges;
}
module.exports = {
name: "ACE incremental_search.js",
setUp: function() {
var session = new EditSession(["abc123", "xyz124"]);
editor = new Editor(new MockRenderer(), session);
iSearch = new IncrementalSearch();
},
"test: keyboard handler setup" : function() {
iSearch.activate(editor);
assert.equal(editor.getKeyboardHandler(), iSearch.$keyboardHandler);
iSearch.deactivate();
assert.notEqual(editor.getKeyboardHandler(), iSearch.$keyboardHandler);
},
"test: isearch highlight setup" : function() {
var sess = editor.session;
iSearch.activate(editor);
iSearch.highlight('foo');
var highl = sess.$isearchHighlight.id;
assert.ok(sess.$isearchHighlight, 'session has no isearch highlighter');
assert.equal(sess.getMarkers()[highl.id], highl.id, 'isearch highlight not in markers');
iSearch.deactivate();
iSearch.activate(editor);
iSearch.highlight('bar');
var highl2 = sess.$isearchHighlight.id;
assert.equal(highl2, highl, 'multiple isearch highlights');
},
"test: find simple text incrementally" : function() {
iSearch.activate(editor);
var range = iSearch.addChar('1'), // "1"
highlightRanges = callHighlighterUpdate(editor.session);
testRanges("Range: [0/3] -> [0/4]", [range], "range");
testRanges("Range: [0/3] -> [0/4],Range: [1/3] -> [1/4]", highlightRanges, "highlight");
range = iSearch.addChar('2'); // "12"
highlightRanges = callHighlighterUpdate(editor.session);
testRanges("Range: [0/3] -> [0/5]", [range], "range");
testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
range = iSearch.addChar('3'); // "123"
highlightRanges = callHighlighterUpdate(editor.session);
testRanges("Range: [0/3] -> [0/6]", [range], "range");
testRanges("Range: [0/3] -> [0/6]", highlightRanges, "highlight");
range = iSearch.removeChar(); // "12"
highlightRanges = callHighlighterUpdate(editor.session);
testRanges("Range: [0/3] -> [0/5]", [range], "range");
testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
},
"test: forward / backward" : function() {
iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');
var range = iSearch.next();
testRanges("Range: [1/3] -> [1/5]", [range], "range");
range = iSearch.next(); // nothing to find
testRanges("", [range], "range");
range = iSearch.next({backwards: true}); // backwards
testRanges("Range: [1/5] -> [1/3]", [range], "range");
},
"test: cancelSearch" : function() {
iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');
var range = iSearch.cancelSearch(true);
testRanges("Range: [0/0] -> [0/0]", [range], "range");
iSearch.addChar('1'); range = iSearch.addChar('2');
testRanges("Range: [0/3] -> [0/5]", [range], "range");
},
"test: failing search keeps pos" : function() {
iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');
var range = iSearch.addChar('x');
testRanges("", [range], "range");
assert.position(editor.getCursorPosition(), 0, 5);
},
"test: backwards search" : function() {
editor.moveCursorTo(1,0);
iSearch.activate(editor, true);
iSearch.addChar('1'); var range = iSearch.addChar('2');;
testRanges("Range: [0/5] -> [0/3]", [range], "range");
assert.position(editor.getCursorPosition(), 0, 3);
},
"test: forwards then backwards, same result, reoriented range" : function() {
iSearch.activate(editor);
iSearch.addChar('1'); var range = iSearch.addChar('2');;
testRanges("Range: [0/3] -> [0/5]", [range], "range");
assert.position(editor.getCursorPosition(), 0, 5);
range = iSearch.next({backwards: true});
testRanges("Range: [0/5] -> [0/3]", [range], "range");
assert.position(editor.getCursorPosition(), 0, 3);
},
"test: reuse prev search via option" : function() {
iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');;
assert.position(editor.getCursorPosition(), 0, 5);
iSearch.deactivate();
iSearch.activate(editor);
iSearch.next({backwards: false, useCurrentOrPrevSearch: true});
assert.position(editor.getCursorPosition(), 1, 5);
},
"test: don't extend selection range if selection is empty" : function() {
iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');;
testRanges("Range: [0/5] -> [0/5]", [editor.getSelectionRange()], "sel range");
},
"test: extend selection range if selection exists" : function() {
iSearch.activate(editor);
editor.selection.selectTo(0, 1);
iSearch.addChar('1'); iSearch.addChar('2');;
testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
},
"test: extend selection in emacs mark mode" : function() {
var emacs = require('./keyboard/emacs');
editor.keyBinding.addKeyboardHandler(emacs.handler);
emacs.handler.commands.setMark.exec(editor);
iSearch.activate(editor);
iSearch.addChar('1'); iSearch.addChar('2');;
testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
}
};
});
if (typeof module !== "undefined" && module === require.main) {
require("asyncjs").test.testcase(module.exports).exec()
}

View file

@ -32,6 +32,9 @@ define(function(require, exports, module) {
"use strict";
var dom = require("../lib/dom");
require("../incremental_search");
var iSearchCommandModule = require("../commands/incremental_search_commands");
var screenToTextBlockCoordinates = function(x, y) {
var canvasPos = this.scroller.getBoundingClientRect();
@ -49,6 +52,8 @@ var screenToTextBlockCoordinates = function(x, y) {
var HashHandler = require("./hash_handler").HashHandler;
exports.handler = new HashHandler();
exports.handler.isEmacs = true
var initialized = false;
var $formerLongWords;
var $formerLineStart;
@ -100,7 +105,7 @@ exports.handler.attach = function(editor) {
}
editor.on("click", $resetMarkMode);
editor.on("changeSession",$kbSessionChange);
editor.on("changeSession", $kbSessionChange);
editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates;
editor.setStyle("emacs-mode");
editor.commands.addCommands(commands);
@ -296,8 +301,10 @@ exports.emacsKeys = {
"PageUp|M-v": {command: "goorselect", args: ["gotopageup","selectpageup"]},
"S-C-Down": "selectpagedown",
"S-C-Up": "selectpageup",
"C-s": "findnext",
"C-r": "findprevious",
"C-s": "iSearch",
"C-r": "iSearchBackwards",
"M-C-s": "findnext",
"M-C-r": "findprevious",
"S-M-5": "replace",
@ -457,6 +464,8 @@ exports.handler.addCommands({
}
});
exports.handler.addCommands(iSearchCommandModule.iSearchStartCommands);
var commands = exports.handler.commands;
commands.yank.isYank = true;
commands.yankRotate.isYank = true;
@ -482,5 +491,4 @@ exports.killRing = {
}
};
});

View file

@ -21,6 +21,7 @@ var testNames = [
"ace/editor_navigation_test",
"ace/editor_text_edit_test",
"ace/ext/static_highlight_test",
"ace/incremental_search_test",
"ace/keyboard/emacs_test",
"ace/keyboard/keybinding_test",
"ace/layer/text_test",