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