From 3e6a03699587fb7bbaaaaffb41b491091ede015e Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 06:32:53 -0700 Subject: [PATCH 01/32] [incremental search] adding basic functionality --- demo/kitchen-sink/demo.js | 3 + lib/ace/incremental_search.js | 163 +++++++++++++++++++++++++++++ lib/ace/incremental_search_test.js | 127 ++++++++++++++++++++++ lib/ace/test/all_browser.js | 1 + 4 files changed, 294 insertions(+) create mode 100644 lib/ace/incremental_search.js create mode 100644 lib/ace/incremental_search_test.js diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index adc47c00..26e96767 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -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;\ diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js new file mode 100644 index 00000000..173c963d --- /dev/null +++ b/lib/ace/incremental_search.js @@ -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; + +}); diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js new file mode 100644 index 00000000..40b09eb0 --- /dev/null +++ b/lib/ace/incremental_search_test.js @@ -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() +} diff --git a/lib/ace/test/all_browser.js b/lib/ace/test/all_browser.js index 9a274329..b98d6558 100644 --- a/lib/ace/test/all_browser.js +++ b/lib/ace/test/all_browser.js @@ -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" From d6be35e8e19ec22dfba699df5f9b34496a05caf3 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 06:59:35 -0700 Subject: [PATCH 02/32] [incremental search] forward / backward movement --- lib/ace/incremental_search.js | 26 ++++++++++++++++++------- lib/ace/incremental_search_test.js | 31 +++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 173c963d..3870c678 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -76,24 +76,36 @@ oop.inherits(IncrementalSearch, Search); delete this.$editor; } - iSearch.highlightAndFindWithNeedle = function(dir, moveToMatch, needleUpdateFunc) { + iSearch.cancelSearch = function() { + var session = this.$editor.session, + sel = this.$editor.selection; + this.$options.needle = ''; + session.highlight(null); + sel.setRange(this.$startRange); + return this.$currentRange = this.$startRange; + } + + iSearch.highlightAndFindWithNeedle = function(dir, moveToNext, 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 (options.needle.length === 0) { + return this.cancelSearch(); + } if (dir === "forward") { - options.start = this.$currentRange.end; + options.start = moveToNext ? this.$currentRange.end : this.$currentRange.start; options.backwards = false; } else { - options.start = this.$currentRange.start; + options.start = moveToNext ? this.$currentRange.start : this.$currentRange.end; options.backwards = true; } var range = this.find(session); - if (range && moveToMatch) { - this.$editor.selection.setRange(range); - this.$currentRange = range; - } + if (!range) range = this.$currentRange; + + this.$editor.selection.setRange(range); + this.$currentRange = range; session.highlight(options.re); return range; diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index 40b09eb0..ea40630b 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -90,6 +90,7 @@ module.exports = { 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"); @@ -99,12 +100,36 @@ module.exports = { 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); + "test: forward / backward" : function() { + iSearch.activate(editor); + iSearch.addChar('1'); iSearch.addChar('2'); + var range = iSearch.forward(); testRanges("Range: [1/3] -> [1/5]", [range], "range"); - testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight"); + range = iSearch.forward(); + testRanges("Range: [1/3] -> [1/5]", [range], "range"); + + range = iSearch.backward(); + testRanges("Range: [0/3] -> [0/5]", [range], "range"); + }, + + "test: cancelSearch" : function() { + iSearch.activate(editor); + iSearch.addChar('1'); iSearch.addChar('2'); + var range = iSearch.cancelSearch(); + 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 range" : function() { + iSearch.activate(editor); + iSearch.addChar('1'); iSearch.addChar('2'); + var range = iSearch.addChar('x'); + testRanges("Range: [0/3] -> [0/5]", [range], "range"); } // // "test: find simple text in document" : function() { From 1423893c3b8c20b11c0f5ba4d9863aa7ea52fbb1 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 09:25:40 -0700 Subject: [PATCH 03/32] [incremental search] refining forward/backward behavior --- lib/ace/incremental_search.js | 129 +++++++++++++++++------------ lib/ace/incremental_search_test.js | 8 +- 2 files changed, 80 insertions(+), 57 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 3870c678..0c3d61a2 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -31,7 +31,6 @@ 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; @@ -65,46 +64,51 @@ oop.inherits(IncrementalSearch, Search); var iSearch = this; - iSearch.activate = function(editor) { + iSearch.activate = function(editor, backwards) { this.$editor = editor; - this.$startRange = this.$currentRange = editor.selection.toOrientedRange(); + var pos = editor.getCursorPosition(); + this.$startRange = this.$currentRange = Range.fromPoints(pos, pos); + // this.$startRange = this.$currentRange = editor.selection.toOrientedRange(); this.installKeyboardHandler(editor); + this.$options.needle = ''; + this.$options.backwards = backwards; } - this.deactivate = function() { + iSearch.deactivate = function(reset) { + this.cancelSearch(reset); this.uninstallKeyboardHandler(this.$editor); delete this.$editor; } - iSearch.cancelSearch = function() { - var session = this.$editor.session, - sel = this.$editor.selection; + iSearch.cancelSearch = function(reset) { + var e = this.$editor; + this.$prevNeedle = this.$options.needle; this.$options.needle = ''; - session.highlight(null); - sel.setRange(this.$startRange); + e.session.highlight(null); + if (reset) e.selection.setRange(this.$startRange); + else e.selection.clearSelection(); return this.$currentRange = this.$startRange; } - iSearch.highlightAndFindWithNeedle = function(dir, moveToNext, needleUpdateFunc) { + iSearch.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) { if (!this.$editor) return null; - dir = dir || 'forward'; - var session = this.$editor.session, - options = this.$options; + var options = this.$options; if (needleUpdateFunc) options.needle = needleUpdateFunc(options.needle || '') || ''; if (options.needle.length === 0) { - return this.cancelSearch(); + return this.cancelSearch(true); } - if (dir === "forward") { - options.start = moveToNext ? this.$currentRange.end : this.$currentRange.start; - options.backwards = false; - } else { - options.start = moveToNext ? this.$currentRange.start : this.$currentRange.end; - options.backwards = true; - } - var range = this.find(session); - if (!range) range = this.$currentRange; - this.$editor.selection.setRange(range); + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + var session = this.$editor.session, range = this.$currentRange; + if (!options.backwards) { + options.start = moveToNext ? range.end : range.start; + } else { + options.start = moveToNext ? range.start : range.end; + } + range = this.find(session) || range; + + // if (dir === "backward") range = Range.fromPoints(range.end, range.start); + this.$editor.selection.setRange(range, options.backwards); this.$currentRange = range; session.highlight(options.re); @@ -112,58 +116,77 @@ oop.inherits(IncrementalSearch, Search); } this.addChar = function(c) { - return this.highlightAndFindWithNeedle('forward', false, function(needle) { + return this.highlightAndFindWithNeedle(false, function(needle) { return needle + c; }); - }, + } iSearch.removeChar = function(c) { - return this.highlightAndFindWithNeedle('forward', false, function(needle) { + return this.highlightAndFindWithNeedle(false, function(needle) { return needle.length > 0 ? needle.substring(0, needle.length-1) : needle; }); } - iSearch.forward = function() { - return this.highlightAndFindWithNeedle('forward', true); + iSearch.next = function(backwards) { + this.$options.backwards = backwards; + return this.highlightAndFindWithNeedle(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); - }); + iSearch.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) { + iSearch.uninstallKeyboardHandler = function(editor) { editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler); - if (this.$origKeyboardHandlers) { - this.$origKeyboardHandlers.forEach(function(handler) { - editor.keyBinding.addKeyboardHandler(handler); - }); - delete this.$origKeyboardHandlers; - } + // if (this.$origKeyboardHandlers) { + // this.$origKeyboardHandlers.forEach(function(handler) { + // editor.keyBinding.addKeyboardHandler(handler); + // }); + // delete this.$origKeyboardHandlers; + // } + } + + iSearch.message = function(msg) { + console.log(msg); + if (this.commandLine) + this.commandLine.setValue(msg, 1); } iSearch.handleKeyboard = function(data, hashId, key, keyCode) { - console.log("data: %s, hashId: %s, key: %s, keyCode: %s", - data, hashId, key, keyCode); + this.message("data: " + data + ", hashId: " + hashId + ", key: " + key + ", keyCode: " + keyCode); + var stop = {command: 'null'}, + result = undefined; + + console.log(this.$options.needle); if (hashId === 0) { - if (key === 'backspace') this.removeChar(); - else if (key.length === 1) this.addChar(key); + if (key === 'backspace') { this.removeChar(); result = stop; } + if (key === 'return') { this.deactivate(); this.message(''); return stop; } + if (key === 'esc') { this.deactivate(true); this.message(''); return stop; } + if (key.length === 1) { this.addChar(key); result = stop; } } if (hashId === 1) { - if (key === 's') this.forward(); - if (key === 'r') this.backward(); + if (key === 's' || key === 'r') { + if (this.$options.needle.length === 0) + this.$options.needle = this.$prevNeedle || ''; + this.$options.backwards = key === 'r'; + this.forward(); + result = stop; + } + if (key === 'g') { this.deactivate(true); this.message(''); return stop; } + // let others handle but don't deactivate iSearch + // stop.passEvent = true; + // return stop; } - console.log(this.$options.needle); - return {command: 'null'} + this.message('isearch: ' + this.$options.needle); + // this.deactivate(); + + return result; } diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index ea40630b..58d927c2 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -105,20 +105,20 @@ module.exports = { "test: forward / backward" : function() { iSearch.activate(editor); iSearch.addChar('1'); iSearch.addChar('2'); - var range = iSearch.forward(); + var range = iSearch.next(); testRanges("Range: [1/3] -> [1/5]", [range], "range"); - range = iSearch.forward(); + range = iSearch.next(); testRanges("Range: [1/3] -> [1/5]", [range], "range"); - range = iSearch.backward(); + range = iSearch.next(true); // backwards testRanges("Range: [0/3] -> [0/5]", [range], "range"); }, "test: cancelSearch" : function() { iSearch.activate(editor); iSearch.addChar('1'); iSearch.addChar('2'); - var range = iSearch.cancelSearch(); + var range = iSearch.cancelSearch(true); testRanges("Range: [0/0] -> [0/0]", [range], "range"); iSearch.addChar('1'); range = iSearch.addChar('2'); From 901585024b89da34e42c5887b0b8c16cc8745ca0 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 10:08:34 -0700 Subject: [PATCH 04/32] [incremental search] refining forward/backward behavior --- lib/ace/incremental_search.js | 13 +++++++------ lib/ace/incremental_search_test.js | 7 +++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 0c3d61a2..2018034b 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -105,14 +105,15 @@ oop.inherits(IncrementalSearch, Search); } else { options.start = moveToNext ? range.start : range.end; } - range = this.find(session) || range; + var oldRange = this.$editor.selection.toOrientedRange(); + var found = this.find(session); // if (dir === "backward") range = Range.fromPoints(range.end, range.start); - this.$editor.selection.setRange(range, options.backwards); - this.$currentRange = range; + this.$editor.selection.setRange(found || oldRange || range, options.backwards); + if (found && moveToNext) this.$currentRange = found; session.highlight(options.re); - return range; + return found || oldRange || range; } this.addChar = function(c) { @@ -128,6 +129,7 @@ oop.inherits(IncrementalSearch, Search); } iSearch.next = function(backwards) { + this.$currentRange = this.$editor.selection.toOrientedRange(); this.$options.backwards = backwards; return this.highlightAndFindWithNeedle(true); } @@ -173,8 +175,7 @@ oop.inherits(IncrementalSearch, Search); if (key === 's' || key === 'r') { if (this.$options.needle.length === 0) this.$options.needle = this.$prevNeedle || ''; - this.$options.backwards = key === 'r'; - this.forward(); + this.next(key === 'r'); result = stop; } if (key === 'g') { this.deactivate(true); this.message(''); return stop; } diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index 58d927c2..0f48060d 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -130,6 +130,13 @@ module.exports = { iSearch.addChar('1'); iSearch.addChar('2'); var range = iSearch.addChar('x'); testRanges("Range: [0/3] -> [0/5]", [range], "range"); + }, + + "test: backwards search" : function() { + editor.moveCursorTo(1,0); + iSearch.activate(editor, true); + iSearch.addChar('1'); var range = iSearch.addChar('2');; + testRanges("Range: [0/3] -> [0/5]", [range], "range"); } // // "test: find simple text in document" : function() { From 4708bac758d2ead61366384fe28d35acd26d9dc8 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 10:50:12 -0700 Subject: [PATCH 05/32] [incremental search] properly forward/backward wrapping on same term --- .../editor_highlight_selected_word_test.js | 4 +-- lib/ace/incremental_search.js | 32 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/ace/editor_highlight_selected_word_test.js b/lib/ace/editor_highlight_selected_word_test.js index 864de9a1..13e19c23 100644 --- a/lib/ace/editor_highlight_selected_word_test.js +++ b/lib/ace/editor_highlight_selected_word_test.js @@ -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 diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 2018034b..48135519 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -67,7 +67,8 @@ oop.inherits(IncrementalSearch, Search); iSearch.activate = function(editor, backwards) { this.$editor = editor; var pos = editor.getCursorPosition(); - this.$startRange = this.$currentRange = Range.fromPoints(pos, pos); + this.$startPos = this.$currentPos = pos; + // this.$startRange = this.$currentRange = Range.fromPoints(pos, pos); // this.$startRange = this.$currentRange = editor.selection.toOrientedRange(); this.installKeyboardHandler(editor); this.$options.needle = ''; @@ -85,9 +86,13 @@ oop.inherits(IncrementalSearch, Search); this.$prevNeedle = this.$options.needle; this.$options.needle = ''; e.session.highlight(null); - if (reset) e.selection.setRange(this.$startRange); - else e.selection.clearSelection(); - return this.$currentRange = this.$startRange; + if (reset) { + e.moveCursorTo(this.$startPos); + this.$currentPos = this.$startPos; + } else { + e.selection.clearSelection(); + } + return Range.fromPoints(this.$currentPos, this.$currentPos); } iSearch.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) { @@ -99,21 +104,17 @@ oop.inherits(IncrementalSearch, Search); } // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - var session = this.$editor.session, range = this.$currentRange; - if (!options.backwards) { - options.start = moveToNext ? range.end : range.start; - } else { - options.start = moveToNext ? range.start : range.end; - } + var session = this.$editor.session, pos = this.$currentPos; + options.start = pos; var oldRange = this.$editor.selection.toOrientedRange(); var found = this.find(session); // if (dir === "backward") range = Range.fromPoints(range.end, range.start); - this.$editor.selection.setRange(found || oldRange || range, options.backwards); - if (found && moveToNext) this.$currentRange = found; + this.$editor.selection.setRange(found || oldRange, options.backwards); + if (found && moveToNext) this.$currentPos = options.backwards ? found.end : found.start; session.highlight(options.re); - return found || oldRange || range; + return found || oldRange; } this.addChar = function(c) { @@ -129,7 +130,8 @@ oop.inherits(IncrementalSearch, Search); } iSearch.next = function(backwards) { - this.$currentRange = this.$editor.selection.toOrientedRange(); + var currentRange = this.$editor.selection.toOrientedRange(); + this.$currentPos = this.$options.backwards ? currentRange.start : currentRange.end; this.$options.backwards = backwards; return this.highlightAndFindWithNeedle(true); } @@ -184,7 +186,7 @@ oop.inherits(IncrementalSearch, Search); // return stop; } - this.message('isearch: ' + this.$options.needle); + this.message((this.$options.backwards ? 'reverse-' : '') + 'isearch: ' + this.$options.needle); // this.deactivate(); return result; From ed9713df3dfd48e16123080673cedce61c17b909 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 12:22:19 -0700 Subject: [PATCH 06/32] [incremental search] don't use selection ranges for marking finds --- lib/ace/incremental_search.js | 57 +++++++++++++----------------- lib/ace/incremental_search_test.js | 37 ++++++++++--------- 2 files changed, 45 insertions(+), 49 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 48135519..87630f2e 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -68,8 +68,6 @@ oop.inherits(IncrementalSearch, Search); this.$editor = editor; var pos = editor.getCursorPosition(); this.$startPos = this.$currentPos = pos; - // this.$startRange = this.$currentRange = Range.fromPoints(pos, pos); - // this.$startRange = this.$currentRange = editor.selection.toOrientedRange(); this.installKeyboardHandler(editor); this.$options.needle = ''; this.$options.backwards = backwards; @@ -87,10 +85,10 @@ oop.inherits(IncrementalSearch, Search); this.$options.needle = ''; e.session.highlight(null); if (reset) { - e.moveCursorTo(this.$startPos); + e.moveCursorToPosition(this.$startPos); this.$currentPos = this.$startPos; } else { - e.selection.clearSelection(); + e.renderer.updateFull(true); // for highlight } return Range.fromPoints(this.$currentPos, this.$currentPos); } @@ -98,23 +96,28 @@ oop.inherits(IncrementalSearch, Search); iSearch.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) { if (!this.$editor) return null; var options = this.$options; + + // get search term if (needleUpdateFunc) options.needle = needleUpdateFunc(options.needle || '') || ''; - if (options.needle.length === 0) { - return this.cancelSearch(true); - } - - // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - var session = this.$editor.session, pos = this.$currentPos; - options.start = pos; - var oldRange = this.$editor.selection.toOrientedRange(); - var found = this.find(session); - - // if (dir === "backward") range = Range.fromPoints(range.end, range.start); - this.$editor.selection.setRange(found || oldRange, options.backwards); - if (found && moveToNext) this.$currentPos = options.backwards ? found.end : found.start; + if (options.needle.length === 0) 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); session.highlight(options.re); - return found || oldRange; + this.$editor.renderer.updateFull(true); // force highlight layer redraw + if (found) { + if (options.backwards) found = Range.fromPoints(found.end, found.start); + this.$editor.moveCursorToPosition(found.end); + if (moveToNext) this.$currentPos = found.end; + } + console.log("searching %s from [%s/%s]: %s", + options.backwards ? 'backwards' : 'forwards', + options.start.row, options.start.column, + found ? found.toString() : 'found nothing'); + + return found; } this.addChar = function(c) { @@ -130,28 +133,19 @@ oop.inherits(IncrementalSearch, Search); } iSearch.next = function(backwards) { - var currentRange = this.$editor.selection.toOrientedRange(); - this.$currentPos = this.$options.backwards ? currentRange.start : currentRange.end; + // try to find the next occurence of whatever we have searched for + // earlier this.$options.backwards = backwards; + this.$currentPos = this.$editor.getCursorPosition(); return this.highlightAndFindWithNeedle(true); } iSearch.installKeyboardHandler = function(editor) { - // this.$origKeyboardHandlers = [].concat(editor.keyBinding.$handlers); - // this.$origKeyboardHandlers.reverse().forEach(function(handler) { - // editor.keyBinding.removeKeyboardHandler(handler); - // }); editor.keyBinding.addKeyboardHandler(this.$keyboardHandler); } iSearch.uninstallKeyboardHandler = function(editor) { editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler); - // if (this.$origKeyboardHandlers) { - // this.$origKeyboardHandlers.forEach(function(handler) { - // editor.keyBinding.addKeyboardHandler(handler); - // }); - // delete this.$origKeyboardHandlers; - // } } iSearch.message = function(msg) { @@ -162,8 +156,7 @@ oop.inherits(IncrementalSearch, Search); iSearch.handleKeyboard = function(data, hashId, key, keyCode) { this.message("data: " + data + ", hashId: " + hashId + ", key: " + key + ", keyCode: " + keyCode); - var stop = {command: 'null'}, - result = undefined; + var stop = {command: 'null'}, result = undefined; console.log(this.$options.needle); if (hashId === 0) { diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index 0f48060d..230c3c28 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -108,11 +108,12 @@ module.exports = { var range = iSearch.next(); testRanges("Range: [1/3] -> [1/5]", [range], "range"); - range = iSearch.next(); - testRanges("Range: [1/3] -> [1/5]", [range], "range"); + range = iSearch.next(); // nothing to find + testRanges("", [range], "range"); + assert.ok(!editor.selection.isBackwards(), 'reoriented?'); range = iSearch.next(true); // backwards - testRanges("Range: [0/3] -> [0/5]", [range], "range"); + testRanges("Range: [1/5] -> [1/3]", [range], "range"); }, "test: cancelSearch" : function() { @@ -125,30 +126,32 @@ module.exports = { testRanges("Range: [0/3] -> [0/5]", [range], "range"); }, - "test: failing search keeps range" : function() { + "test: failing search keeps pos" : function() { iSearch.activate(editor); iSearch.addChar('1'); iSearch.addChar('2'); var range = iSearch.addChar('x'); - testRanges("Range: [0/3] -> [0/5]", [range], "range"); + 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/3] -> [0/5]", [range], "range"); - } + testRanges("Range: [0/5] -> [0/3]", [range], "range"); + assert.position(editor.getCursorPosition(), 0, 3); + }, - // // "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); - // }, + "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(true); + testRanges("Range: [0/5] -> [0/3]", [range], "range"); + assert.position(editor.getCursorPosition(), 0, 3); + } }; From d314374a60d5fe569c66c015b9d3c7e23f7523f0 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 12:23:40 -0700 Subject: [PATCH 07/32] [incremental search] test fix --- lib/ace/incremental_search_test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index 230c3c28..ccabc051 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -110,7 +110,6 @@ module.exports = { range = iSearch.next(); // nothing to find testRanges("", [range], "range"); - assert.ok(!editor.selection.isBackwards(), 'reoriented?'); range = iSearch.next(true); // backwards testRanges("Range: [1/5] -> [1/3]", [range], "range"); From 2faf91223463d16781c47ab4eeb089b82a8be4af Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 12:45:36 -0700 Subject: [PATCH 08/32] [incremental search] renderer update for highlight markers --- lib/ace/incremental_search.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 87630f2e..ddcde695 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -83,13 +83,12 @@ oop.inherits(IncrementalSearch, Search); var e = this.$editor; this.$prevNeedle = this.$options.needle; this.$options.needle = ''; - e.session.highlight(null); if (reset) { e.moveCursorToPosition(this.$startPos); this.$currentPos = this.$startPos; - } else { - e.renderer.updateFull(true); // for highlight } + e.session.highlight(null); + this.$editor.renderer.updateBackMarkers(); // force highlight layer redraw return Range.fromPoints(this.$currentPos, this.$currentPos); } @@ -105,11 +104,11 @@ oop.inherits(IncrementalSearch, Search); options.start = this.$currentPos; var session = this.$editor.session, found = this.find(session); - session.highlight(options.re); - this.$editor.renderer.updateFull(true); // force highlight layer redraw if (found) { if (options.backwards) found = Range.fromPoints(found.end, found.start); + session.highlight(options.re); this.$editor.moveCursorToPosition(found.end); + this.$editor.renderer.updateBackMarkers(); // force highlight layer redraw if (moveToNext) this.$currentPos = found.end; } console.log("searching %s from [%s/%s]: %s", @@ -163,6 +162,7 @@ oop.inherits(IncrementalSearch, Search); if (key === 'backspace') { this.removeChar(); result = stop; } if (key === 'return') { this.deactivate(); this.message(''); return stop; } if (key === 'esc') { this.deactivate(true); this.message(''); return stop; } + if (key === 'space') { this.addChar(" "); result = stop; } if (key.length === 1) { this.addChar(key); result = stop; } } From 35a006696590f526d0482b40946c822c8b010ef5 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 12:53:59 -0700 Subject: [PATCH 09/32] [incremental search] disable other keyboard handlers --- lib/ace/incremental_search.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index ddcde695..d1b8083f 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -106,10 +106,12 @@ oop.inherits(IncrementalSearch, Search); found = this.find(session); if (found) { if (options.backwards) found = Range.fromPoints(found.end, found.start); - session.highlight(options.re); this.$editor.moveCursorToPosition(found.end); - this.$editor.renderer.updateBackMarkers(); // force highlight layer redraw if (moveToNext) this.$currentPos = found.end; + // highlight after cursor move, so selection works properly + // also force highlight layer redraw + session.highlight(options.re); + this.$editor.renderer.updateBackMarkers(); } console.log("searching %s from [%s/%s]: %s", options.backwards ? 'backwards' : 'forwards', @@ -155,7 +157,7 @@ oop.inherits(IncrementalSearch, Search); iSearch.handleKeyboard = function(data, hashId, key, keyCode) { this.message("data: " + data + ", hashId: " + hashId + ", key: " + key + ", keyCode: " + keyCode); - var stop = {command: 'null'}, result = undefined; + var stop = {command: 'null'}, result = stop; console.log(this.$options.needle); if (hashId === 0) { From eab7df5a3d01cdce24fe4ae1f8a0d61fdf103f98 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 19:32:20 -0700 Subject: [PATCH 10/32] [incremental search] cleaning up keyboard handling --- lib/ace/incremental_search.js | 108 +++++++++++------------------ lib/ace/incremental_search_test.js | 15 +++- 2 files changed, 54 insertions(+), 69 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index d1b8083f..0faee1d9 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -34,6 +34,7 @@ define(function(require, exports, module) { var oop = require("./lib/oop"); var Range = require("./range").Range; var Search = require("./search").Search; +var ISearchKbd = require("./commands/incremental_search_commands").IncrementalSearchKeyboardHandler; /** * @class IncrementalSearch @@ -46,40 +47,42 @@ var Search = require("./search").Search; * **/ + /** * * - * Creates a new `IncrementalSearch` object. Options: + * Creates a new `IncrementalSearch` object. * * @constructor **/ function IncrementalSearch() { this.$options = {wrap: false, skipCurrent: false}; - this.$keyboardHandler = this; + this.$keyboardHandler = new ISearchKbd(this); } oop.inherits(IncrementalSearch, Search); ;(function() { - var iSearch = this; - - iSearch.activate = function(editor, backwards) { + this.activate = function(editor, backwards) { this.$editor = editor; - var pos = editor.getCursorPosition(); - this.$startPos = this.$currentPos = pos; - this.installKeyboardHandler(editor); + this.$startPos = this.$currentPos = editor.getCursorPosition(); this.$options.needle = ''; this.$options.backwards = backwards; + editor.keyBinding.addKeyboardHandler(this.$keyboardHandler); + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + var msg = this.$options.backwards ? 'reverse-' : ''; + msg += 'isearch: ' + this.$options.needle; + this.message(msg); } - iSearch.deactivate = function(reset) { + this.deactivate = function(reset) { this.cancelSearch(reset); - this.uninstallKeyboardHandler(this.$editor); - delete this.$editor; + this.$editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler); + this.message(''); } - iSearch.cancelSearch = function(reset) { + this.cancelSearch = function(reset) { var e = this.$editor; this.$prevNeedle = this.$options.needle; this.$options.needle = ''; @@ -88,16 +91,18 @@ oop.inherits(IncrementalSearch, Search); this.$currentPos = this.$startPos; } e.session.highlight(null); - this.$editor.renderer.updateBackMarkers(); // force highlight layer redraw + e.renderer.updateBackMarkers(); // force highlight layer redraw return Range.fromPoints(this.$currentPos, this.$currentPos); } - iSearch.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) { + this.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) { if (!this.$editor) return null; var options = this.$options; // get search term - if (needleUpdateFunc) options.needle = needleUpdateFunc(options.needle || '') || ''; + if (needleUpdateFunc) { + options.needle = needleUpdateFunc.call(this, options.needle || '') || ''; + } if (options.needle.length === 0) return this.cancelSearch(true); // try to find the next occurence and enable highlighting marker @@ -113,10 +118,11 @@ oop.inherits(IncrementalSearch, Search); session.highlight(options.re); this.$editor.renderer.updateBackMarkers(); } - console.log("searching %s from [%s/%s]: %s", - options.backwards ? 'backwards' : 'forwards', - options.start.row, options.start.column, - found ? found.toString() : 'found nothing'); + + var msg = options.backwards ? 'reverse-' : ''; + msg += 'isearch: ' + options.needle; + if (!found) msg += ' (not found)'; + this.message(msg); return found; } @@ -127,64 +133,32 @@ oop.inherits(IncrementalSearch, Search); }); } - iSearch.removeChar = function(c) { + this.removeChar = function(c) { return this.highlightAndFindWithNeedle(false, function(needle) { return needle.length > 0 ? needle.substring(0, needle.length-1) : needle; }); } - iSearch.next = function(backwards) { + this.next = function(options) { // try to find the next occurence of whatever we have searched for - // earlier - this.$options.backwards = backwards; + // earlier. + // options = {[backwards: BOOL], [useCurrentOrPrevSearch: BOOL]} + options = options || {}; + this.$options.backwards = !!options.backwards; this.$currentPos = this.$editor.getCursorPosition(); - return this.highlightAndFindWithNeedle(true); + return this.highlightAndFindWithNeedle(true, function(needle) { + return options.useCurrentOrPrevSearch && needle.length === 0 ? + this.$prevNeedle || '' : needle; + }); } - iSearch.installKeyboardHandler = function(editor) { - editor.keyBinding.addKeyboardHandler(this.$keyboardHandler); - } - - iSearch.uninstallKeyboardHandler = function(editor) { - editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler); - } - - iSearch.message = function(msg) { - console.log(msg); - if (this.commandLine) - this.commandLine.setValue(msg, 1); - } - - iSearch.handleKeyboard = function(data, hashId, key, keyCode) { - this.message("data: " + data + ", hashId: " + hashId + ", key: " + key + ", keyCode: " + keyCode); - var stop = {command: 'null'}, result = stop; - - console.log(this.$options.needle); - if (hashId === 0) { - if (key === 'backspace') { this.removeChar(); result = stop; } - if (key === 'return') { this.deactivate(); this.message(''); return stop; } - if (key === 'esc') { this.deactivate(true); this.message(''); return stop; } - if (key === 'space') { this.addChar(" "); result = stop; } - if (key.length === 1) { this.addChar(key); result = stop; } + this.message = function(msg) { + var cmdLine = this.$editor && this.$editor.cmdLine; + if (cmdLine) { + cmdLine.setValue(msg, 1); + } else { + console.log(msg); } - - if (hashId === 1) { - if (key === 's' || key === 'r') { - if (this.$options.needle.length === 0) - this.$options.needle = this.$prevNeedle || ''; - this.next(key === 'r'); - result = stop; - } - if (key === 'g') { this.deactivate(true); this.message(''); return stop; } - // let others handle but don't deactivate iSearch - // stop.passEvent = true; - // return stop; - } - - this.message((this.$options.backwards ? 'reverse-' : '') + 'isearch: ' + this.$options.needle); - // this.deactivate(); - - return result; } diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index ccabc051..4d064f75 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -111,7 +111,7 @@ module.exports = { range = iSearch.next(); // nothing to find testRanges("", [range], "range"); - range = iSearch.next(true); // backwards + range = iSearch.next({backwards: true}); // backwards testRanges("Range: [1/5] -> [1/3]", [range], "range"); }, @@ -147,9 +147,20 @@ module.exports = { testRanges("Range: [0/3] -> [0/5]", [range], "range"); assert.position(editor.getCursorPosition(), 0, 5); - range = iSearch.next(true); + 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); } }; From 6f68392f673764f3e756137e159b31fe074eded0 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 19:33:41 -0700 Subject: [PATCH 11/32] emacs key handler has a flag for making it recognizable --- lib/ace/keyboard/emacs.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 65fc5840..c11837e2 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -49,6 +49,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; From 4d09567bc04afe6926ad833473dc9eb27a59750c Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 19:34:27 -0700 Subject: [PATCH 12/32] [incremental search] adding config option for enabling/disabling it --- lib/ace/incremental_search.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 0faee1d9..5d439c81 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -167,4 +167,33 @@ oop.inherits(IncrementalSearch, Search); exports.IncrementalSearch = IncrementalSearch; + +var Editor = require("./editor").Editor; +require("./config").defineOptions(Editor.prototype, "editor", { + useIncrementalSearch: { + set: function(val) { + var iSearchCommands = require("ace/commands/incremental_search_commands").iSearchStartCommands; + var kbd = ace.getKeyboardHandler(); + if (val) { + // enable for whole editor + this.commands.addCommands(iSearchCommands); + if (kbd.isEmacs) { // adapt emacs key handler if used + kbd.oldSearchBindings = { + 'c-s': kbd.commmandKeyBinding['c-s'], + 'c-r': kbd.commmandKeyBinding['c-r'] + } + kbd.bindKey('C-s', 'iSearch'); + kbd.bindKey('C-r', 'iSearchBackwards'); + } + } else { + this.commands.removeCommands(iSearchCommands); + if (kbd.isEmacs) { + kbd.bindKey('C-s', kbd.oldSearchBindings['c-s']); + kbd.bindKey('C-r', kbd.oldSearchBindings['c-r']); + } + } + } + } +}); + }); From 8a62ad8be0e321b76f9e2ca69db38605692eb920 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 19:35:18 -0700 Subject: [PATCH 13/32] [incremental search] extending kitchen sink to use isearch --- demo/kitchen-sink/demo.js | 14 ++++++++------ kitchen-sink.html | 8 ++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 26e96767..8ef6ec68 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -64,6 +64,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"); @@ -83,9 +85,6 @@ 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;\ @@ -171,7 +170,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", @@ -377,7 +376,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) { @@ -393,6 +392,10 @@ bindCheckbox("elastic_tabstops", function(checked) { env.editor.setOption("useElasticTabstops", checked); }); +bindCheckbox("isearch", function(checked) { + env.editor.setOption("useIncrementalSearch", checked); +}); + function synchroniseScrolling() { var s1 = env.split.$editors[0].session; @@ -448,4 +451,3 @@ var StatusBar = require("./statusbar").StatusBar; new StatusBar(env.editor, cmdLine.container); }); - diff --git a/kitchen-sink.html b/kitchen-sink.html index 33bc19a7..b97aea75 100644 --- a/kitchen-sink.html +++ b/kitchen-sink.html @@ -249,6 +249,14 @@ + + + + + + + + From 5b0c216580819d05b008da2370ea679010068c87 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 19:56:16 -0700 Subject: [PATCH 14/32] [incremental search] style patch for better highlights --- lib/ace/incremental_search.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 5d439c81..806622a3 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -168,6 +168,32 @@ oop.inherits(IncrementalSearch, Search); exports.IncrementalSearch = IncrementalSearch; +/** + * + * Config settings for enabling/disabling [[IncrementalSearch `IncrementalSearch`]]. + * + **/ + +function patchHighlightMarkerStyling(options) { + options = options || {}; + var id = 'incremental-search-highlight-style-patch', + style = document.getElementById(id); + if (style) { + if (options.enable) return; + style.parentNode.removeChild(style); + return; + } + if (!options.enable) return; + style = document.createElement('style'); + style.setAttribute('id', id); + style.textContent = "div.ace_selected-word {\n" + + " background-color: orange !important;\n" + + " border: 0 !important;" + + "}\n" + document.getElementsByTagName('head')[0].appendChild(style); +} + + var Editor = require("./editor").Editor; require("./config").defineOptions(Editor.prototype, "editor", { useIncrementalSearch: { @@ -177,6 +203,7 @@ require("./config").defineOptions(Editor.prototype, "editor", { if (val) { // enable for whole editor this.commands.addCommands(iSearchCommands); + patchHighlightMarkerStyling({enable: true}); if (kbd.isEmacs) { // adapt emacs key handler if used kbd.oldSearchBindings = { 'c-s': kbd.commmandKeyBinding['c-s'], @@ -187,6 +214,7 @@ require("./config").defineOptions(Editor.prototype, "editor", { } } else { this.commands.removeCommands(iSearchCommands); + patchHighlightMarkerStyling({enable: false}); if (kbd.isEmacs) { kbd.bindKey('C-s', kbd.oldSearchBindings['c-s']); kbd.bindKey('C-r', kbd.oldSearchBindings['c-r']); From 6141a63ba392bffd83c75de0f8a4b2edba6435b4 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 20:48:34 -0700 Subject: [PATCH 15/32] [incremental search] cleanup --- lib/ace/incremental_search.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 806622a3..1fdf6515 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -199,11 +199,11 @@ require("./config").defineOptions(Editor.prototype, "editor", { useIncrementalSearch: { set: function(val) { var iSearchCommands = require("ace/commands/incremental_search_commands").iSearchStartCommands; - var kbd = ace.getKeyboardHandler(); + var kbd = this.getKeyboardHandler(); + patchHighlightMarkerStyling({enable: val}); if (val) { // enable for whole editor this.commands.addCommands(iSearchCommands); - patchHighlightMarkerStyling({enable: true}); if (kbd.isEmacs) { // adapt emacs key handler if used kbd.oldSearchBindings = { 'c-s': kbd.commmandKeyBinding['c-s'], @@ -214,8 +214,7 @@ require("./config").defineOptions(Editor.prototype, "editor", { } } else { this.commands.removeCommands(iSearchCommands); - patchHighlightMarkerStyling({enable: false}); - if (kbd.isEmacs) { + if (kbd.isEmacs && kbd.oldSearchBindings) { kbd.bindKey('C-s', kbd.oldSearchBindings['c-s']); kbd.bindKey('C-r', kbd.oldSearchBindings['c-r']); } From 438e3c985c769a664157779a70dc5a2e36dc83d4 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 10 Mar 2013 21:10:43 -0700 Subject: [PATCH 16/32] [incremental search] oops, forgot to add isearch commands --- .../commands/incremental_search_commands.js | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 lib/ace/commands/incremental_search_commands.js diff --git a/lib/ace/commands/incremental_search_commands.js b/lib/ace/commands/incremental_search_commands.js new file mode 100644 index 00000000..f0514947 --- /dev/null +++ b/lib/ace/commands/incremental_search_commands.js @@ -0,0 +1,176 @@ +/* ***** 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.$editor.execCommand("extendSearchTerm", ' '); }, + 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) { + console.log("data: " + data + + ", hashId: " + hashId + + ", key: " + key + + ", keyCode: " + keyCode); + var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode); + if (cmd.command) { return cmd; } + if (key.length === 1 && !(/[\x00-\x1F]/.test(key))) { + var extendCmd = this.commands.extendSearchTerm; + if (extendCmd) { return {command: extendCmd, args: key}; } + } + return {command: "null"}; + } + +}).call(IncrementalSearchKeyboardHandler.prototype); + + +exports.IncrementalSearchKeyboardHandler = IncrementalSearchKeyboardHandler; + +}); From 1daaeb3a797a66ecad15380fab11a7101b4b0335 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 15:07:53 -0700 Subject: [PATCH 17/32] [incremental search] polymorphic setup invocation for keyboard handlers --- lib/ace/incremental_search.js | 53 ++++++++++++++++++++++------------- lib/ace/keyboard/emacs.js | 4 +++ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 1fdf6515..1587e77f 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -34,7 +34,8 @@ define(function(require, exports, module) { var oop = require("./lib/oop"); var Range = require("./range").Range; var Search = require("./search").Search; -var ISearchKbd = require("./commands/incremental_search_commands").IncrementalSearchKeyboardHandler; +var iSearchCommandModule = require("./commands/incremental_search_commands"); +var ISearchKbd = iSearchCommandModule.IncrementalSearchKeyboardHandler; /** * @class IncrementalSearch @@ -194,31 +195,43 @@ function patchHighlightMarkerStyling(options) { } +// support for default keyboard handler +var CommandManager = require("./commands/command_manager").CommandManager; +(function() { + this.setupIncrementalSearch = function(editor, val) { + if (this.usesIncrementalSearch == val) return; + this.usesIncrementalSearch = val; + var iSearchCommands = iSearchCommandModule.iSearchStartCommands, + method = val ? 'addCommands' : 'removeCommands'; + this[method](iSearchCommands); + }; +}).call(CommandManager.prototype); + +// support for emacskeyboard handler +var emacs = require("./keyboard/emacs"); +emacs.handler.setupIncrementalSearch = function(editor, val) { + if (this.usesIncrementalSearch == val) return; + this.usesIncrementalSearch = val; + if (val) { + this.bindKey('C-s', 'iSearch'); + this.bindKey('C-r', 'iSearchBackwards'); + } else { + this.bindKey('C-s', "findnext"); + this.bindKey('C-r', "findprevious"); + } +} + +// incremental search config option var Editor = require("./editor").Editor; require("./config").defineOptions(Editor.prototype, "editor", { useIncrementalSearch: { set: function(val) { - var iSearchCommands = require("ace/commands/incremental_search_commands").iSearchStartCommands; - var kbd = this.getKeyboardHandler(); patchHighlightMarkerStyling({enable: val}); - if (val) { - // enable for whole editor - this.commands.addCommands(iSearchCommands); - if (kbd.isEmacs) { // adapt emacs key handler if used - kbd.oldSearchBindings = { - 'c-s': kbd.commmandKeyBinding['c-s'], - 'c-r': kbd.commmandKeyBinding['c-r'] - } - kbd.bindKey('C-s', 'iSearch'); - kbd.bindKey('C-r', 'iSearchBackwards'); + this.keyBinding.$handlers.forEach(function(handler) { + if (handler.setupIncrementalSearch) { + handler.setupIncrementalSearch(this, val); } - } else { - this.commands.removeCommands(iSearchCommands); - if (kbd.isEmacs && kbd.oldSearchBindings) { - kbd.bindKey('C-s', kbd.oldSearchBindings['c-s']); - kbd.bindKey('C-r', kbd.oldSearchBindings['c-r']); - } - } + }); } } }); diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index c11837e2..36c7e6c7 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -108,6 +108,10 @@ exports.handler.attach = function(editor) { editor.commands.addCommands(commands); exports.handler.platform = editor.commands.platform; editor.$emacsModeHandler = this; + var hasISearch = editor.getOption('useIncrementalSearch'); + if (hasISearch != this.usesIncrementalSearch) { + this.setupIncrementalSearch(editor, hasISearch); + }; }; exports.handler.detach = function(editor) { From 724193dcbc381c601533f27094569d163c5ab6e4 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 16:49:21 -0700 Subject: [PATCH 18/32] [incremental search] fixing when / when not selection should be extended --- lib/ace/incremental_search.js | 11 +++++++++++ lib/ace/incremental_search_test.js | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 1587e77f..e158b6d5 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -71,6 +71,7 @@ oop.inherits(IncrementalSearch, Search); this.$options.needle = ''; this.$options.backwards = backwards; editor.keyBinding.addKeyboardHandler(this.$keyboardHandler); + this.selectionFix(editor); // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- var msg = this.$options.backwards ? 'reverse-' : ''; msg += 'isearch: ' + this.$options.needle; @@ -83,6 +84,16 @@ oop.inherits(IncrementalSearch, Search); 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.cancelSearch = function(reset) { var e = this.$editor; this.$prevNeedle = this.$options.needle; diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index 4d064f75..66129991 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -161,6 +161,28 @@ module.exports = { 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('ace/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"); } }; From 20ff66cf04e1f8f3186e78c74741a0e38d148c4f Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 17:58:39 -0700 Subject: [PATCH 19/32] [incremental search] using dom.importCssString --- lib/ace/incremental_search.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index e158b6d5..c7227cdc 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -186,23 +186,15 @@ exports.IncrementalSearch = IncrementalSearch; * **/ +var dom = require('./lib/dom'); function patchHighlightMarkerStyling(options) { options = options || {}; var id = 'incremental-search-highlight-style-patch', - style = document.getElementById(id); - if (style) { - if (options.enable) return; - style.parentNode.removeChild(style); - return; - } - if (!options.enable) return; - style = document.createElement('style'); - style.setAttribute('id', id); - style.textContent = "div.ace_selected-word {\n" - + " background-color: orange !important;\n" - + " border: 0 !important;" - + "}\n" - document.getElementsByTagName('head')[0].appendChild(style); + css = 'div.ace_selected-word {\n' + + ' background-color: orange !important;\n' + + ' border: 0 !important;\n' + + '}\n' + dom.importCssString(css, id); } From f6ba62083e444987a2491125f18587fbac40df71 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 18:17:04 -0700 Subject: [PATCH 20/32] [incremental search] fixing require in test for async --- lib/ace/incremental_search_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index 66129991..1f8d78c0 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -177,7 +177,7 @@ module.exports = { }, "test: extend selection in emacs mark mode" : function() { - var emacs = require('ace/keyboard/emacs'); + var emacs = require('./keyboard/emacs'); editor.keyBinding.addKeyboardHandler(emacs.handler); emacs.handler.commands.setMark.exec(editor); iSearch.activate(editor); From 116e42c29ae990a22bb70c52e2a9e6280e11ea7f Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 18:39:11 -0700 Subject: [PATCH 21/32] [incremental search] adding fix for handleKeyboard from @nightwing --- lib/ace/commands/incremental_search_commands.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/commands/incremental_search_commands.js b/lib/ace/commands/incremental_search_commands.js index f0514947..986fb56e 100644 --- a/lib/ace/commands/incremental_search_commands.js +++ b/lib/ace/commands/incremental_search_commands.js @@ -161,11 +161,11 @@ oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); + ", keyCode: " + keyCode); var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode); if (cmd.command) { return cmd; } - if (key.length === 1 && !(/[\x00-\x1F]/.test(key))) { + if (hashId == -1) { var extendCmd = this.commands.extendSearchTerm; if (extendCmd) { return {command: extendCmd, args: key}; } } - return {command: "null"}; + return {command: "null", passEvent: hashId == 0 || hashId == 4}; } }).call(IncrementalSearchKeyboardHandler.prototype); From 6fcebcf1e905c31000871e035e934d7f0582b206 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 18:39:39 -0700 Subject: [PATCH 22/32] [incremental search] fix for space --- lib/ace/commands/incremental_search_commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/commands/incremental_search_commands.js b/lib/ace/commands/incremental_search_commands.js index 986fb56e..d7c99feb 100644 --- a/lib/ace/commands/incremental_search_commands.js +++ b/lib/ace/commands/incremental_search_commands.js @@ -98,7 +98,7 @@ exports.iSearchCommands = [{ }, { name: "extendSearchTermSpace", bindKey: "space", - exec: function(iSearch) { iSearch.$editor.execCommand("extendSearchTerm", ' '); }, + exec: function(iSearch) { iSearch.addChar(' '); }, readOnly: true, isIncrementalSearchCommand: true }, { From 0bbfdafec399a646440254c309a87812a9830d90 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 18:39:47 -0700 Subject: [PATCH 23/32] remove debug code --- lib/ace/commands/incremental_search_commands.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/ace/commands/incremental_search_commands.js b/lib/ace/commands/incremental_search_commands.js index d7c99feb..1cce3912 100644 --- a/lib/ace/commands/incremental_search_commands.js +++ b/lib/ace/commands/incremental_search_commands.js @@ -155,10 +155,6 @@ oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); var handleKeyboard$super = this.handleKeyboard; this.handleKeyboard = function(data, hashId, key, keyCode) { - console.log("data: " + data - + ", hashId: " + hashId - + ", key: " + key - + ", keyCode: " + keyCode); var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode); if (cmd.command) { return cmd; } if (hashId == -1) { From 569563aa5f9a9099ba15444bf943ee4cd0b7f5ed Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 22:36:19 -0700 Subject: [PATCH 24/32] [incremental search] quit isearch on mousedown --- lib/ace/incremental_search.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index c7227cdc..a9d7c36f 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -71,6 +71,7 @@ oop.inherits(IncrementalSearch, Search); this.$options.needle = ''; this.$options.backwards = backwards; editor.keyBinding.addKeyboardHandler(this.$keyboardHandler); + this.$mousedownHandler = editor.addEventListener('mousedown', this.onMouseDown.bind(this)); this.selectionFix(editor); // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- var msg = this.$options.backwards ? 'reverse-' : ''; @@ -81,6 +82,10 @@ oop.inherits(IncrementalSearch, Search); 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(''); } @@ -94,6 +99,7 @@ oop.inherits(IncrementalSearch, Search); editor.clearSelection(); } } + this.cancelSearch = function(reset) { var e = this.$editor; this.$prevNeedle = this.$options.needle; @@ -164,6 +170,12 @@ oop.inherits(IncrementalSearch, Search); }); } + this.onMouseDown = function(evt) { + // when mouse interaction happens then we quit incremental search + this.deactivate(); + return true; + } + this.message = function(msg) { var cmdLine = this.$editor && this.$editor.cmdLine; if (cmdLine) { @@ -173,7 +185,6 @@ oop.inherits(IncrementalSearch, Search); } } - }).call(IncrementalSearch.prototype); From 5cc2c50ebbb1475f060b71c0b91820022ecc6c19 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sat, 16 Mar 2013 23:51:33 -0700 Subject: [PATCH 25/32] [incremental search] emacs uses isearch by default --- demo/kitchen-sink/demo.js | 6 +++++- demo/kitchen-sink/util.js | 2 +- lib/ace/incremental_search.js | 1 + lib/ace/keyboard/emacs.js | 7 +++---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 8ef6ec68..476a53c8 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -392,10 +392,14 @@ bindCheckbox("elastic_tabstops", function(checked) { env.editor.setOption("useElasticTabstops", checked); }); -bindCheckbox("isearch", function(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; diff --git a/demo/kitchen-sink/util.js b/demo/kitchen-sink/util.js index 28525e1a..752e79ba 100644 --- a/demo/kitchen-sink/util.js +++ b/demo/kitchen-sink/util.js @@ -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) { }); - diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index a9d7c36f..30ecfc18 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -246,6 +246,7 @@ require("./config").defineOptions(Editor.prototype, "editor", { handler.setupIncrementalSearch(this, val); } }); + this._emit('incrementalSearchSettingChanged', {isEnabled: val}); } } }); diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 36c7e6c7..218d730d 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -108,10 +108,9 @@ exports.handler.attach = function(editor) { editor.commands.addCommands(commands); exports.handler.platform = editor.commands.platform; editor.$emacsModeHandler = this; - var hasISearch = editor.getOption('useIncrementalSearch'); - if (hasISearch != this.usesIncrementalSearch) { - this.setupIncrementalSearch(editor, hasISearch); - }; + require('../incremental_search'); + editor.setOption('useIncrementalSearch', true); + this.setupIncrementalSearch(editor, true); }; exports.handler.detach = function(editor) { From e5fc2ea85374b9fefd2a8e568603134d70b57815 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 17 Mar 2013 04:59:18 -0700 Subject: [PATCH 26/32] [incremental search] cleanup && fixing module dependencies --- lib/ace/incremental_search.js | 44 ++++++++++++++++++----------------- lib/ace/test/all_browser.js | 2 +- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 30ecfc18..4fefcb0d 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -210,30 +210,32 @@ function patchHighlightMarkerStyling(options) { // support for default keyboard handler -var CommandManager = require("./commands/command_manager").CommandManager; -(function() { - this.setupIncrementalSearch = function(editor, val) { - if (this.usesIncrementalSearch == val) return; - this.usesIncrementalSearch = val; - var iSearchCommands = iSearchCommandModule.iSearchStartCommands, - method = val ? 'addCommands' : 'removeCommands'; - this[method](iSearchCommands); - }; -}).call(CommandManager.prototype); +require(["./commands/command_manager"], function(cmdMgr) { + (function() { + this.setupIncrementalSearch = function(editor, val) { + if (this.usesIncrementalSearch == val) return; + this.usesIncrementalSearch = val; + var iSearchCommands = iSearchCommandModule.iSearchStartCommands, + method = val ? 'addCommands' : 'removeCommands'; + this[method](iSearchCommands); + }; + }).call(cmdMgr.CommandManager.prototype); +}); // support for emacskeyboard handler -var emacs = require("./keyboard/emacs"); -emacs.handler.setupIncrementalSearch = function(editor, val) { - if (this.usesIncrementalSearch == val) return; - this.usesIncrementalSearch = val; - if (val) { - this.bindKey('C-s', 'iSearch'); - this.bindKey('C-r', 'iSearchBackwards'); - } else { - this.bindKey('C-s', "findnext"); - this.bindKey('C-r', "findprevious"); +require(["./keyboard/emacs"], function(emacs) { + emacs.handler.setupIncrementalSearch = function(editor, val) { + if (this.usesIncrementalSearch == val) return; + this.usesIncrementalSearch = val; + if (val) { + this.bindKey('C-s', 'iSearch'); + this.bindKey('C-r', 'iSearchBackwards'); + } else { + this.bindKey('C-s', "findnext"); + this.bindKey('C-r', "findprevious"); + } } -} +}); // incremental search config option var Editor = require("./editor").Editor; diff --git a/lib/ace/test/all_browser.js b/lib/ace/test/all_browser.js index b98d6558..4ce68ae3 100644 --- a/lib/ace/test/all_browser.js +++ b/lib/ace/test/all_browser.js @@ -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", @@ -45,7 +46,6 @@ 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" From 9f7cfde308d74629321ed852db35f920e2858d5b Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 17 Mar 2013 05:52:20 -0700 Subject: [PATCH 27/32] [incremental search] moving emacs specific code to emacs.js --- lib/ace/incremental_search.js | 33 ++++++++------------------------- lib/ace/keyboard/emacs.js | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 4fefcb0d..969ac9ba 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -208,34 +208,17 @@ function patchHighlightMarkerStyling(options) { dom.importCssString(css, id); } - // support for default keyboard handler -require(["./commands/command_manager"], function(cmdMgr) { - (function() { - this.setupIncrementalSearch = function(editor, val) { - if (this.usesIncrementalSearch == val) return; - this.usesIncrementalSearch = val; - var iSearchCommands = iSearchCommandModule.iSearchStartCommands, - method = val ? 'addCommands' : 'removeCommands'; - this[method](iSearchCommands); - }; - }).call(cmdMgr.CommandManager.prototype); -}); - -// support for emacskeyboard handler -require(["./keyboard/emacs"], function(emacs) { - emacs.handler.setupIncrementalSearch = function(editor, val) { +var commands = require("./commands/command_manager"); +(function() { + this.setupIncrementalSearch = function(editor, val) { if (this.usesIncrementalSearch == val) return; this.usesIncrementalSearch = val; - if (val) { - this.bindKey('C-s', 'iSearch'); - this.bindKey('C-r', 'iSearchBackwards'); - } else { - this.bindKey('C-s', "findnext"); - this.bindKey('C-r', "findprevious"); - } - } -}); + var iSearchCommands = iSearchCommandModule.iSearchStartCommands, + method = val ? 'addCommands' : 'removeCommands'; + this[method](iSearchCommands); + }; +}).call(commands.CommandManager.prototype); // incremental search config option var Editor = require("./editor").Editor; diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 218d730d..ff397fa2 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -108,9 +108,8 @@ exports.handler.attach = function(editor) { editor.commands.addCommands(commands); exports.handler.platform = editor.commands.platform; editor.$emacsModeHandler = this; - require('../incremental_search'); - editor.setOption('useIncrementalSearch', true); - this.setupIncrementalSearch(editor, true); + if (!editor.getOption('useIncrementalSearch')) editor.setOption('useIncrementalSearch', true) + else this.setupIncrementalSearch(editor, true);; }; exports.handler.detach = function(editor) { @@ -487,5 +486,17 @@ exports.killRing = { } }; +require('../incremental_search'); +exports.handler.setupIncrementalSearch = function(editor, val) { + if (this.usesIncrementalSearch == val) return; + this.usesIncrementalSearch = val; + if (val) { + this.bindKey('C-s', 'iSearch'); + this.bindKey('C-r', 'iSearchBackwards'); + } else { + this.bindKey('C-s', "findnext"); + this.bindKey('C-r', "findprevious"); + } +} }); From 832d30c97cc14e9cdc679872c0ecc058c431d79a Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 17 Mar 2013 06:04:19 -0700 Subject: [PATCH 28/32] [incremental search] fixing status-message-remove-last-char bug --- lib/ace/incremental_search.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 969ac9ba..aa75c592 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -73,10 +73,7 @@ oop.inherits(IncrementalSearch, Search); editor.keyBinding.addKeyboardHandler(this.$keyboardHandler); this.$mousedownHandler = editor.addEventListener('mousedown', this.onMouseDown.bind(this)); this.selectionFix(editor); - // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - var msg = this.$options.backwards ? 'reverse-' : ''; - msg += 'isearch: ' + this.$options.needle; - this.message(msg); + this.statusMessage(true); } this.deactivate = function(reset) { @@ -121,7 +118,10 @@ oop.inherits(IncrementalSearch, Search); if (needleUpdateFunc) { options.needle = needleUpdateFunc.call(this, options.needle || '') || ''; } - if (options.needle.length === 0) return this.cancelSearch(true); + 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; @@ -137,10 +137,7 @@ oop.inherits(IncrementalSearch, Search); this.$editor.renderer.updateBackMarkers(); } - var msg = options.backwards ? 'reverse-' : ''; - msg += 'isearch: ' + options.needle; - if (!found) msg += ' (not found)'; - this.message(msg); + this.statusMessage(found); return found; } @@ -176,6 +173,14 @@ oop.inherits(IncrementalSearch, Search); 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) { var cmdLine = this.$editor && this.$editor.cmdLine; if (cmdLine) { From 236a1e5f7b364dc75caf77869ace1e88e473769b Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 17 Mar 2013 06:10:32 -0700 Subject: [PATCH 29/32] [incremental search] cleaner render update --- lib/ace/incremental_search.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index aa75c592..efff41ea 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -106,7 +106,7 @@ oop.inherits(IncrementalSearch, Search); this.$currentPos = this.$startPos; } e.session.highlight(null); - e.renderer.updateBackMarkers(); // force highlight layer redraw + e.session._emit("changeBackMarker"); // force highlight layer redraw return Range.fromPoints(this.$currentPos, this.$currentPos); } @@ -134,7 +134,7 @@ oop.inherits(IncrementalSearch, Search); // highlight after cursor move, so selection works properly // also force highlight layer redraw session.highlight(options.re); - this.$editor.renderer.updateBackMarkers(); + session._emit("changeBackMarker"); } this.statusMessage(found); From a11c06a589fe978c74ad950ce24f952c53eb49ef Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 17 Mar 2013 06:22:50 -0700 Subject: [PATCH 30/32] [incremental search] using showCommandLine() --- lib/ace/incremental_search.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index efff41ea..24f68bc8 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -182,9 +182,9 @@ oop.inherits(IncrementalSearch, Search); } this.message = function(msg) { - var cmdLine = this.$editor && this.$editor.cmdLine; - if (cmdLine) { - cmdLine.setValue(msg, 1); + if (this.$editor.showCommandLine) { + this.$editor.showCommandLine(msg); + this.$editor.focus(); } else { console.log(msg); } From 69b125db10a7fc59a003e1a4a04dfa6ad7a904f5 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Sun, 17 Mar 2013 17:38:33 -0700 Subject: [PATCH 31/32] [incremental search] isearch highlight cleanup --- lib/ace/css/editor.css | 8 ++++++ lib/ace/incremental_search.js | 41 +++++++++++++++++++----------- lib/ace/incremental_search_test.js | 16 +++++++++++- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 81f8fbe6..3aebfb17 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -209,6 +209,14 @@ box-sizing: border-box; } +.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; +}} + .ace_line .ace_fold { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 24f68bc8..5564aa81 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -34,6 +34,7 @@ define(function(require, exports, module) { 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; @@ -65,14 +66,14 @@ oop.inherits(IncrementalSearch, Search); ;(function() { - this.activate = function(editor, backwards) { - this.$editor = editor; - this.$startPos = this.$currentPos = editor.getCursorPosition(); + this.activate = function(ed, backwards) { + this.$editor = ed; + this.$startPos = this.$currentPos = ed.getCursorPosition(); this.$options.needle = ''; this.$options.backwards = backwards; - editor.keyBinding.addKeyboardHandler(this.$keyboardHandler); - this.$mousedownHandler = editor.addEventListener('mousedown', this.onMouseDown.bind(this)); - this.selectionFix(editor); + ed.keyBinding.addKeyboardHandler(this.$keyboardHandler); + this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this)); + this.selectionFix(ed); this.statusMessage(true); } @@ -97,6 +98,14 @@ oop.inherits(IncrementalSearch, Search); } } + 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; @@ -105,8 +114,7 @@ oop.inherits(IncrementalSearch, Search); e.moveCursorToPosition(this.$startPos); this.$currentPos = this.$startPos; } - e.session.highlight(null); - e.session._emit("changeBackMarker"); // force highlight layer redraw + this.highlight(null); return Range.fromPoints(this.$currentPos, this.$currentPos); } @@ -132,9 +140,7 @@ oop.inherits(IncrementalSearch, Search); this.$editor.moveCursorToPosition(found.end); if (moveToNext) this.$currentPos = found.end; // highlight after cursor move, so selection works properly - // also force highlight layer redraw - session.highlight(options.re); - session._emit("changeBackMarker"); + this.highlight(options.re) } this.statusMessage(found); @@ -205,10 +211,15 @@ exports.IncrementalSearch = IncrementalSearch; var dom = require('./lib/dom'); function patchHighlightMarkerStyling(options) { options = options || {}; - var id = 'incremental-search-highlight-style-patch', - css = 'div.ace_selected-word {\n' - + ' background-color: orange !important;\n' - + ' border: 0 !important;\n' + var id = 'incremental-search-highlighting', + css = 'div.ace_isearch-result {\n' + + " border-radius: 4px;\n" + + " border: 8px solid rgba(255, 200, 0, 0.5);\n" + + " box-shadow: 0 0 4px rgb(255, 200, 0);\n" + + "}\n" + + '.ace_dark div.ace_isearch-result {\n' + + " border: 8px solid rgb(100, 110, 160);\n" + + " box-shadow: 0 0 4px rgb(80, 90, 140);\n" + '}\n' dom.importCssString(css, id); } diff --git a/lib/ace/incremental_search_test.js b/lib/ace/incremental_search_test.js index 1f8d78c0..c351d8e2 100644 --- a/lib/ace/incremental_search_test.js +++ b/lib/ace/incremental_search_test.js @@ -57,7 +57,7 @@ function callHighlighterUpdate() { ranges = ranges.concat(markerRanges); } } - session.$searchHighlight.update([], mockMarkerLayer, session, { + session.$isearchHighlight.update([], mockMarkerLayer, session, { firstRow: 0, lastRow: session.getRowLength()}); return ranges; } @@ -79,6 +79,20 @@ module.exports = { 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" From 3a781ed2c2c514743104a22691f66478de849e80 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 18 Mar 2013 17:01:10 +0400 Subject: [PATCH 32/32] small cleanup --- lib/ace/css/editor.css | 8 -------- lib/ace/incremental_search.js | 36 ++++++++++++++++++----------------- lib/ace/keyboard/emacs.js | 28 ++++++++++----------------- 3 files changed, 29 insertions(+), 43 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 3aebfb17..81f8fbe6 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -209,14 +209,6 @@ box-sizing: border-box; } -.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; -}} - .ace_line .ace_fold { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index 5564aa81..a15d7acc 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -209,20 +209,23 @@ exports.IncrementalSearch = IncrementalSearch; **/ var dom = require('./lib/dom'); -function patchHighlightMarkerStyling(options) { - options = options || {}; - var id = 'incremental-search-highlighting', - css = 'div.ace_isearch-result {\n' - + " border-radius: 4px;\n" - + " border: 8px solid rgba(255, 200, 0, 0.5);\n" - + " box-shadow: 0 0 4px rgb(255, 200, 0);\n" - + "}\n" - + '.ace_dark div.ace_isearch-result {\n' - + " border: 8px solid rgb(100, 110, 160);\n" - + " box-shadow: 0 0 4px rgb(80, 90, 140);\n" - + '}\n' - dom.importCssString(css, id); -} +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"); @@ -230,8 +233,8 @@ var commands = require("./commands/command_manager"); this.setupIncrementalSearch = function(editor, val) { if (this.usesIncrementalSearch == val) return; this.usesIncrementalSearch = val; - var iSearchCommands = iSearchCommandModule.iSearchStartCommands, - method = val ? 'addCommands' : 'removeCommands'; + var iSearchCommands = iSearchCommandModule.iSearchStartCommands; + var method = val ? 'addCommands' : 'removeCommands'; this[method](iSearchCommands); }; }).call(commands.CommandManager.prototype); @@ -241,7 +244,6 @@ var Editor = require("./editor").Editor; require("./config").defineOptions(Editor.prototype, "editor", { useIncrementalSearch: { set: function(val) { - patchHighlightMarkerStyling({enable: val}); this.keyBinding.$handlers.forEach(function(handler) { if (handler.setupIncrementalSearch) { handler.setupIncrementalSearch(this, val); diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index ff397fa2..81247be4 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -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(); @@ -102,14 +105,12 @@ 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); exports.handler.platform = editor.commands.platform; editor.$emacsModeHandler = this; - if (!editor.getOption('useIncrementalSearch')) editor.setOption('useIncrementalSearch', true) - else this.setupIncrementalSearch(editor, true);; }; exports.handler.detach = function(editor) { @@ -300,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", @@ -461,6 +464,8 @@ exports.handler.addCommands({ } }); +exports.handler.addCommands(iSearchCommandModule.iSearchStartCommands); + var commands = exports.handler.commands; commands.yank.isYank = true; commands.yankRotate.isYank = true; @@ -486,17 +491,4 @@ exports.killRing = { } }; -require('../incremental_search'); -exports.handler.setupIncrementalSearch = function(editor, val) { - if (this.usesIncrementalSearch == val) return; - this.usesIncrementalSearch = val; - if (val) { - this.bindKey('C-s', 'iSearch'); - this.bindKey('C-r', 'iSearchBackwards'); - } else { - this.bindKey('C-s', "findnext"); - this.bindKey('C-r', "findprevious"); - } -} - });