Changeset View
Changeset View
Standalone View
Standalone View
src/search/kateregexpsearch.cpp
Show All 15 Lines | 1 | /* SPDX-License-Identifier: LGPL-2.0-or-later | |||
---|---|---|---|---|---|
16 | You should have received a copy of the GNU Library General Public License | 16 | You should have received a copy of the GNU Library General Public License | ||
17 | along with this library; see the file COPYING.LIB. If not, write to | 17 | along with this library; see the file COPYING.LIB. If not, write to | ||
18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
19 | Boston, MA 02110-1301, USA. | 19 | Boston, MA 02110-1301, USA. | ||
20 | */ | 20 | */ | ||
21 | 21 | | |||
22 | // BEGIN includes | 22 | // BEGIN includes | ||
23 | #include "kateregexpsearch.h" | 23 | #include "kateregexpsearch.h" | ||
24 | #include "kateregexp.h" | | |||
25 | 24 | | |||
26 | #include <ktexteditor/document.h> | 25 | #include <ktexteditor/document.h> | ||
27 | // END includes | 26 | // END includes | ||
28 | 27 | | |||
29 | // Turn debug messages on/off here | 28 | // Turn debug messages on/off here | ||
30 | // #define FAST_DEBUG_ENABLE | 29 | // #define FAST_DEBUG_ENABLE | ||
31 | 30 | | |||
32 | #ifdef FAST_DEBUG_ENABLE | 31 | #ifdef FAST_DEBUG_ENABLE | ||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Line(s) | 153 | { | |||
156 | 155 | | |||
157 | return *this; | 156 | return *this; | ||
158 | } | 157 | } | ||
159 | 158 | | |||
160 | // BEGIN d'tor, c'tor | 159 | // BEGIN d'tor, c'tor | ||
161 | // | 160 | // | ||
162 | // KateSearch Constructor | 161 | // KateSearch Constructor | ||
163 | // | 162 | // | ||
164 | KateRegExpSearch::KateRegExpSearch(const KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity) | 163 | KateRegExpSearch::KateRegExpSearch(const KTextEditor::Document *document) | ||
165 | : m_document(document) | 164 | : m_document(document) | ||
166 | , m_caseSensitivity(caseSensitivity) | | |||
167 | { | 165 | { | ||
168 | } | 166 | } | ||
169 | 167 | | |||
170 | // | 168 | // | ||
171 | // KateSearch Destructor | 169 | // KateSearch Destructor | ||
172 | // | 170 | // | ||
173 | KateRegExpSearch::~KateRegExpSearch() | 171 | KateRegExpSearch::~KateRegExpSearch() | ||
174 | { | 172 | { | ||
Show All 11 Lines | 176 | struct TwoViewCursor { | |||
186 | // easier. overhead is minimal. | 184 | // easier. overhead is minimal. | ||
187 | }; | 185 | }; | ||
188 | 186 | | |||
189 | struct IndexPair { | 187 | struct IndexPair { | ||
190 | int openIndex; | 188 | int openIndex; | ||
191 | int closeIndex; | 189 | int closeIndex; | ||
192 | }; | 190 | }; | ||
193 | 191 | | |||
194 | QVector<KTextEditor::Range> KateRegExpSearch::search(const QString &pattern, const KTextEditor::Range &inputRange, bool backwards) | 192 | QVector<KTextEditor::Range> KateRegExpSearch::search(const QString &pattern, const KTextEditor::Range &inputRange, bool backwards, QRegularExpression::PatternOptions options) | ||
195 | { | 193 | { | ||
196 | // regex search | 194 | // Enable multiline mode: | ||
197 | KateRegExp regexp(pattern, m_caseSensitivity); | 195 | // - "^" will match at the beginning of the subject string and will | ||
196 | // also match right after a newline character (unless the newline | ||||
197 | // is the last character in the searched range) | ||||
198 | // - "$" will match at the end of the subject string and will also match | ||||
199 | // right before a newline character | ||||
200 | // | ||||
201 | // This is neccessary because of the way Ranges are passed to QRegularExpression, | ||||
202 | // from its POV, the end of the range is the end of the string it's | ||||
203 | // matching against so it would match "$" at the end of the range _but_ | ||||
204 | // "$" must not match in the middle of a document line, that's why here | ||||
205 | // QRegularExpression is fed whole lines, regardless of where the range | ||||
206 | // ends, then matches that aren't contained in the searched range are rejected. | ||||
207 | options |= QRegularExpression::MultilineOption; | ||||
198 | 208 | | |||
199 | if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end())) { | 209 | QRegularExpression regexp; | ||
200 | QVector<KTextEditor::Range> result; | 210 | | ||
201 | result.append(KTextEditor::Range::invalid()); | 211 | bool stillMultiLine; | ||
202 | return result; | 212 | const QString repairedPattern = repairPattern(pattern, stillMultiLine); | ||
213 | | ||||
214 | regexp.setPattern(repairedPattern); | ||||
215 | regexp.setPatternOptions(options); | ||||
216 | | ||||
217 | // returned if no matches are found | ||||
218 | QVector<KTextEditor::Range> noResult(1, KTextEditor::Range::invalid()); | ||||
219 | | ||||
220 | if (!regexp.isValid() || !inputRange.isValid() || inputRange.isEmpty()) { | ||||
221 | return noResult; | ||||
203 | } | 222 | } | ||
204 | 223 | | |||
205 | // detect pattern type (single- or mutli-line) | 224 | const int rangeStartLine = inputRange.start().line(); | ||
206 | bool isMultiLine; | 225 | const int rangeStartCol = inputRange.start().column(); | ||
207 | 226 | | |||
208 | // detect '.' and '\s' and fix them | 227 | const int rangeEndLine = inputRange.end().line(); | ||
209 | const bool dotMatchesNewline = false; // TODO | 228 | const int rangeEndCol = inputRange.end().column(); | ||
210 | const int replacements = regexp.repairPattern(isMultiLine); | 229 | | ||
211 | if (dotMatchesNewline && (replacements > 0)) { | 230 | if (stillMultiLine) { | ||
212 | isMultiLine = true; | 231 | const int inputLineCount = rangeEndLine - rangeStartLine + 1; | ||
213 | } | 232 | FAST_DEBUG("regular expression search (lines " << rangeStartLine << ".." << rangeEndLine << ")"); | ||
214 | | ||||
215 | const int firstLineIndex = inputRange.start().line(); | | |||
216 | const int minColStart = inputRange.start().column(); | | |||
217 | // const int maxColEnd = inputRange.end().column(); | | |||
218 | if (isMultiLine) { | | |||
219 | // multi-line regex search (both forward and backward mode) | | |||
220 | QString wholeDocument; | | |||
221 | const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1; | | |||
222 | FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")"); | | |||
223 | 233 | | |||
224 | // nothing to do... | 234 | // nothing to do... | ||
225 | if (firstLineIndex >= m_document->lines()) { | 235 | if (rangeStartLine >= m_document->lines()) { | ||
226 | QVector<KTextEditor::Range> result; | 236 | return noResult; | ||
227 | result.append(KTextEditor::Range::invalid()); | | |||
228 | return result; | | |||
229 | } | 237 | } | ||
230 | 238 | | |||
231 | QVector<int> lineLens(inputLineCount); | 239 | QVector<int> lineLens(inputLineCount); | ||
240 | int maxMatchOffset = 0; | ||||
232 | 241 | | |||
233 | // first line | 242 | // all lines in the input range | ||
234 | if (firstLineIndex < 0 || m_document->lines() <= firstLineIndex) { | 243 | QString wholeRange; | ||
235 | QVector<KTextEditor::Range> result; | 244 | const QString sep = QLatin1String("\n"); | ||
236 | result.append(KTextEditor::Range::invalid()); | 245 | for (int i = 0; i < inputLineCount; ++i) { | ||
237 | return result; | 246 | const int docLineIndex = rangeStartLine + i; | ||
247 | if (docLineIndex < 0 || m_document->lines() <= docLineIndex) { | ||||
248 | return noResult; | ||||
238 | } | 249 | } | ||
239 | 250 | | |||
240 | const QString firstLine = m_document->line(firstLineIndex); | 251 | const QString textLine = m_document->line(docLineIndex); | ||
241 | 252 | lineLens[i] = textLine.length(); | |||
242 | const int firstLineLen = firstLine.length() - minColStart; | 253 | wholeRange.append(textLine); | ||
243 | wholeDocument.append(firstLine.rightRef(firstLineLen)); | 254 | if (i != rangeEndLine) { | ||
244 | lineLens[0] = firstLineLen; | 255 | wholeRange.append(sep); | ||
245 | FAST_DEBUG(" line" << 0 << "has length" << lineLens[0]); | | |||
246 | | ||||
247 | // second line and after | | |||
248 | for (int i = 1; i < inputLineCount; i++) { | | |||
249 | const int lineNum = firstLineIndex + i; | | |||
250 | if (lineNum < 0 || m_document->lines() <= lineNum) { | | |||
251 | QVector<KTextEditor::Range> result; | | |||
252 | result.append(KTextEditor::Range::invalid()); | | |||
253 | return result; | | |||
254 | } | 256 | } | ||
255 | const QString text = m_document->line(lineNum); | | |||
256 | 257 | | |||
257 | lineLens[i] = text.length(); | 258 | maxMatchOffset += (i == rangeEndLine) ? rangeEndCol : lineLens[i] + 1; | ||
258 | wholeDocument.append(QLatin1Char('\n')); | 259 | | ||
259 | wholeDocument.append(text); | | |||
260 | FAST_DEBUG(" line" << i << "has length" << lineLens[i]); | 260 | FAST_DEBUG(" line" << i << "has length" << lineLens[i]); | ||
261 | } | 261 | } | ||
262 | 262 | | |||
263 | const int pos = backwards ? regexp.lastIndexIn(wholeDocument, 0, wholeDocument.length()) : regexp.indexIn(wholeDocument, 0, wholeDocument.length()); | 263 | FAST_DEBUG("Max. match offset" << maxMatchOffset); | ||
264 | if (pos == -1) { | 264 | | ||
265 | // no match | 265 | QRegularExpressionMatch match; | ||
266 | FAST_DEBUG("not found"); | 266 | | ||
267 | { | 267 | bool found = false; | ||
268 | QVector<KTextEditor::Range> result; | 268 | QRegularExpressionMatch curMatch; | ||
269 | result.append(KTextEditor::Range::invalid()); | 269 | | ||
270 | return result; | 270 | QRegularExpressionMatchIterator iter = regexp.globalMatch(wholeRange, rangeStartCol); | ||
271 | | ||||
272 | if (backwards) { | ||||
273 | while (iter.hasNext()) { | ||||
274 | curMatch = iter.next(); | ||||
275 | if (curMatch.hasMatch() && curMatch.capturedEnd() <= maxMatchOffset) { | ||||
276 | match.swap(curMatch); | ||||
277 | found = true; | ||||
278 | } | ||||
279 | } | ||||
280 | } else { /* forwards */ | ||||
281 | if (iter.hasNext()) { | ||||
282 | curMatch = iter.next(); | ||||
283 | } | ||||
284 | if (curMatch.hasMatch() && curMatch.capturedEnd() <= maxMatchOffset) { | ||||
285 | match.swap(curMatch); | ||||
286 | found = true; | ||||
271 | } | 287 | } | ||
272 | } | 288 | } | ||
273 | 289 | | |||
274 | #ifdef FAST_DEBUG_ENABLE | 290 | if (!found) { | ||
275 | const int matchLen = regexp.matchedLength(); | 291 | // no match | ||
276 | FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen); | 292 | FAST_DEBUG("not found"); | ||
277 | #endif | 293 | return noResult; | ||
294 | } | ||||
278 | 295 | | |||
296 | if (found) { | ||||
279 | // save opening and closing indices and build a map. | 297 | // save opening and closing indices and build a map. | ||
280 | // the correct values will be written into it later. | 298 | // the correct values will be written into it later. | ||
281 | QMap<int, TwoViewCursor *> indicesToCursors; | 299 | QMap<int, TwoViewCursor *> indicesToCursors; | ||
282 | const int numCaptures = regexp.numCaptures(); | 300 | const int numCaptures = regexp.captureCount(); | ||
283 | QVector<IndexPair> indexPairs(1 + numCaptures); | 301 | QVector<IndexPair> indexPairs(numCaptures + 1); | ||
284 | for (int z = 0; z <= numCaptures; z++) { | 302 | for (int c = 0; c <= numCaptures; ++c) { | ||
285 | const int openIndex = regexp.pos(z); | 303 | const int openIndex = match.capturedStart(c); | ||
286 | IndexPair &pair = indexPairs[z]; | 304 | IndexPair &pair = indexPairs[c]; | ||
287 | if (openIndex == -1) { | 305 | if (openIndex == -1) { | ||
288 | // empty capture gives invalid | 306 | // empty capture gives invalid | ||
289 | pair.openIndex = -1; | 307 | pair.openIndex = -1; | ||
290 | pair.closeIndex = -1; | 308 | pair.closeIndex = -1; | ||
291 | FAST_DEBUG("capture []"); | 309 | FAST_DEBUG("capture []"); | ||
292 | } else { | 310 | } else { | ||
293 | const int closeIndex = openIndex + regexp.cap(z).length(); | 311 | const int closeIndex = match.capturedEnd(c); | ||
294 | pair.openIndex = openIndex; | 312 | pair.openIndex = openIndex; | ||
295 | pair.closeIndex = closeIndex; | 313 | pair.closeIndex = closeIndex; | ||
296 | FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]"); | 314 | FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]"); | ||
297 | 315 | | |||
298 | // each key no more than once | 316 | // each key no more than once | ||
299 | if (!indicesToCursors.contains(openIndex)) { | 317 | if (!indicesToCursors.contains(openIndex)) { | ||
300 | TwoViewCursor *twoViewCursor = new TwoViewCursor; | 318 | TwoViewCursor *twoViewCursor = new TwoViewCursor; | ||
301 | twoViewCursor->index = openIndex; | 319 | twoViewCursor->index = openIndex; | ||
302 | indicesToCursors.insert(openIndex, twoViewCursor); | 320 | indicesToCursors.insert(openIndex, twoViewCursor); | ||
303 | FAST_DEBUG(" border index added: " << openIndex); | 321 | FAST_DEBUG(" border index added: " << openIndex); | ||
304 | } | 322 | } | ||
305 | if (!indicesToCursors.contains(closeIndex)) { | 323 | if (!indicesToCursors.contains(closeIndex)) { | ||
306 | TwoViewCursor *twoViewCursor = new TwoViewCursor; | 324 | TwoViewCursor *twoViewCursor = new TwoViewCursor; | ||
307 | twoViewCursor->index = closeIndex; | 325 | twoViewCursor->index = closeIndex; | ||
308 | indicesToCursors.insert(closeIndex, twoViewCursor); | 326 | indicesToCursors.insert(closeIndex, twoViewCursor); | ||
309 | FAST_DEBUG(" border index added: " << closeIndex); | 327 | FAST_DEBUG(" border index added: " << closeIndex); | ||
310 | } | 328 | } | ||
311 | } | 329 | } | ||
312 | } | 330 | } | ||
313 | 331 | | |||
314 | // find out where they belong | 332 | // find out where they belong | ||
315 | int curRelLine = 0; | 333 | int curRelLine = 0; | ||
316 | int curRelCol = 0; | 334 | int curRelCol = 0; | ||
317 | int curRelIndex = 0; | 335 | int curRelIndex = 0; | ||
318 | QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin(); | 336 | | ||
337 | auto iter = indicesToCursors.constBegin(); | ||||
319 | while (iter != indicesToCursors.constEnd()) { | 338 | while (iter != indicesToCursors.constEnd()) { | ||
320 | // forward to index, save line/col | 339 | // forward to index, save line/col | ||
321 | const int index = (*iter)->index; | 340 | const int index = (*iter)->index; | ||
322 | FAST_DEBUG("resolving position" << index); | 341 | FAST_DEBUG("resolving position" << index); | ||
323 | TwoViewCursor &twoViewCursor = *(*iter); | 342 | TwoViewCursor &twoViewCursor = *(*iter); | ||
324 | while (curRelIndex <= index) { | 343 | while (curRelIndex <= index) { | ||
325 | FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = " << curRelIndex << "relative, steps more to go" << index - curRelIndex); | 344 | FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = " << curRelIndex << "relative, steps more to go" << index - curRelIndex); | ||
345 | | ||||
326 | const int curRelLineLen = lineLens[curRelLine]; | 346 | const int curRelLineLen = lineLens[curRelLine]; | ||
327 | const int curLineRemainder = curRelLineLen - curRelCol; | 347 | const int curLineRemainder = curRelLineLen - curRelCol; | ||
328 | const int lineFeedIndex = curRelIndex + curLineRemainder; | 348 | const int lineFeedIndex = curRelIndex + curLineRemainder; | ||
329 | if (index <= lineFeedIndex) { | 349 | if (index <= lineFeedIndex) { | ||
330 | if (index == lineFeedIndex) { | 350 | if (index == lineFeedIndex) { | ||
331 | // on this line _on_ line feed | 351 | // on this line _on_ line feed | ||
332 | FAST_DEBUG(" on line feed"); | 352 | FAST_DEBUG(" on line feed"); | ||
333 | const int absLine = curRelLine + firstLineIndex; | 353 | const int absLine = curRelLine + rangeStartLine; | ||
334 | twoViewCursor.openLine = twoViewCursor.closeLine = absLine; | 354 | twoViewCursor.openLine = twoViewCursor.closeLine = absLine; | ||
335 | twoViewCursor.openCol = twoViewCursor.closeCol = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen; | 355 | twoViewCursor.openCol = twoViewCursor.closeCol = curRelLineLen; | ||
336 | 356 | | |||
337 | // advance to next line | 357 | // advance to next line | ||
338 | const int advance = (index - curRelIndex) + 1; | 358 | const int advance = (index - curRelIndex) + 1; | ||
339 | curRelLine++; | 359 | ++curRelLine; | ||
340 | curRelCol = 0; | 360 | curRelCol = 0; | ||
341 | curRelIndex += advance; | 361 | curRelIndex += advance; | ||
342 | } else { // index < lineFeedIndex | 362 | } else { // index < lineFeedIndex | ||
343 | // on this line _before_ line feed | 363 | // on this line _before_ line feed | ||
344 | FAST_DEBUG(" before line feed"); | 364 | FAST_DEBUG(" before line feed"); | ||
345 | const int diff = (index - curRelIndex); | 365 | const int diff = (index - curRelIndex); | ||
346 | const int absLine = curRelLine + firstLineIndex; | 366 | const int absLine = curRelLine + rangeStartLine; | ||
347 | const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff; | 367 | const int absCol = curRelCol + diff; | ||
348 | twoViewCursor.openLine = twoViewCursor.closeLine = absLine; | 368 | twoViewCursor.openLine = twoViewCursor.closeLine = absLine; | ||
349 | twoViewCursor.openCol = twoViewCursor.closeCol = absCol; | 369 | twoViewCursor.openCol = twoViewCursor.closeCol = absCol; | ||
350 | 370 | | |||
351 | // advance on same line | 371 | // advance on same line | ||
352 | const int advance = diff + 1; | 372 | const int advance = diff + 1; | ||
353 | curRelCol += advance; | 373 | curRelCol += advance; | ||
354 | curRelIndex += advance; | 374 | curRelIndex += advance; | ||
355 | } | 375 | } | ||
356 | FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")"); | 376 | FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")"); | ||
357 | } else { // if (index > lineFeedIndex) | 377 | } else { // if (index > lineFeedIndex) | ||
358 | // not on this line | 378 | // not on this line | ||
359 | // advance to next line | 379 | // advance to next line | ||
360 | FAST_DEBUG(" not on this line"); | 380 | FAST_DEBUG(" not on this line"); | ||
361 | const int advance = curLineRemainder + 1; | 381 | ++curRelLine; | ||
362 | curRelLine++; | | |||
363 | curRelCol = 0; | 382 | curRelCol = 0; | ||
383 | const int advance = curLineRemainder + 1; | ||||
364 | curRelIndex += advance; | 384 | curRelIndex += advance; | ||
365 | } | 385 | } | ||
366 | } | 386 | } | ||
367 | | ||||
368 | ++iter; | 387 | ++iter; | ||
369 | } | 388 | } | ||
370 | 389 | | |||
371 | // build result array | 390 | // build result array | ||
372 | QVector<KTextEditor::Range> result(1 + numCaptures); | 391 | QVector<KTextEditor::Range> result(numCaptures + 1, KTextEditor::Range::invalid()); | ||
373 | for (int y = 0; y <= numCaptures; y++) { | 392 | for (int y = 0; y <= numCaptures; y++) { | ||
374 | IndexPair &pair = indexPairs[y]; | 393 | IndexPair &pair = indexPairs[y]; | ||
375 | if ((pair.openIndex == -1) || (pair.closeIndex == -1)) { | 394 | if (!(pair.openIndex == -1) || !(pair.closeIndex == -1)) { | ||
376 | result[y] = KTextEditor::Range::invalid(); | | |||
377 | } else { | | |||
378 | const TwoViewCursor *const openCursors = indicesToCursors[pair.openIndex]; | 395 | const TwoViewCursor *const openCursors = indicesToCursors[pair.openIndex]; | ||
379 | const TwoViewCursor *const closeCursors = indicesToCursors[pair.closeIndex]; | 396 | const TwoViewCursor *const closeCursors = indicesToCursors[pair.closeIndex]; | ||
380 | const int startLine = openCursors->openLine; | 397 | const int startLine = openCursors->openLine; | ||
381 | const int startCol = openCursors->openCol; | 398 | const int startCol = openCursors->openCol; | ||
382 | const int endLine = closeCursors->closeLine; | 399 | const int endLine = closeCursors->closeLine; | ||
383 | const int endCol = closeCursors->closeCol; | 400 | const int endCol = closeCursors->closeCol; | ||
384 | FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")"); | 401 | FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")"); | ||
385 | result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol); | 402 | result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol); | ||
386 | } | 403 | } | ||
387 | } | 404 | } | ||
388 | 405 | | |||
389 | // free structs allocated for indicesToCursors | 406 | // free structs allocated for indicesToCursors | ||
390 | iter = indicesToCursors.constBegin(); | 407 | iter = indicesToCursors.constBegin(); | ||
391 | while (iter != indicesToCursors.constEnd()) { | 408 | while (iter != indicesToCursors.constEnd()) { | ||
392 | TwoViewCursor *const twoViewCursor = *iter; | 409 | TwoViewCursor *const twoViewCursor = *iter; | ||
393 | delete twoViewCursor; | 410 | delete twoViewCursor; | ||
394 | ++iter; | 411 | ++iter; | ||
395 | } | 412 | } | ||
413 | | ||||
396 | return result; | 414 | return result; | ||
415 | } | ||||
397 | } else { | 416 | } else { | ||
398 | // single-line regex search (both forward of backward mode) | 417 | // single-line regex search (both forward of backward mode) | ||
399 | const int minLeft = inputRange.start().column(); | 418 | const int rangeStartCol = inputRange.start().column(); | ||
400 | const uint maxRight = inputRange.end().column(); // first not included | 419 | const uint rangeEndCol = inputRange.end().column(); | ||
401 | const int forMin = inputRange.start().line(); | 420 | | ||
402 | const int forMax = inputRange.end().line(); | 421 | const int rangeStartLine = inputRange.start().line(); | ||
403 | const int forInit = backwards ? forMax : forMin; | 422 | const int rangeEndLine = inputRange.end().line(); | ||
423 | | ||||
424 | const int forInit = backwards ? rangeEndLine : rangeStartLine; | ||||
425 | | ||||
404 | const int forInc = backwards ? -1 : +1; | 426 | const int forInc = backwards ? -1 : +1; | ||
405 | FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".." << (backwards ? forMin : forMax)); | 427 | | ||
406 | for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc) { | 428 | FAST_DEBUG("single line " << (backwards ? rangeEndLine : rangeStartLine) << ".." << (backwards ? rangeStartLine : rangeEndLine)); | ||
429 | | ||||
430 | for (int j = forInit; (rangeStartLine <= j) && (j <= rangeEndLine); j += forInc) { | ||||
407 | if (j < 0 || m_document->lines() <= j) { | 431 | if (j < 0 || m_document->lines() <= j) { | ||
408 | FAST_DEBUG("searchText | line " << j << ": no"); | 432 | FAST_DEBUG("searchText | line " << j << ": no"); | ||
409 | QVector<KTextEditor::Range> result; | 433 | return noResult; | ||
410 | result.append(KTextEditor::Range::invalid()); | | |||
411 | return result; | | |||
412 | } | 434 | } | ||
435 | | ||||
413 | const QString textLine = m_document->line(j); | 436 | const QString textLine = m_document->line(j); | ||
414 | 437 | | |||
415 | // Find (and don't match ^ in between...) | 438 | const int offset = (j == rangeStartLine) ? rangeStartCol : 0; | ||
416 | const int first = (j == forMin) ? minLeft : 0; | 439 | const int endLineMaxOffset = (j == rangeEndLine) ? rangeEndCol : textLine.length(); | ||
417 | const int last = (j == forMax) ? maxRight : textLine.length(); | 440 | | ||
418 | const int foundAt = (backwards ? regexp.lastIndexIn(textLine, first, last) : regexp.indexIn(textLine, first, last)); | 441 | bool found = false; | ||
419 | const bool found = (foundAt != -1); | 442 | | ||
420 | 443 | QRegularExpressionMatch match; | |||
421 | /* | 444 | | ||
422 | TODO do we still need this? | 445 | if (backwards) { | ||
423 | 446 | QRegularExpressionMatchIterator iter = regexp.globalMatch(textLine, offset); | |||
424 | // A special case which can only occur when searching with a regular expression consisting | 447 | while (iter.hasNext()) { | ||
425 | // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{'). | 448 | QRegularExpressionMatch curMatch = iter.next(); | ||
426 | if (myMatchLen == 0 && line == startPosition.line() && foundAt == (uint) col) | 449 | if (curMatch.hasMatch() && curMatch.capturedEnd() <= endLineMaxOffset) { | ||
427 | { | 450 | match.swap(curMatch); | ||
428 | if (col < lineLength(line)) | 451 | found = true; | ||
429 | col++; | 452 | } | ||
430 | else { | 453 | } | ||
431 | line++; | 454 | } else { | ||
432 | col = 0; | 455 | match = regexp.match(textLine, offset); | ||
456 | if (match.hasMatch() && match.capturedEnd() <= endLineMaxOffset) { | ||||
457 | found = true; | ||||
433 | } | 458 | } | ||
434 | continue; | | |||
435 | } | 459 | } | ||
436 | */ | | |||
437 | 460 | | |||
438 | if (found) { | 461 | if (found) { | ||
439 | FAST_DEBUG("line " << j << ": yes"); | 462 | FAST_DEBUG("line " << j << ": yes"); | ||
440 | 463 | | |||
441 | // build result array | 464 | // build result array | ||
442 | const int numCaptures = regexp.numCaptures(); | 465 | const int numCaptures = regexp.captureCount(); | ||
443 | QVector<KTextEditor::Range> result(1 + numCaptures); | 466 | QVector<KTextEditor::Range> result(numCaptures + 1); | ||
444 | result[0] = KTextEditor::Range(j, foundAt, j, foundAt + regexp.matchedLength()); | 467 | result[0] = KTextEditor::Range(j, match.capturedStart(), j, match.capturedEnd()); | ||
445 | FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << foundAt + regexp.matchedLength() << ")"); | 468 | | ||
446 | for (int y = 1; y <= numCaptures; y++) { | 469 | FAST_DEBUG("result range " << 0 << ": (" << j << ", " << match.capturedStart << ")..(" << j << ", " << match.capturedEnd() << ")"); | ||
447 | const int openIndex = regexp.pos(y); | 470 | | ||
471 | for (int y = 1; y <= numCaptures; ++y) { | ||||
472 | const int openIndex = match.capturedStart(y); | ||||
473 | | ||||
448 | if (openIndex == -1) { | 474 | if (openIndex == -1) { | ||
449 | result[y] = KTextEditor::Range::invalid(); | 475 | result[y] = KTextEditor::Range::invalid(); | ||
476 | | ||||
450 | FAST_DEBUG("capture []"); | 477 | FAST_DEBUG("capture []"); | ||
451 | } else { | 478 | } else { | ||
452 | const int closeIndex = openIndex + regexp.cap(y).length(); | 479 | const int closeIndex = match.capturedEnd(y); | ||
480 | | ||||
453 | FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")"); | 481 | FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")"); | ||
482 | | ||||
454 | result[y] = KTextEditor::Range(j, openIndex, j, closeIndex); | 483 | result[y] = KTextEditor::Range(j, openIndex, j, closeIndex); | ||
455 | } | 484 | } | ||
456 | } | 485 | } | ||
457 | return result; | 486 | return result; | ||
458 | } else { | 487 | } else { | ||
459 | FAST_DEBUG("searchText | line " << j << ": no"); | 488 | FAST_DEBUG("searchText | line " << j << ": no"); | ||
460 | } | 489 | } | ||
461 | } | 490 | } | ||
462 | } | 491 | } | ||
463 | 492 | return noResult; | |||
464 | QVector<KTextEditor::Range> result; | | |||
465 | result.append(KTextEditor::Range::invalid()); | | |||
466 | return result; | | |||
467 | } | 493 | } | ||
468 | 494 | | |||
469 | /*static*/ QString KateRegExpSearch::escapePlaintext(const QString &text) | 495 | /*static*/ QString KateRegExpSearch::escapePlaintext(const QString &text) | ||
470 | { | 496 | { | ||
471 | return buildReplacement(text, QStringList(), 0, false); | 497 | return buildReplacement(text, QStringList(), 0, false); | ||
472 | } | 498 | } | ||
473 | 499 | | |||
474 | /*static*/ QString KateRegExpSearch::buildReplacement(const QString &text, const QStringList &capturedTexts, int replacementCounter) | 500 | /*static*/ QString KateRegExpSearch::buildReplacement(const QString &text, const QStringList &capturedTexts, int replacementCounter) | ||
▲ Show 20 Lines • Show All 242 Lines • ▼ Show 20 Line(s) | 742 | default: | |||
717 | out << text[input]; | 743 | out << text[input]; | ||
718 | input++; | 744 | input++; | ||
719 | } | 745 | } | ||
720 | } | 746 | } | ||
721 | 747 | | |||
722 | return out.str(); | 748 | return out.str(); | ||
723 | } | 749 | } | ||
724 | 750 | | |||
751 | QString KateRegExpSearch::repairPattern(const QString &pattern, bool &stillMultiLine) | ||||
752 | { | ||||
753 | // these characters can make a pattern multi-line: | ||||
754 | // \n, \x000A, \x????-\x????, \0012, \0???-\0??? | ||||
755 | // a multi-line pattern must not pass as single-line, the other | ||||
756 | // way around will just result in slower searches and is therefore | ||||
757 | // not as critical | ||||
758 | | ||||
759 | const int inputLen = pattern.length(); | ||||
760 | | ||||
761 | // prepare output | ||||
762 | QString output; | ||||
763 | output.reserve(2 * inputLen + 1); // twice should be enough for the average case | ||||
764 | | ||||
765 | // parser state | ||||
766 | bool insideClass = false; | ||||
767 | | ||||
768 | stillMultiLine = false; | ||||
769 | int input = 0; | ||||
770 | while (input < inputLen) { | ||||
771 | if (insideClass) { | ||||
772 | // wait for closing, unescaped ']' | ||||
773 | switch (pattern[input].unicode()) { | ||||
774 | case L'\\': | ||||
775 | switch (pattern[input + 1].unicode()) { | ||||
776 | case L'x': | ||||
777 | if (input + 5 < inputLen) { | ||||
778 | // copy "\x????" unmodified | ||||
779 | output.append(pattern.midRef(input, 6)); | ||||
780 | input += 6; | ||||
781 | } else { | ||||
782 | // copy "\x" unmodified | ||||
783 | output.append(pattern.midRef(input, 2)); | ||||
784 | input += 2; | ||||
785 | } | ||||
786 | stillMultiLine = true; | ||||
787 | break; | ||||
788 | | ||||
789 | case L'0': | ||||
790 | if (input + 4 < inputLen) { | ||||
791 | // copy "\0???" unmodified | ||||
792 | output.append(pattern.midRef(input, 5)); | ||||
793 | input += 5; | ||||
794 | } else { | ||||
795 | // copy "\0" unmodified | ||||
796 | output.append(pattern.midRef(input, 2)); | ||||
797 | input += 2; | ||||
798 | } | ||||
799 | stillMultiLine = true; | ||||
800 | break; | ||||
801 | | ||||
802 | case L's': | ||||
803 | // replace "\s" with "[ \t]" | ||||
804 | output.append(QLatin1String(" \\t")); | ||||
805 | input += 2; | ||||
806 | break; | ||||
807 | | ||||
808 | case L'n': | ||||
809 | stillMultiLine = true; | ||||
810 | // FALLTROUGH | ||||
811 | Q_FALLTHROUGH(); | ||||
812 | | ||||
813 | default: | ||||
814 | // copy "\?" unmodified | ||||
815 | output.append(pattern.midRef(input, 2)); | ||||
816 | input += 2; | ||||
817 | } | ||||
818 | break; | ||||
819 | | ||||
820 | case L']': | ||||
821 | // copy "]" unmodified | ||||
822 | insideClass = false; | ||||
823 | output.append(pattern[input]); | ||||
824 | ++input; | ||||
825 | break; | ||||
826 | | ||||
827 | default: | ||||
828 | // copy "?" unmodified | ||||
829 | output.append(pattern[input]); | ||||
830 | ++input; | ||||
831 | } | ||||
832 | } else { | ||||
833 | // search for real dots and \s | ||||
834 | switch (pattern[input].unicode()) { | ||||
835 | case L'\\': | ||||
836 | switch (pattern[input + 1].unicode()) { | ||||
837 | case L'x': | ||||
838 | if (input + 5 < inputLen) { | ||||
839 | // copy "\x????" unmodified | ||||
840 | output.append(pattern.midRef(input, 6)); | ||||
841 | input += 6; | ||||
842 | } else { | ||||
843 | // copy "\x" unmodified | ||||
844 | output.append(pattern.midRef(input, 2)); | ||||
845 | input += 2; | ||||
846 | } | ||||
847 | stillMultiLine = true; | ||||
848 | break; | ||||
849 | | ||||
850 | case L'0': | ||||
851 | if (input + 4 < inputLen) { | ||||
852 | // copy "\0???" unmodified | ||||
853 | output.append(pattern.midRef(input, 5)); | ||||
854 | input += 5; | ||||
855 | } else { | ||||
856 | // copy "\0" unmodified | ||||
857 | output.append(pattern.midRef(input, 2)); | ||||
858 | input += 2; | ||||
859 | } | ||||
860 | stillMultiLine = true; | ||||
861 | break; | ||||
862 | | ||||
863 | case L's': | ||||
864 | // replace "\s" with "[ \t]" | ||||
865 | output.append(QLatin1String("[ \\t]")); | ||||
866 | input += 2; | ||||
867 | break; | ||||
868 | | ||||
869 | case L'n': | ||||
870 | stillMultiLine = true; | ||||
871 | // FALLTROUGH | ||||
872 | Q_FALLTHROUGH(); | ||||
873 | default: | ||||
874 | // copy "\?" unmodified | ||||
875 | output.append(pattern.midRef(input, 2)); | ||||
876 | input += 2; | ||||
877 | } | ||||
878 | break; | ||||
879 | | ||||
880 | case L'[': | ||||
881 | // copy "[" unmodified | ||||
882 | insideClass = true; | ||||
883 | output.append(pattern[input]); | ||||
884 | ++input; | ||||
885 | break; | ||||
886 | | ||||
887 | default: | ||||
888 | // copy "?" unmodified | ||||
889 | output.append(pattern[input]); | ||||
890 | ++input; | ||||
891 | } | ||||
892 | } | ||||
893 | } | ||||
894 | return output; | ||||
895 | } | ||||
896 | | ||||
725 | // Kill our helpers again | 897 | // Kill our helpers again | ||
726 | #ifdef FAST_DEBUG_ENABLE | 898 | #ifdef FAST_DEBUG_ENABLE | ||
727 | #undef FAST_DEBUG_ENABLE | 899 | #undef FAST_DEBUG_ENABLE | ||
728 | #endif | 900 | #endif | ||
729 | #undef FAST_DEBUG | 901 | #undef FAST_DEBUG |