2 var Pos
= CodeMirror
.Pos
;
4 function SearchCursor(doc
, query
, pos
, caseFold
) {
5 this.atOccurrence
= false; this.doc
= doc
;
6 if (caseFold
== null && typeof query
== "string") caseFold
= false;
8 pos
= pos
? doc
.clipPos(pos
) : Pos(0, 0);
9 this.pos
= {from: pos
, to
: pos
};
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
) {
20 var line
= doc
.getLine(pos
.line
).slice(0, pos
.ch
), cutOff
= 0, match
, start
;
22 query
.lastIndex
= cutOff
;
23 var newMatch
= query
.exec(line
);
27 cutOff
= match
.index
+ (match
[0].length
|| 1);
28 if (cutOff
== line
.length
) break;
30 var matchLen
= (match
&& match
[0].length
) || 0;
32 if (start
== 0 && line
.length
== 0) {match
= undefined;}
33 else if (start
!= doc
.getLine(pos
.line
).length
) {
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;
44 if (match
&& matchLen
)
45 return {from: Pos(pos
.line
, start
),
46 to
: Pos(pos
.line
, start
+ matchLen
),
49 } else { // String query
50 if (caseFold
) query
= query
.toLowerCase();
51 var fold
= caseFold
? function(str
){return str
.toLowerCase();} : function(str
){return str
;};
52 var target
= query
.split("\n");
53 // Different methods for single-line and multi-line queries
54 if (target
.length
== 1) {
56 // Empty string would match anything and never progress, so
57 // we define it to match nothing instead.
58 this.matches = function() {};
60 this.matches = function(reverse
, pos
) {
61 var line
= fold(doc
.getLine(pos
.line
)), len
= query
.length
, match
;
62 if (reverse
? (pos
.ch
>= len
&& (match
= line
.lastIndexOf(query
, pos
.ch
- len
)) != -1)
63 : (match
= line
.indexOf(query
, pos
.ch
)) != -1)
64 return {from: Pos(pos
.line
, match
),
65 to
: Pos(pos
.line
, match
+ len
)};
69 this.matches = function(reverse
, pos
) {
70 var ln
= pos
.line
, idx
= (reverse
? target
.length
- 1 : 0), match
= target
[idx
], line
= fold(doc
.getLine(ln
));
71 var offsetA
= (reverse
? line
.indexOf(match
) + match
.length
: line
.lastIndexOf(match
));
72 if (reverse
? offsetA
>= pos
.ch
|| offsetA
!= match
.length
73 : offsetA
<= pos
.ch
|| offsetA
!= line
.length
- match
.length
)
76 if (reverse
? !ln
: ln
== doc
.lineCount() - 1) return;
77 line
= fold(doc
.getLine(ln
+= reverse
? -1 : 1));
78 match
= target
[reverse
? --idx
: ++idx
];
79 if (idx
> 0 && idx
< target
.length
- 1) {
80 if (line
!= match
) return;
83 var offsetB
= (reverse
? line
.lastIndexOf(match
) : line
.indexOf(match
) + match
.length
);
84 if (reverse
? offsetB
!= line
.length
- match
.length
: offsetB
!= match
.length
)
86 var start
= Pos(pos
.line
, offsetA
), end
= Pos(ln
, offsetB
);
87 return {from: reverse
? end
: start
, to
: reverse
? start
: end
};
94 SearchCursor
.prototype = {
95 findNext: function() {return this.find(false);},
96 findPrevious: function() {return this.find(true);},
98 find: function(reverse
) {
99 var self
= this, pos
= this.doc
.clipPos(reverse
? this.pos
.from : this.pos
.to
);
100 function savePosAndFail(line
) {
101 var pos
= Pos(line
, 0);
102 self
.pos
= {from: pos
, to
: pos
};
103 self
.atOccurrence
= false;
108 if (this.pos
= this.matches(reverse
, pos
)) {
109 if (!this.pos
.from || !this.pos
.to
) { console
.log(this.matches
, this.pos
); }
110 this.atOccurrence
= true;
111 return this.pos
.match
|| true;
114 if (!pos
.line
) return savePosAndFail(0);
115 pos
= Pos(pos
.line
-1, this.doc
.getLine(pos
.line
-1).length
);
118 var maxLine
= this.doc
.lineCount();
119 if (pos
.line
== maxLine
- 1) return savePosAndFail(maxLine
);
120 pos
= Pos(pos
.line
+ 1, 0);
125 from: function() {if (this.atOccurrence
) return this.pos
.from;},
126 to: function() {if (this.atOccurrence
) return this.pos
.to
;},
128 replace: function(newText
) {
129 if (!this.atOccurrence
) return;
130 var lines
= CodeMirror
.splitLines(newText
);
131 this.doc
.replaceRange(lines
, this.pos
.from, this.pos
.to
);
132 this.pos
.to
= Pos(this.pos
.from.line
+ lines
.length
- 1,
133 lines
[lines
.length
- 1].length
+ (lines
.length
== 1 ? this.pos
.from.ch
: 0));
137 CodeMirror
.defineExtension("getSearchCursor", function(query
, pos
, caseFold
) {
138 return new SearchCursor(this.doc
, query
, pos
, caseFold
);
140 CodeMirror
.defineDocExtension("getSearchCursor", function(query
, pos
, caseFold
) {
141 return new SearchCursor(this, query
, pos
, caseFold
);