From 6ae6b188e82430fbf57d396f5bfa1f9a0b8de368 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Dec 2011 11:04:47 +0100 Subject: [PATCH] Improve replace UI in search script --- lib/util/dialog.js | 85 ++++++++++++++++++++++++++++++++-------------- lib/util/search.js | 43 +++++++++++++---------- 2 files changed, 86 insertions(+), 42 deletions(-) diff --git a/lib/util/dialog.js b/lib/util/dialog.js index 29d8938782..8950bf0c8b 100644 --- a/lib/util/dialog.js +++ b/lib/util/dialog.js @@ -1,28 +1,63 @@ // Open simple dialogs on top of an editor. Relies on dialog.css. -CodeMirror.defineExtension("openDialog", function(template, callback) { - var wrap = this.getWrapperElement(); - var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild); - dialog.className = "CodeMirror-dialog"; - dialog.innerHTML = '
' + template + '
'; - var closed = false, me = this; - function close() { - if (closed) return; - closed = true; - dialog.parentNode.removeChild(dialog); +(function() { + function dialogDiv(cm, template) { + var wrap = cm.getWrapperElement(); + var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild); + dialog.className = "CodeMirror-dialog"; + dialog.innerHTML = '
' + template + '
'; + return dialog; } - var inp = dialog.getElementsByTagName("input")[0]; - if (inp) { - CodeMirror.connect(inp, "keydown", function(e) { - if (e.keyCode == 13 || e.keyCode == 27) { - CodeMirror.e_stop(e); - close(); - me.focus(); - if (e.keyCode == 13) callback(inp.value); - } - }); - inp.focus(); - CodeMirror.connect(inp, "blur", close); - } - return close; -}); + + CodeMirror.defineExtension("openDialog", function(template, callback) { + var dialog = dialogDiv(this, template); + var closed = false, me = this; + function close() { + if (closed) return; + closed = true; + dialog.parentNode.removeChild(dialog); + } + var inp = dialog.getElementsByTagName("input")[0]; + if (inp) { + CodeMirror.connect(inp, "keydown", function(e) { + if (e.keyCode == 13 || e.keyCode == 27) { + CodeMirror.e_stop(e); + close(); + me.focus(); + if (e.keyCode == 13) callback(inp.value); + } + }); + inp.focus(); + CodeMirror.connect(inp, "blur", close); + } + return close; + }); + + CodeMirror.defineExtension("openConfirm", function(template, callbacks) { + var dialog = dialogDiv(this, template); + var buttons = dialog.getElementsByTagName("button"); + var closed = false, me = this, blurring = 1; + function close() { + if (closed) return; + closed = true; + dialog.parentNode.removeChild(dialog); + me.focus(); + } + buttons[0].focus(); + for (var i = 0; i < buttons.length; ++i) { + var b = buttons[i]; + (function(callback) { + CodeMirror.connect(b, "click", function(e) { + CodeMirror.e_preventDefault(e); + close(); + if (callback) callback(me); + }); + })(callbacks[i]); + CodeMirror.connect(b, "blur", function() { + --blurring; + setTimeout(function() { if (blurring <= 0) close(); }, 200); + }); + CodeMirror.connect(b, "focus", function() { ++blurring; }); + } + }); +})(); \ No newline at end of file diff --git a/lib/util/search.js b/lib/util/search.js index ad11892ec2..63ebca9b2c 100644 --- a/lib/util/search.js +++ b/lib/util/search.js @@ -8,8 +8,8 @@ (function() { function SearchState() { - this.posFrom = this.posTo = null; - this.query = this.replacing = null; this.marked = []; + this.posFrom = this.posTo = this.query = null; + this.marked = []; } function getSearchState(cm) { return cm._searchState || (cm._searchState = new SearchState()); @@ -18,6 +18,10 @@ if (cm.openDialog) cm.openDialog(text, f); else f(prompt(shortText, "")); } + function confirmDialog(cm, text, shortText, fs) { + if (cm.openConfirm) cm.openConfirm(text, fs); + else if (confirm(shortText)) fs[0](); + } function parseQuery(query) { var isRE = query.match(/^\/(.*)\/$/); return isRE ? new RegExp(isRE[1]) : query; @@ -42,15 +46,6 @@ } function findNext(cm, rev) {cm.operation(function() { var state = getSearchState(cm); - if (state.replacing) { - var sel = cm.getSelection(); - if (typeof state.query == "string") { - if (sel == state.query) cm.replaceSelection(state.replacing); - } else { - var match = sel.match(state.query); - if (match) cm.replaceSelection(state.replacing.replace(/\$(\d)/, function(w, i) {return match[i];})); - } - } var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo); if (!cursor.find(rev)) { cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0}); @@ -62,7 +57,7 @@ function clearSearch(cm) {cm.operation(function() { var state = getSearchState(cm); if (!state.query) return; - state.query = state.replacing = null; + state.query = null; for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); state.marked.length = 0; })} @@ -70,6 +65,7 @@ var replaceQueryDialog = 'Replace: (Use /re/ syntax for regexp search)'; var replacementQueryDialog = 'With: '; + var doReplaceConfirm = "Replace? "; function replace(cm, all) { dialog(cm, replaceQueryDialog, "Replace:", function(query) { if (!query) return; @@ -85,12 +81,25 @@ } }); } else { - var state = getSearchState(cm); clearSearch(cm); - state.query = query; - state.replacing = text; - state.posFrom = state.posTo = cm.getCursor(); - findNext(cm); + var cursor = cm.getSearchCursor(query, cm.getCursor()); + function advance() { + var start = cursor.from(), match; + if (!(match = cursor.findNext())) { + cursor = cm.getSearchCursor(query); + if (!(match = cursor.findNext()) || + (cursor.from().line == start.line && cursor.from().ch == start.ch)) return; + } + cm.setSelection(cursor.from(), cursor.to()); + confirmDialog(cm, doReplaceConfirm, "Replace?", + [function() {doReplace(match);}, advance]); + } + function doReplace(match) { + cursor.replace(typeof query == "string" ? text : + text.replace(/\$(\d)/, function(w, i) {return match[i];})); + advance(); + } + advance(); } }); });