1
// Define search commands. Depends on dialog.js or another
2
// implementation of the openDialog method.
4
// Replace works a little oddly -- it will do the replace on the next
5
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
6
// replace by making sure the match is no longer selected when hitting
10
function SearchState() {
11
this.posFrom = this.posTo = this.query = null;
14
function getSearchState(cm) {
15
return cm._searchState || (cm._searchState = new SearchState());
17
function dialog(cm, text, shortText, f) {
18
if (cm.openDialog) cm.openDialog(text, f);
19
else f(prompt(shortText, ""));
21
function confirmDialog(cm, text, shortText, fs) {
22
if (cm.openConfirm) cm.openConfirm(text, fs);
23
else if (confirm(shortText)) fs[0]();
25
function parseQuery(query) {
26
var isRE = query.match(/^\/(.*)\/$/);
27
return isRE ? new RegExp(isRE[1]) : query;
30
'Search: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
31
function doSearch(cm, rev) {
32
var state = getSearchState(cm);
33
if (state.query) return findNext(cm, rev);
34
dialog(cm, queryDialog, "Search for:", function(query) {
35
cm.operation(function() {
36
if (!query || state.query) return;
37
state.query = parseQuery(query);
38
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
39
for (var cursor = cm.getSearchCursor(query); cursor.findNext();)
40
state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching"));
42
state.posFrom = state.posTo = cm.getCursor();
47
function findNext(cm, rev) {cm.operation(function() {
48
var state = getSearchState(cm);
49
var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo);
50
if (!cursor.find(rev)) {
51
cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
52
if (!cursor.find(rev)) return;
54
cm.setSelection(cursor.from(), cursor.to());
55
state.posFrom = cursor.from(); state.posTo = cursor.to();
57
function clearSearch(cm) {cm.operation(function() {
58
var state = getSearchState(cm);
59
if (!state.query) return;
61
for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
62
state.marked.length = 0;
65
var replaceQueryDialog =
66
'Replace: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
67
var replacementQueryDialog = 'With: <input type="text" style="width: 10em">';
68
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
69
function replace(cm, all) {
70
dialog(cm, replaceQueryDialog, "Replace:", function(query) {
72
query = parseQuery(query);
73
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
75
cm.operation(function() {
76
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
77
if (typeof query != "string") {
78
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
79
cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];}));
80
} else cursor.replace(text);
85
var cursor = cm.getSearchCursor(query, cm.getCursor());
87
var start = cursor.from(), match;
88
if (!(match = cursor.findNext())) {
89
cursor = cm.getSearchCursor(query);
90
if (!(match = cursor.findNext()) ||
91
(cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
93
cm.setSelection(cursor.from(), cursor.to());
94
confirmDialog(cm, doReplaceConfirm, "Replace?",
95
[function() {doReplace(match);}, advance]);
97
function doReplace(match) {
98
cursor.replace(typeof query == "string" ? text :
99
text.replace(/\$(\d)/, function(w, i) {return match[i];}));
108
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
109
CodeMirror.commands.findNext = doSearch;
110
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
111
CodeMirror.commands.clearSearch = clearSearch;
112
CodeMirror.commands.replace = replace;
113
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};