Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
51008 views
1
// Define search commands. Depends on dialog.js or another
2
// implementation of the openDialog method.
3
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
7
// Ctrl-G.
8
9
(function() {
10
function searchOverlay(query, caseInsensitive) {
11
var startChar;
12
if (typeof query == "string") {
13
startChar = query.charAt(0);
14
query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"),
15
caseInsensitive ? "i" : "");
16
} else {
17
query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : "");
18
}
19
if (typeof query == "string") return {token: function(stream) {
20
if (stream.match(query)) return "searching";
21
stream.next();
22
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
23
}};
24
return {token: function(stream) {
25
if (stream.match(query)) return "searching";
26
while (!stream.eol()) {
27
stream.next();
28
if (startChar)
29
stream.skipTo(startChar) || stream.skipToEnd();
30
if (stream.match(query, false)) break;
31
}
32
}};
33
}
34
35
function SearchState() {
36
this.posFrom = this.posTo = this.query = null;
37
this.overlay = null;
38
}
39
function getSearchState(cm) {
40
return cm.state.search || (cm.state.search = new SearchState());
41
}
42
function queryCaseInsensitive(query) {
43
return typeof query == "string" && query == query.toLowerCase();
44
}
45
function getSearchCursor(cm, query, pos) {
46
// Heuristic: if the query string is all lowercase, do a case insensitive search.
47
return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
48
}
49
function dialog(cm, text, shortText, deflt, f) {
50
if (cm.openDialog) cm.openDialog(text, f, {value: deflt});
51
else f(prompt(shortText, deflt));
52
}
53
function confirmDialog(cm, text, shortText, fs) {
54
if (cm.openConfirm) cm.openConfirm(text, fs);
55
else if (confirm(shortText)) fs[0]();
56
}
57
function parseQuery(query) {
58
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
59
if (isRE) {
60
query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i");
61
if (query.test("")) query = /x^/;
62
} else if (query == "") {
63
query = /x^/;
64
}
65
return query;
66
}
67
var queryDialog =
68
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
69
function doSearch(cm, rev) {
70
var state = getSearchState(cm);
71
if (state.query) return findNext(cm, rev);
72
dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) {
73
cm.operation(function() {
74
if (!query || state.query) return;
75
state.query = parseQuery(query);
76
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
77
state.overlay = searchOverlay(state.query);
78
cm.addOverlay(state.overlay);
79
state.posFrom = state.posTo = cm.getCursor();
80
findNext(cm, rev);
81
});
82
});
83
}
84
function findNext(cm, rev) {cm.operation(function() {
85
var state = getSearchState(cm);
86
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
87
if (!cursor.find(rev)) {
88
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
89
if (!cursor.find(rev)) return;
90
}
91
cm.setSelection(cursor.from(), cursor.to());
92
cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
93
state.posFrom = cursor.from(); state.posTo = cursor.to();
94
});}
95
function clearSearch(cm) {cm.operation(function() {
96
var state = getSearchState(cm);
97
if (!state.query) return;
98
state.query = null;
99
cm.removeOverlay(state.overlay);
100
});}
101
102
var replaceQueryDialog =
103
'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
104
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
105
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
106
function replace(cm, all) {
107
dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) {
108
if (!query) return;
109
query = parseQuery(query);
110
dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
111
if (all) {
112
cm.operation(function() {
113
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
114
if (typeof query != "string") {
115
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
116
cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
117
} else cursor.replace(text);
118
}
119
});
120
} else {
121
clearSearch(cm);
122
var cursor = getSearchCursor(cm, query, cm.getCursor());
123
var advance = function() {
124
var start = cursor.from(), match;
125
if (!(match = cursor.findNext())) {
126
cursor = getSearchCursor(cm, query);
127
if (!(match = cursor.findNext()) ||
128
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
129
}
130
cm.setSelection(cursor.from(), cursor.to());
131
cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
132
confirmDialog(cm, doReplaceConfirm, "Replace?",
133
[function() {doReplace(match);}, advance]);
134
};
135
var doReplace = function(match) {
136
cursor.replace(typeof query == "string" ? text :
137
text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
138
advance();
139
};
140
advance();
141
}
142
});
143
});
144
}
145
146
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
147
CodeMirror.commands.findNext = doSearch;
148
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
149
CodeMirror.commands.clearSearch = clearSearch;
150
CodeMirror.commands.replace = replace;
151
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
152
})();
153
154