[incremental search] adding basic functionality
This commit is contained in:
parent
cf8aaa43fa
commit
3e6a036995
4 changed files with 294 additions and 0 deletions
|
|
@ -83,6 +83,9 @@ env.editor.setAnimatedScroll(true);
|
|||
// add multiple cursor support to editor
|
||||
require("ace/multi_select").MultiSelect(env.editor);
|
||||
|
||||
// add incremental search
|
||||
window.iSearch = new (require("ace/incremental_search").IncrementalSearch)();
|
||||
|
||||
var consoleEl = dom.createElement("div");
|
||||
container.parentNode.appendChild(consoleEl);
|
||||
consoleEl.style.cssText = "position:fixed; bottom:1px; right:0;\
|
||||
|
|
|
|||
163
lib/ace/incremental_search.js
Normal file
163
lib/ace/incremental_search.js
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/* ***** 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 lang = require("./lib/lang");
|
||||
var oop = require("./lib/oop");
|
||||
var Range = require("./range").Range;
|
||||
var Search = require("./search").Search;
|
||||
|
||||
/**
|
||||
* @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. Options:
|
||||
*
|
||||
* @constructor
|
||||
**/
|
||||
function IncrementalSearch() {
|
||||
this.$options = {wrap: false, skipCurrent: false};
|
||||
this.$keyboardHandler = this;
|
||||
}
|
||||
|
||||
oop.inherits(IncrementalSearch, Search);
|
||||
|
||||
;(function() {
|
||||
|
||||
var iSearch = this;
|
||||
|
||||
iSearch.activate = function(editor) {
|
||||
this.$editor = editor;
|
||||
this.$startRange = this.$currentRange = editor.selection.toOrientedRange();
|
||||
this.installKeyboardHandler(editor);
|
||||
}
|
||||
|
||||
this.deactivate = function() {
|
||||
this.uninstallKeyboardHandler(this.$editor);
|
||||
delete this.$editor;
|
||||
}
|
||||
|
||||
iSearch.highlightAndFindWithNeedle = function(dir, moveToMatch, needleUpdateFunc) {
|
||||
if (!this.$editor) return null;
|
||||
dir = dir || 'forward';
|
||||
var session = this.$editor.session,
|
||||
options = this.$options;
|
||||
if (needleUpdateFunc) options.needle = needleUpdateFunc(options.needle || '') || '';
|
||||
if (dir === "forward") {
|
||||
options.start = this.$currentRange.end;
|
||||
options.backwards = false;
|
||||
} else {
|
||||
options.start = this.$currentRange.start;
|
||||
options.backwards = true;
|
||||
}
|
||||
var range = this.find(session);
|
||||
if (range && moveToMatch) {
|
||||
this.$editor.selection.setRange(range);
|
||||
this.$currentRange = range;
|
||||
}
|
||||
|
||||
session.highlight(options.re);
|
||||
return range;
|
||||
}
|
||||
|
||||
this.addChar = function(c) {
|
||||
return this.highlightAndFindWithNeedle('forward', false, function(needle) {
|
||||
return needle + c;
|
||||
});
|
||||
},
|
||||
|
||||
iSearch.removeChar = function(c) {
|
||||
return this.highlightAndFindWithNeedle('forward', false, function(needle) {
|
||||
return needle.length > 0 ? needle.substring(0, needle.length-1) : needle;
|
||||
});
|
||||
}
|
||||
|
||||
iSearch.forward = function() {
|
||||
return this.highlightAndFindWithNeedle('forward', true);
|
||||
}
|
||||
|
||||
iSearch.backward = function() {
|
||||
return this.highlightAndFindWithNeedle('backward', true);
|
||||
}
|
||||
|
||||
this.installKeyboardHandler = function(editor) {
|
||||
this.$origKeyboardHandlers = [].concat(editor.keyBinding.$handlers);
|
||||
this.$origKeyboardHandlers.reverse().forEach(function(handler) {
|
||||
editor.keyBinding.removeKeyboardHandler(handler);
|
||||
});
|
||||
editor.keyBinding.addKeyboardHandler(this.$keyboardHandler);
|
||||
}
|
||||
|
||||
this.uninstallKeyboardHandler = function(editor) {
|
||||
editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler);
|
||||
if (this.$origKeyboardHandlers) {
|
||||
this.$origKeyboardHandlers.forEach(function(handler) {
|
||||
editor.keyBinding.addKeyboardHandler(handler);
|
||||
});
|
||||
delete this.$origKeyboardHandlers;
|
||||
}
|
||||
}
|
||||
|
||||
iSearch.handleKeyboard = function(data, hashId, key, keyCode) {
|
||||
console.log("data: %s, hashId: %s, key: %s, keyCode: %s",
|
||||
data, hashId, key, keyCode);
|
||||
if (hashId === 0) {
|
||||
if (key === 'backspace') this.removeChar();
|
||||
else if (key.length === 1) this.addChar(key);
|
||||
}
|
||||
|
||||
if (hashId === 1) {
|
||||
if (key === 's') this.forward();
|
||||
if (key === 'r') this.backward();
|
||||
}
|
||||
|
||||
console.log(this.$options.needle);
|
||||
return {command: 'null'}
|
||||
}
|
||||
|
||||
|
||||
}).call(IncrementalSearch.prototype);
|
||||
|
||||
|
||||
exports.IncrementalSearch = IncrementalSearch;
|
||||
|
||||
});
|
||||
127
lib/ace/incremental_search_test.js
Normal file
127
lib/ace/incremental_search_test.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/* ***** 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.$searchHighlight.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: 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");
|
||||
|
||||
range = iSearch.forward(); // "12", cursor forward
|
||||
highlightRanges = callHighlighterUpdate(editor.session);
|
||||
testRanges("Range: [1/3] -> [1/5]", [range], "range");
|
||||
testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
|
||||
|
||||
}
|
||||
|
||||
// // "test: find simple text in document" : function() {
|
||||
// var session = new EditSession(["juhu kinners 123", "456"]);
|
||||
// var search = new Search().set({
|
||||
// needle: "kinners"
|
||||
// });
|
||||
// session.getSelection().moveCursorTo(0, 13);
|
||||
// var range = search.find(session);
|
||||
// assert.position(range.start, 0, 5);
|
||||
// assert.position(range.end, 0, 12);
|
||||
// },
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec()
|
||||
}
|
||||
|
|
@ -45,6 +45,7 @@ var testNames = [
|
|||
"ace/range_test",
|
||||
"ace/range_list_test",
|
||||
"ace/search_test",
|
||||
"ace/incremental_search_test",
|
||||
"ace/selection_test",
|
||||
"ace/token_iterator_test",
|
||||
"ace/virtual_renderer_test"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue