Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
51008 views
1
(function(){
2
var Pos = CodeMirror.Pos;
3
4
function SearchCursor(doc, query, pos, caseFold) {
5
this.atOccurrence = false; this.doc = doc;
6
if (caseFold == null && typeof query == "string") caseFold = false;
7
8
pos = pos ? doc.clipPos(pos) : Pos(0, 0);
9
this.pos = {from: pos, to: pos};
10
11
// The matches method is filled in based on the type of query.
12
// It takes a position and a direction, and returns an object
13
// describing the next occurrence of the query, or null if no
14
// more matches were found.
15
if (typeof query != "string") { // Regexp match
16
if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
17
this.matches = function(reverse, pos) {
18
if (reverse) {
19
query.lastIndex = 0;
20
var line = doc.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
21
for (;;) {
22
query.lastIndex = cutOff;
23
var newMatch = query.exec(line);
24
if (!newMatch) break;
25
match = newMatch;
26
start = match.index;
27
cutOff = match.index + (match[0].length || 1);
28
if (cutOff == line.length) break;
29
}
30
var matchLen = (match && match[0].length) || 0;
31
if (!matchLen) {
32
if (start == 0 && line.length == 0) {match = undefined;}
33
else if (start != doc.getLine(pos.line).length) {
34
matchLen++;
35
}
36
}
37
} else {
38
query.lastIndex = pos.ch;
39
var line = doc.getLine(pos.line), match = query.exec(line);
40
var matchLen = (match && match[0].length) || 0;
41
var start = match && match.index;
42
if (start + matchLen != line.length && !matchLen) matchLen = 1;
43
}
44
if (match && matchLen)
45
return {from: Pos(pos.line, start),
46
to: Pos(pos.line, start + matchLen),
47
match: match};
48
};
49
} else { // String query
50
var origQuery = query;
51
if (caseFold) query = query.toLowerCase();
52
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
53
var target = query.split("\n");
54
// Different methods for single-line and multi-line queries
55
if (target.length == 1) {
56
if (!query.length) {
57
// Empty string would match anything and never progress, so
58
// we define it to match nothing instead.
59
this.matches = function() {};
60
} else {
61
this.matches = function(reverse, pos) {
62
if (reverse) {
63
var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig);
64
var match = line.lastIndexOf(query);
65
if (match > -1) {
66
match = adjustPos(orig, line, match);
67
return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
68
}
69
} else {
70
var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig);
71
var match = line.indexOf(query);
72
if (match > -1) {
73
match = adjustPos(orig, line, match) + pos.ch;
74
return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
75
}
76
}
77
};
78
}
79
} else {
80
var origTarget = origQuery.split("\n");
81
this.matches = function(reverse, pos) {
82
var last = target.length - 1;
83
if (reverse) {
84
if (pos.line - (target.length - 1) < doc.firstLine()) return;
85
if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return;
86
var to = Pos(pos.line, origTarget[last].length);
87
for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln)
88
if (target[i] != fold(doc.getLine(ln))) return;
89
var line = doc.getLine(ln), cut = line.length - origTarget[0].length;
90
if (fold(line.slice(cut)) != target[0]) return;
91
return {from: Pos(ln, cut), to: to};
92
} else {
93
if (pos.line + (target.length - 1) > doc.lastLine()) return;
94
var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length;
95
if (fold(line.slice(cut)) != target[0]) return;
96
var from = Pos(pos.line, cut);
97
for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln)
98
if (target[i] != fold(doc.getLine(ln))) return;
99
if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return;
100
return {from: from, to: Pos(ln, origTarget[last].length)};
101
}
102
};
103
}
104
}
105
}
106
107
SearchCursor.prototype = {
108
findNext: function() {return this.find(false);},
109
findPrevious: function() {return this.find(true);},
110
111
find: function(reverse) {
112
var self = this, pos = this.doc.clipPos(reverse ? this.pos.from : this.pos.to);
113
function savePosAndFail(line) {
114
var pos = Pos(line, 0);
115
self.pos = {from: pos, to: pos};
116
self.atOccurrence = false;
117
return false;
118
}
119
120
for (;;) {
121
if (this.pos = this.matches(reverse, pos)) {
122
this.atOccurrence = true;
123
return this.pos.match || true;
124
}
125
if (reverse) {
126
if (!pos.line) return savePosAndFail(0);
127
pos = Pos(pos.line-1, this.doc.getLine(pos.line-1).length);
128
}
129
else {
130
var maxLine = this.doc.lineCount();
131
if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
132
pos = Pos(pos.line + 1, 0);
133
}
134
}
135
},
136
137
from: function() {if (this.atOccurrence) return this.pos.from;},
138
to: function() {if (this.atOccurrence) return this.pos.to;},
139
140
replace: function(newText) {
141
if (!this.atOccurrence) return;
142
var lines = CodeMirror.splitLines(newText);
143
this.doc.replaceRange(lines, this.pos.from, this.pos.to);
144
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
145
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
146
}
147
};
148
149
// Maps a position in a case-folded line back to a position in the original line
150
// (compensating for codepoints increasing in number during folding)
151
function adjustPos(orig, folded, pos) {
152
if (orig.length == folded.length) return pos;
153
for (var pos1 = Math.min(pos, orig.length);;) {
154
var len1 = orig.slice(0, pos1).toLowerCase().length;
155
if (len1 < pos) ++pos1;
156
else if (len1 > pos) --pos1;
157
else return pos1;
158
}
159
}
160
161
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
162
return new SearchCursor(this.doc, query, pos, caseFold);
163
});
164
CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
165
return new SearchCursor(this, query, pos, caseFold);
166
});
167
})();
168
169