search.js
114 lines
| 4.6 KiB
| application/javascript
|
JavascriptLexer
Brian Granger
|
r5941 | // Define search commands. Depends on dialog.js or another | ||
// implementation of the openDialog method. | ||||
// Replace works a little oddly -- it will do the replace on the next | ||||
// Ctrl-G (or whatever is bound to findNext) press. You prevent a | ||||
// replace by making sure the match is no longer selected when hitting | ||||
// Ctrl-G. | ||||
(function() { | ||||
function SearchState() { | ||||
this.posFrom = this.posTo = this.query = null; | ||||
this.marked = []; | ||||
} | ||||
function getSearchState(cm) { | ||||
return cm._searchState || (cm._searchState = new SearchState()); | ||||
} | ||||
function dialog(cm, text, shortText, f) { | ||||
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; | ||||
} | ||||
var queryDialog = | ||||
'Search: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>'; | ||||
function doSearch(cm, rev) { | ||||
var state = getSearchState(cm); | ||||
if (state.query) return findNext(cm, rev); | ||||
dialog(cm, queryDialog, "Search for:", function(query) { | ||||
cm.operation(function() { | ||||
if (!query || state.query) return; | ||||
state.query = parseQuery(query); | ||||
if (cm.lineCount() < 2000) { // This is too expensive on big documents. | ||||
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) | ||||
state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching")); | ||||
} | ||||
state.posFrom = state.posTo = cm.getCursor(); | ||||
findNext(cm, rev); | ||||
}); | ||||
}); | ||||
} | ||||
function findNext(cm, rev) {cm.operation(function() { | ||||
var state = getSearchState(cm); | ||||
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}); | ||||
if (!cursor.find(rev)) return; | ||||
} | ||||
cm.setSelection(cursor.from(), cursor.to()); | ||||
state.posFrom = cursor.from(); state.posTo = cursor.to(); | ||||
})} | ||||
function clearSearch(cm) {cm.operation(function() { | ||||
var state = getSearchState(cm); | ||||
if (!state.query) return; | ||||
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; | ||||
query = parseQuery(query); | ||||
dialog(cm, replacementQueryDialog, "Replace with:", function(text) { | ||||
if (all) { | ||||
cm.operation(function() { | ||||
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) { | ||||
if (typeof query != "string") { | ||||
var match = cm.getRange(cursor.from(), cursor.to()).match(query); | ||||
cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];})); | ||||
} else cursor.replace(text); | ||||
} | ||||
}); | ||||
} else { | ||||
clearSearch(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(); | ||||
} | ||||
}); | ||||
}); | ||||
} | ||||
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; | ||||
CodeMirror.commands.findNext = doSearch; | ||||
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; | ||||
CodeMirror.commands.clearSearch = clearSearch; | ||||
CodeMirror.commands.replace = replace; | ||||
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; | ||||
})(); | ||||