diff options
Diffstat (limited to 'imports/codemirror/lib/util/search.js')
-rwxr-xr-x | imports/codemirror/lib/util/search.js | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/imports/codemirror/lib/util/search.js b/imports/codemirror/lib/util/search.js new file mode 100755 index 00000000..63ebca9b --- /dev/null +++ b/imports/codemirror/lib/util/search.js | |||
@@ -0,0 +1,114 @@ | |||
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 SearchState() { | ||
11 | this.posFrom = this.posTo = this.query = null; | ||
12 | this.marked = []; | ||
13 | } | ||
14 | function getSearchState(cm) { | ||
15 | return cm._searchState || (cm._searchState = new SearchState()); | ||
16 | } | ||
17 | function dialog(cm, text, shortText, f) { | ||
18 | if (cm.openDialog) cm.openDialog(text, f); | ||
19 | else f(prompt(shortText, "")); | ||
20 | } | ||
21 | function confirmDialog(cm, text, shortText, fs) { | ||
22 | if (cm.openConfirm) cm.openConfirm(text, fs); | ||
23 | else if (confirm(shortText)) fs[0](); | ||
24 | } | ||
25 | function parseQuery(query) { | ||
26 | var isRE = query.match(/^\/(.*)\/$/); | ||
27 | return isRE ? new RegExp(isRE[1]) : query; | ||
28 | } | ||
29 | var queryDialog = | ||
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")); | ||
41 | } | ||
42 | state.posFrom = state.posTo = cm.getCursor(); | ||
43 | findNext(cm, rev); | ||
44 | }); | ||
45 | }); | ||
46 | } | ||
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; | ||
53 | } | ||
54 | cm.setSelection(cursor.from(), cursor.to()); | ||
55 | state.posFrom = cursor.from(); state.posTo = cursor.to(); | ||
56 | })} | ||
57 | function clearSearch(cm) {cm.operation(function() { | ||
58 | var state = getSearchState(cm); | ||
59 | if (!state.query) return; | ||
60 | state.query = null; | ||
61 | for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); | ||
62 | state.marked.length = 0; | ||
63 | })} | ||
64 | |||
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) { | ||
71 | if (!query) return; | ||
72 | query = parseQuery(query); | ||
73 | dialog(cm, replacementQueryDialog, "Replace with:", function(text) { | ||
74 | if (all) { | ||
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); | ||
81 | } | ||
82 | }); | ||
83 | } else { | ||
84 | clearSearch(cm); | ||
85 | var cursor = cm.getSearchCursor(query, cm.getCursor()); | ||
86 | function advance() { | ||
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; | ||
92 | } | ||
93 | cm.setSelection(cursor.from(), cursor.to()); | ||
94 | confirmDialog(cm, doReplaceConfirm, "Replace?", | ||
95 | [function() {doReplace(match);}, advance]); | ||
96 | } | ||
97 | function doReplace(match) { | ||
98 | cursor.replace(typeof query == "string" ? text : | ||
99 | text.replace(/\$(\d)/, function(w, i) {return match[i];})); | ||
100 | advance(); | ||
101 | } | ||
102 | advance(); | ||
103 | } | ||
104 | }); | ||
105 | }); | ||
106 | } | ||
107 | |||
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);}; | ||
114 | })(); | ||