KTextEditor

kateregexpsearch.cpp
1 /* SPDX-License-Identifier: LGPL-2.0-or-later
2 
3  Copyright (C) 2010 Bernhard Beschow <[email protected]>
4  Copyright (C) 2007 Sebastian Pipping <[email protected]>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
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
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 // BEGIN includes
23 #include "kateregexpsearch.h"
24 #include "kateregexp.h"
25 
26 #include <ktexteditor/document.h>
27 // END includes
28 
29 // Turn debug messages on/off here
30 // #define FAST_DEBUG_ENABLE
31 
32 #ifdef FAST_DEBUG_ENABLE
33 #define FAST_DEBUG(x) qCDebug(LOG_KTE) << x
34 #else
35 #define FAST_DEBUG(x)
36 #endif
37 
38 class KateRegExpSearch::ReplacementStream
39 {
40 public:
41  struct counter {
42  counter(int value, int minWidth)
43  : value(value)
44  , minWidth(minWidth)
45  {
46  }
47 
48  const int value;
49  const int minWidth;
50  };
51 
52  struct cap {
53  cap(int n)
54  : n(n)
55  {
56  }
57 
58  const int n;
59  };
60 
61  enum CaseConversion {
62  upperCase,
63  upperCaseFirst,
64  lowerCase,
65  lowerCaseFirst,
66  keepCase
67  };
68 
69 public:
70  ReplacementStream(const QStringList &capturedTexts);
71 
72  QString str() const
73  {
74  return m_str;
75  }
76 
77  ReplacementStream &operator<<(const QString &);
78  ReplacementStream &operator<<(const counter &);
79  ReplacementStream &operator<<(const cap &);
80  ReplacementStream &operator<<(CaseConversion);
81 
82 private:
83  const QStringList m_capturedTexts;
84  CaseConversion m_caseConversion;
85  QString m_str;
86 };
87 
88 KateRegExpSearch::ReplacementStream::ReplacementStream(const QStringList &capturedTexts)
89  : m_capturedTexts(capturedTexts)
90  , m_caseConversion(keepCase)
91 {
92 }
93 
94 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const QString &str)
95 {
96  switch (m_caseConversion) {
97  case upperCase:
98  // Copy as uppercase
99  m_str.append(str.toUpper());
100  break;
101 
102  case upperCaseFirst:
103  if (str.length() > 0) {
104  m_str.append(str.at(0).toUpper());
105  m_str.append(str.midRef(1));
106  m_caseConversion = keepCase;
107  }
108  break;
109 
110  case lowerCase:
111  // Copy as lowercase
112  m_str.append(str.toLower());
113  break;
114 
115  case lowerCaseFirst:
116  if (str.length() > 0) {
117  m_str.append(str.at(0).toLower());
118  m_str.append(str.midRef(1));
119  m_caseConversion = keepCase;
120  }
121  break;
122 
123  case keepCase: // FALLTHROUGH
124  default:
125  // Copy unmodified
126  m_str.append(str);
127  break;
128  }
129 
130  return *this;
131 }
132 
133 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const counter &c)
134 {
135  // Zero padded counter value
136  m_str.append(QStringLiteral("%1").arg(c.value, c.minWidth, 10, QLatin1Char('0')));
137 
138  return *this;
139 }
140 
141 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const cap &cap)
142 {
143  if (0 <= cap.n && cap.n < m_capturedTexts.size()) {
144  (*this) << m_capturedTexts[cap.n];
145  } else {
146  // Insert just the number to be consistent with QRegExp ("\c" becomes "c")
147  m_str.append(QString::number(cap.n));
148  }
149 
150  return *this;
151 }
152 
153 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(CaseConversion caseConversion)
154 {
155  m_caseConversion = caseConversion;
156 
157  return *this;
158 }
159 
160 // BEGIN d'tor, c'tor
161 //
162 // KateSearch Constructor
163 //
164 KateRegExpSearch::KateRegExpSearch(const KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity)
165  : m_document(document)
166  , m_caseSensitivity(caseSensitivity)
167 {
168 }
169 
170 //
171 // KateSearch Destructor
172 //
173 KateRegExpSearch::~KateRegExpSearch()
174 {
175 }
176 
177 // helper structs for captures re-construction
178 struct TwoViewCursor {
179  int index;
180  int openLine;
181  int openCol;
182  int closeLine;
183  int closeCol;
184  // note: open/close distinction does not seem needed
185  // anymore. i keep it to make a potential way back
186  // easier. overhead is minimal.
187 };
188 
189 struct IndexPair {
190  int openIndex;
191  int closeIndex;
192 };
193 
194 QVector<KTextEditor::Range> KateRegExpSearch::search(const QString &pattern, const KTextEditor::Range &inputRange, bool backwards)
195 {
196  // regex search
197  KateRegExp regexp(pattern, m_caseSensitivity);
198 
199  if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end())) {
202  return result;
203  }
204 
205  // detect pattern type (single- or mutli-line)
206  bool isMultiLine;
207 
208  // detect '.' and '\s' and fix them
209  const bool dotMatchesNewline = false; // TODO
210  const int replacements = regexp.repairPattern(isMultiLine);
211  if (dotMatchesNewline && (replacements > 0)) {
212  isMultiLine = true;
213  }
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 
224  // nothing to do...
225  if (firstLineIndex >= m_document->lines()) {
228  return result;
229  }
230 
231  QVector<int> lineLens(inputLineCount);
232 
233  // first line
234  if (firstLineIndex < 0 || m_document->lines() <= firstLineIndex) {
237  return result;
238  }
239 
240  const QString firstLine = m_document->line(firstLineIndex);
241 
242  const int firstLineLen = firstLine.length() - minColStart;
243  wholeDocument.append(firstLine.rightRef(firstLineLen));
244  lineLens[0] = firstLineLen;
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) {
253  return result;
254  }
255  const QString text = m_document->line(lineNum);
256 
257  lineLens[i] = text.length();
258  wholeDocument.append(QLatin1Char('\n'));
259  wholeDocument.append(text);
260  FAST_DEBUG(" line" << i << "has length" << lineLens[i]);
261  }
262 
263  const int pos = backwards ? regexp.lastIndexIn(wholeDocument, 0, wholeDocument.length()) : regexp.indexIn(wholeDocument, 0, wholeDocument.length());
264  if (pos == -1) {
265  // no match
266  FAST_DEBUG("not found");
267  {
270  return result;
271  }
272  }
273 
274 #ifdef FAST_DEBUG_ENABLE
275  const int matchLen = regexp.matchedLength();
276  FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen);
277 #endif
278 
279  // save opening and closing indices and build a map.
280  // the correct values will be written into it later.
281  QMap<int, TwoViewCursor *> indicesToCursors;
282  const int numCaptures = regexp.numCaptures();
283  QVector<IndexPair> indexPairs(1 + numCaptures);
284  for (int z = 0; z <= numCaptures; z++) {
285  const int openIndex = regexp.pos(z);
286  IndexPair &pair = indexPairs[z];
287  if (openIndex == -1) {
288  // empty capture gives invalid
289  pair.openIndex = -1;
290  pair.closeIndex = -1;
291  FAST_DEBUG("capture []");
292  } else {
293  const int closeIndex = openIndex + regexp.cap(z).length();
294  pair.openIndex = openIndex;
295  pair.closeIndex = closeIndex;
296  FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]");
297 
298  // each key no more than once
299  if (!indicesToCursors.contains(openIndex)) {
300  TwoViewCursor *twoViewCursor = new TwoViewCursor;
301  twoViewCursor->index = openIndex;
302  indicesToCursors.insert(openIndex, twoViewCursor);
303  FAST_DEBUG(" border index added: " << openIndex);
304  }
305  if (!indicesToCursors.contains(closeIndex)) {
306  TwoViewCursor *twoViewCursor = new TwoViewCursor;
307  twoViewCursor->index = closeIndex;
308  indicesToCursors.insert(closeIndex, twoViewCursor);
309  FAST_DEBUG(" border index added: " << closeIndex);
310  }
311  }
312  }
313 
314  // find out where they belong
315  int curRelLine = 0;
316  int curRelCol = 0;
317  int curRelIndex = 0;
318  QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin();
319  while (iter != indicesToCursors.constEnd()) {
320  // forward to index, save line/col
321  const int index = (*iter)->index;
322  FAST_DEBUG("resolving position" << index);
323  TwoViewCursor &twoViewCursor = *(*iter);
324  while (curRelIndex <= index) {
325  FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = " << curRelIndex << "relative, steps more to go" << index - curRelIndex);
326  const int curRelLineLen = lineLens[curRelLine];
327  const int curLineRemainder = curRelLineLen - curRelCol;
328  const int lineFeedIndex = curRelIndex + curLineRemainder;
329  if (index <= lineFeedIndex) {
330  if (index == lineFeedIndex) {
331  // on this line _on_ line feed
332  FAST_DEBUG(" on line feed");
333  const int absLine = curRelLine + firstLineIndex;
334  twoViewCursor.openLine = twoViewCursor.closeLine = absLine;
335  twoViewCursor.openCol = twoViewCursor.closeCol = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen;
336 
337  // advance to next line
338  const int advance = (index - curRelIndex) + 1;
339  curRelLine++;
340  curRelCol = 0;
341  curRelIndex += advance;
342  } else { // index < lineFeedIndex
343  // on this line _before_ line feed
344  FAST_DEBUG(" before line feed");
345  const int diff = (index - curRelIndex);
346  const int absLine = curRelLine + firstLineIndex;
347  const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff;
348  twoViewCursor.openLine = twoViewCursor.closeLine = absLine;
349  twoViewCursor.openCol = twoViewCursor.closeCol = absCol;
350 
351  // advance on same line
352  const int advance = diff + 1;
353  curRelCol += advance;
354  curRelIndex += advance;
355  }
356  FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")");
357  } else { // if (index > lineFeedIndex)
358  // not on this line
359  // advance to next line
360  FAST_DEBUG(" not on this line");
361  const int advance = curLineRemainder + 1;
362  curRelLine++;
363  curRelCol = 0;
364  curRelIndex += advance;
365  }
366  }
367 
368  ++iter;
369  }
370 
371  // build result array
372  QVector<KTextEditor::Range> result(1 + numCaptures);
373  for (int y = 0; y <= numCaptures; y++) {
374  IndexPair &pair = indexPairs[y];
375  if ((pair.openIndex == -1) || (pair.closeIndex == -1)) {
376  result[y] = KTextEditor::Range::invalid();
377  } else {
378  const TwoViewCursor *const openCursors = indicesToCursors[pair.openIndex];
379  const TwoViewCursor *const closeCursors = indicesToCursors[pair.closeIndex];
380  const int startLine = openCursors->openLine;
381  const int startCol = openCursors->openCol;
382  const int endLine = closeCursors->closeLine;
383  const int endCol = closeCursors->closeCol;
384  FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")");
385  result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol);
386  }
387  }
388 
389  // free structs allocated for indicesToCursors
390  iter = indicesToCursors.constBegin();
391  while (iter != indicesToCursors.constEnd()) {
392  TwoViewCursor *const twoViewCursor = *iter;
393  delete twoViewCursor;
394  ++iter;
395  }
396  return result;
397  } else {
398  // single-line regex search (both forward of backward mode)
399  const int minLeft = inputRange.start().column();
400  const uint maxRight = inputRange.end().column(); // first not included
401  const int forMin = inputRange.start().line();
402  const int forMax = inputRange.end().line();
403  const int forInit = backwards ? forMax : forMin;
404  const int forInc = backwards ? -1 : +1;
405  FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".." << (backwards ? forMin : forMax));
406  for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc) {
407  if (j < 0 || m_document->lines() <= j) {
408  FAST_DEBUG("searchText | line " << j << ": no");
411  return result;
412  }
413  const QString textLine = m_document->line(j);
414 
415  // Find (and don't match ^ in between...)
416  const int first = (j == forMin) ? minLeft : 0;
417  const int last = (j == forMax) ? maxRight : textLine.length();
418  const int foundAt = (backwards ? regexp.lastIndexIn(textLine, first, last) : regexp.indexIn(textLine, first, last));
419  const bool found = (foundAt != -1);
420 
421  /*
422  TODO do we still need this?
423 
424  // A special case which can only occur when searching with a regular expression consisting
425  // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
426  if (myMatchLen == 0 && line == startPosition.line() && foundAt == (uint) col)
427  {
428  if (col < lineLength(line))
429  col++;
430  else {
431  line++;
432  col = 0;
433  }
434  continue;
435  }
436  */
437 
438  if (found) {
439  FAST_DEBUG("line " << j << ": yes");
440 
441  // build result array
442  const int numCaptures = regexp.numCaptures();
443  QVector<KTextEditor::Range> result(1 + numCaptures);
444  result[0] = KTextEditor::Range(j, foundAt, j, foundAt + regexp.matchedLength());
445  FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << foundAt + regexp.matchedLength() << ")");
446  for (int y = 1; y <= numCaptures; y++) {
447  const int openIndex = regexp.pos(y);
448  if (openIndex == -1) {
449  result[y] = KTextEditor::Range::invalid();
450  FAST_DEBUG("capture []");
451  } else {
452  const int closeIndex = openIndex + regexp.cap(y).length();
453  FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")");
454  result[y] = KTextEditor::Range(j, openIndex, j, closeIndex);
455  }
456  }
457  return result;
458  } else {
459  FAST_DEBUG("searchText | line " << j << ": no");
460  }
461  }
462  }
463 
466  return result;
467 }
468 
470 {
471  return buildReplacement(text, QStringList(), 0, false);
472 }
473 
474 /*static*/ QString KateRegExpSearch::buildReplacement(const QString &text, const QStringList &capturedTexts, int replacementCounter)
475 {
476  return buildReplacement(text, capturedTexts, replacementCounter, true);
477 }
478 
479 /*static*/ QString KateRegExpSearch::buildReplacement(const QString &text, const QStringList &capturedTexts, int replacementCounter, bool replacementGoodies)
480 {
481  // get input
482  const int inputLen = text.length();
483  int input = 0; // walker index
484 
485  // prepare output
486  ReplacementStream out(capturedTexts);
487 
488  while (input < inputLen) {
489  switch (text[input].unicode()) {
490  case L'\n':
491  out << text[input];
492  input++;
493  break;
494 
495  case L'\\':
496  if (input + 1 >= inputLen) {
497  // copy backslash
498  out << text[input];
499  input++;
500  break;
501  }
502 
503  switch (text[input + 1].unicode()) {
504  case L'0': // "\0000".."\0377"
505  if (input + 4 >= inputLen) {
506  out << ReplacementStream::cap(0);
507  input += 2;
508  } else {
509  bool stripAndSkip = false;
510  const ushort text_2 = text[input + 2].unicode();
511  if ((text_2 >= L'0') && (text_2 <= L'3')) {
512  const ushort text_3 = text[input + 3].unicode();
513  if ((text_3 >= L'0') && (text_3 <= L'7')) {
514  const ushort text_4 = text[input + 4].unicode();
515  if ((text_4 >= L'0') && (text_4 <= L'7')) {
516  int digits[3];
517  for (int i = 0; i < 3; i++) {
518  digits[i] = 7 - (L'7' - text[input + 2 + i].unicode());
519  }
520  const int ch = 64 * digits[0] + 8 * digits[1] + digits[2];
521  out << QChar(ch);
522  input += 5;
523  } else {
524  stripAndSkip = true;
525  }
526  } else {
527  stripAndSkip = true;
528  }
529  } else {
530  stripAndSkip = true;
531  }
532 
533  if (stripAndSkip) {
534  out << ReplacementStream::cap(0);
535  input += 2;
536  }
537  }
538  break;
539 
540  // single letter captures \x
541  case L'1':
542  case L'2':
543  case L'3':
544  case L'4':
545  case L'5':
546  case L'6':
547  case L'7':
548  case L'8':
549  case L'9':
550  out << ReplacementStream::cap(9 - (L'9' - text[input + 1].unicode()));
551  input += 2;
552  break;
553 
554  // multi letter captures \{xxxx}
555  case L'{': {
556  // allow {1212124}.... captures, see bug 365124 + testReplaceManyCapturesBug365124
557  int capture = 0;
558  int captureSize = 2;
559  while ((input + captureSize) < inputLen) {
560  const ushort nextDigit = text[input + captureSize].unicode();
561  if ((nextDigit >= L'0') && (nextDigit <= L'9')) {
562  capture = (10 * capture) + (9 - (L'9' - nextDigit));
563  ++captureSize;
564  continue;
565  }
566  if (nextDigit == L'}') {
567  ++captureSize;
568  break;
569  }
570  break;
571  }
572  out << ReplacementStream::cap(capture);
573  input += captureSize;
574  break;
575  }
576 
577  case L'E': // FALLTHROUGH
578  case L'L': // FALLTHROUGH
579  case L'l': // FALLTHROUGH
580  case L'U': // FALLTHROUGH
581  case L'u':
582  if (!replacementGoodies) {
583  // strip backslash ("\?" -> "?")
584  out << text[input + 1];
585  } else {
586  // handle case switcher
587  switch (text[input + 1].unicode()) {
588  case L'L':
589  out << ReplacementStream::lowerCase;
590  break;
591 
592  case L'l':
593  out << ReplacementStream::lowerCaseFirst;
594  break;
595 
596  case L'U':
597  out << ReplacementStream::upperCase;
598  break;
599 
600  case L'u':
601  out << ReplacementStream::upperCaseFirst;
602  break;
603 
604  case L'E': // FALLTHROUGH
605  default:
606  out << ReplacementStream::keepCase;
607  }
608  }
609  input += 2;
610  break;
611 
612  case L'#':
613  if (!replacementGoodies) {
614  // strip backslash ("\?" -> "?")
615  out << text[input + 1];
616  input += 2;
617  } else {
618  // handle replacement counter
619  // eat and count all following hash marks
620  // each hash stands for a leading zero: \### will produces 001, 002, ...
621  int minWidth = 1;
622  while ((input + minWidth + 1 < inputLen) && (text[input + minWidth + 1].unicode() == L'#')) {
623  minWidth++;
624  }
625  out << ReplacementStream::counter(replacementCounter, minWidth);
626  input += 1 + minWidth;
627  }
628  break;
629 
630  case L'a':
631  out << QChar(0x07);
632  input += 2;
633  break;
634 
635  case L'f':
636  out << QChar(0x0c);
637  input += 2;
638  break;
639 
640  case L'n':
641  out << QChar(0x0a);
642  input += 2;
643  break;
644 
645  case L'r':
646  out << QChar(0x0d);
647  input += 2;
648  break;
649 
650  case L't':
651  out << QChar(0x09);
652  input += 2;
653  break;
654 
655  case L'v':
656  out << QChar(0x0b);
657  input += 2;
658  break;
659 
660  case L'x': // "\x0000".."\xffff"
661  if (input + 5 >= inputLen) {
662  // strip backslash ("\x" -> "x")
663  out << text[input + 1];
664  input += 2;
665  } else {
666  bool stripAndSkip = false;
667  const ushort text_2 = text[input + 2].unicode();
668  if (((text_2 >= L'0') && (text_2 <= L'9')) || ((text_2 >= L'a') && (text_2 <= L'f')) || ((text_2 >= L'A') && (text_2 <= L'F'))) {
669  const ushort text_3 = text[input + 3].unicode();
670  if (((text_3 >= L'0') && (text_3 <= L'9')) || ((text_3 >= L'a') && (text_3 <= L'f')) || ((text_3 >= L'A') && (text_3 <= L'F'))) {
671  const ushort text_4 = text[input + 4].unicode();
672  if (((text_4 >= L'0') && (text_4 <= L'9')) || ((text_4 >= L'a') && (text_4 <= L'f')) || ((text_4 >= L'A') && (text_4 <= L'F'))) {
673  const ushort text_5 = text[input + 5].unicode();
674  if (((text_5 >= L'0') && (text_5 <= L'9')) || ((text_5 >= L'a') && (text_5 <= L'f')) || ((text_5 >= L'A') && (text_5 <= L'F'))) {
675  int digits[4];
676  for (int i = 0; i < 4; i++) {
677  const ushort cur = text[input + 2 + i].unicode();
678  if ((cur >= L'0') && (cur <= L'9')) {
679  digits[i] = 9 - (L'9' - cur);
680  } else if ((cur >= L'a') && (cur <= L'f')) {
681  digits[i] = 15 - (L'f' - cur);
682  } else { // if ((cur >= L'A') && (cur <= L'F')))
683  digits[i] = 15 - (L'F' - cur);
684  }
685  }
686 
687  const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3];
688  out << QChar(ch);
689  input += 6;
690  } else {
691  stripAndSkip = true;
692  }
693  } else {
694  stripAndSkip = true;
695  }
696  } else {
697  stripAndSkip = true;
698  }
699  }
700 
701  if (stripAndSkip) {
702  // strip backslash ("\x" -> "x")
703  out << text[input + 1];
704  input += 2;
705  }
706  }
707  break;
708 
709  default:
710  // strip backslash ("\?" -> "?")
711  out << text[input + 1];
712  input += 2;
713  }
714  break;
715 
716  default:
717  out << text[input];
718  input++;
719  }
720  }
721 
722  return out.str();
723 }
724 
725 // Kill our helpers again
726 #ifdef FAST_DEBUG_ENABLE
727 #undef FAST_DEBUG_ENABLE
728 #endif
729 #undef FAST_DEBUG
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
QString & append(QChar ch)
QString toUpper() const const
static QString buildReplacement(const QString &text, const QStringList &capturedTexts, int replacementCounter)
Returns a modified version of text where.
bool contains(const Key &key) const const
void append(const T &value)
QMap::const_iterator constBegin() const const
static QString escapePlaintext(const QString &text)
Returns a modified version of text where escape sequences are resolved, e.g.
constexpr bool isValid() const Q_DECL_NOEXCEPT
Validity check.
QString number(int n, int base)
A KParts derived class representing a text document.
Definition: document.h:199
QMap::const_iterator constEnd() const const
constexpr int column() const Q_DECL_NOEXCEPT
Retrieve the column on which this cursor is situated.
Definition: cursor.h:217
QStringRef rightRef(int n) const const
constexpr Cursor start() const Q_DECL_NOEXCEPT
Get the start position of this range.
An object representing a section of text, from one Cursor to another.
QString toLower() const const
QStringRef midRef(int position, int n) const const
QChar toLower() const const
QVector< KTextEditor::Range > search(const QString &pattern, const KTextEditor::Range &inputRange, bool backwards=false)
Search for the regular expression pattern inside the range inputRange.
QChar toUpper() const const
constexpr Cursor end() const Q_DECL_NOEXCEPT
Get the end position of this range.
const QChar at(int position) const const
constexpr int line() const Q_DECL_NOEXCEPT
Retrieve the line on which this cursor is situated.
Definition: cursor.h:199
int length() const const
static constexpr Range invalid() Q_DECL_NOEXCEPT
Returns an invalid range.
QMap::iterator insert(const Key &key, const T &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Jun 5 2020 22:55:02 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.