diff --git a/src/search/FindReplace.js b/src/search/FindReplace.js index 93daa4c5f77..f62bbbc83af 100644 --- a/src/search/FindReplace.js +++ b/src/search/FindReplace.js @@ -26,13 +26,12 @@ /* * Adds Find and Replace commands + * + * Originally based on the code in CodeMirror2/lib/util/search.js. * * Define search commands. Depends on dialog.js or another * implementation of the openDialog method. * - * This code was copied from CodeMirror2/lib/util/search.js so that the UI strings - * could be localized. - * * Replace works a little oddly -- it will do the replace on the next findNext press. * You prevent a replace by making sure the match is no longer selected when hitting * findNext. @@ -80,6 +79,10 @@ define(function (require, exports, module) { fs[0](); } } + + function getDialogTextField() { + return $(".CodeMirror-dialog input[type='text']"); + } function parseQuery(query) { var isRE = query.match(/^\/(.*)\/([a-z]*)$/); @@ -102,45 +105,69 @@ define(function (require, exports, module) { }); } + function clearSearch(cm) { + cm.operation(function () { + var state = getSearchState(cm), + i; + if (!state.query) { + return; + } + state.query = null; + + // Clear highlights + for (i = 0; i < state.marked.length; ++i) { + state.marked[i].clear(); + } + state.marked.length = 0; + }); + } + var queryDialog = Strings.CMD_FIND + ': (' + Strings.SEARCH_REGEXP_INFO + ')'; + /** + * If no search pending, opens the search dialog. If search is already open, moves to + * next/prev result (depending on 'rev') + */ function doSearch(cm, rev) { var state = getSearchState(cm); if (state.query) { return findNext(cm, rev); } - dialog(cm, queryDialog, Strings.CMD_FIND, function (query) { + + var searchStartPos = cm.getCursor(); + + // Called each time the search query changes while being typed. Jumps to the first matching + // result, starting from the original cursor position + function findFirst(query) { cm.operation(function () { - if (!query || state.query) { + if (!query) { return; } + + if (state.query) { + clearSearch(cm); // clear highlights from previous query + } state.query = parseQuery(query); + + // Highlight all matches + // FUTURE: if last query was prefix of this one, could optimize by filtering existing result set if (cm.lineCount() < 2000) { // This is too expensive on big documents. var cursor = getSearchCursor(cm, query); while (cursor.findNext()) { state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching")); } } - state.posFrom = state.posTo = cm.getCursor(); + + state.posFrom = state.posTo = searchStartPos; findNext(cm, rev); }); - }); - } - - function clearSearch(cm) { - cm.operation(function () { - var state = getSearchState(cm), - i; - if (!state.query) { - return; - } - state.query = null; - for (i = 0; i < state.marked.length; ++i) { - state.marked[i].clear(); - } - state.marked.length = 0; + } + + dialog(cm, queryDialog, Strings.CMD_FIND, findFirst); + getDialogTextField().on("input", function () { + findFirst(getDialogTextField().attr("value")); }); } @@ -218,7 +245,7 @@ define(function (require, exports, module) { doSearch(codeMirror); // Prepopulate the search field with the current selection, if any - $(".CodeMirror-dialog input[type='text']") + getDialogTextField() .attr("value", codeMirror.getSelection()) .get(0).select(); }