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 getSearchCursor(cm, query, pos) {
18
// Heuristic: if the query string is all lowercase, do a case insensitive search.
19
return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
21
function dialog(cm, text, shortText, f) {
22
if (cm.openDialog) cm.openDialog(text, f);
23
else f(prompt(shortText, ""));
25
function confirmDialog(cm, text, shortText, fs) {
26
if (cm.openConfirm) cm.openConfirm(text, fs);
27
else if (confirm(shortText)) fs[0]();
29
function parseQuery(query) {
30
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
31
return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
34
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
35
function doSearch(cm, rev) {
36
var state = getSearchState(cm);
37
if (state.query) return findNext(cm, rev);
38
dialog(cm, queryDialog, "Search for:", function(query) {
39
cm.operation(function() {
40
if (!query || state.query) return;
41
state.query = parseQuery(query);
42
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
43
for (var cursor = getSearchCursor(cm, state.query); cursor.findNext();)
44
state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching"));
46
state.posFrom = state.posTo = cm.getCursor();
51
function findNext(cm, rev) {cm.operation(function() {
52
var state = getSearchState(cm);
53
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
54
if (!cursor.find(rev)) {
55
cursor = getSearchCursor(cm, state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
56
if (!cursor.find(rev)) return;
58
cm.setSelection(cursor.from(), cursor.to());
59
state.posFrom = cursor.from(); state.posTo = cursor.to();
61
function clearSearch(cm) {cm.operation(function() {
62
var state = getSearchState(cm);
63
if (!state.query) return;
65
for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
66
state.marked.length = 0;
69
var replaceQueryDialog =
70
'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
71
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
72
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
73
function replace(cm, all) {
74
dialog(cm, replaceQueryDialog, "Replace:", function(query) {
76
query = parseQuery(query);
77
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
79
cm.compoundChange(function() { cm.operation(function() {
80
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
81
if (typeof query != "string") {
82
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
83
cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];}));
84
} else cursor.replace(text);
89
var cursor = getSearchCursor(cm, query, cm.getCursor());
91
var start = cursor.from(), match;
92
if (!(match = cursor.findNext())) {
93
cursor = getSearchCursor(cm, query);
94
if (!(match = cursor.findNext()) ||
95
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
97
cm.setSelection(cursor.from(), cursor.to());
98
confirmDialog(cm, doReplaceConfirm, "Replace?",
99
[function() {doReplace(match);}, advance]);
101
function doReplace(match) {
102
cursor.replace(typeof query == "string" ? text :
103
text.replace(/\$(\d)/, function(w, i) {return match[i];}));
112
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
113
CodeMirror.commands.findNext = doSearch;
114
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
115
CodeMirror.commands.clearSearch = clearSearch;
116
CodeMirror.commands.replace = replace;
117
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};