Skip to content

Commit

Permalink
Improve replace UI in search script
Browse files Browse the repository at this point in the history
  • Loading branch information
marijnh committed Dec 20, 2011
1 parent 99b5a84 commit 6ae6b18
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 42 deletions.
85 changes: 60 additions & 25 deletions 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 = '<div>' + template + '</div>';
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 = '<div>' + template + '</div>';
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; });
}
});
})();
43 changes: 26 additions & 17 deletions lib/util/search.js
Expand Up @@ -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());
Expand All @@ -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;
Expand All @@ -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});
Expand All @@ -62,14 +57,15 @@
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;
})}

var replaceQueryDialog =
'Replace: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
var replacementQueryDialog = 'With: <input type="text" style="width: 10em">';
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
function replace(cm, all) {
dialog(cm, replaceQueryDialog, "Replace:", function(query) {
if (!query) return;
Expand All @@ -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();
}
});
});
Expand Down

0 comments on commit 6ae6b18

Please sign in to comment.