File indexing completed on 2024-04-28 15:30:43
0001 /* 0002 SPDX-FileCopyrightText: 2009-2010 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0003 SPDX-FileCopyrightText: 2007 Sebastian Pipping <webmaster@hartwork.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 // BEGIN includes 0009 #include "kateplaintextsearch.h" 0010 0011 #include "kateregexpsearch.h" 0012 0013 #include <ktexteditor/document.h> 0014 0015 #include "katepartdebug.h" 0016 0017 #include <QRegularExpression> 0018 // END includes 0019 0020 // BEGIN d'tor, c'tor 0021 // 0022 // KateSearch Constructor 0023 // 0024 KatePlainTextSearch::KatePlainTextSearch(const KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity, bool wholeWords) 0025 : m_document(document) 0026 , m_caseSensitivity(caseSensitivity) 0027 , m_wholeWords(wholeWords) 0028 { 0029 } 0030 0031 // END 0032 0033 KTextEditor::Range KatePlainTextSearch::search(const QString &text, KTextEditor::Range inputRange, bool backwards) 0034 { 0035 // abuse regex for whole word plaintext search 0036 if (m_wholeWords) { 0037 // escape dot and friends 0038 const QString workPattern = QStringLiteral("\\b%1\\b").arg(QRegularExpression::escape(text)); 0039 0040 QRegularExpression::PatternOptions options; 0041 options |= m_caseSensitivity == Qt::CaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption; 0042 0043 return KateRegExpSearch(m_document).search(workPattern, inputRange, backwards, options).at(0); 0044 } 0045 0046 if (text.isEmpty() || !inputRange.isValid() || (inputRange.start() == inputRange.end())) { 0047 return KTextEditor::Range::invalid(); 0048 } 0049 0050 // split multi-line needle into single lines 0051 const QVector needleLines = text.splitRef(QLatin1Char('\n')); 0052 0053 if (needleLines.count() > 1) { 0054 // multi-line plaintext search (both forwards or backwards) 0055 const int forMin = inputRange.start().line(); // first line in range 0056 const int forMax = inputRange.end().line() + 1 - needleLines.count(); // last line in range 0057 const int forInit = backwards ? forMax : forMin; 0058 const int forInc = backwards ? -1 : +1; 0059 0060 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc) { 0061 // try to match all lines 0062 const int startCol = m_document->lineLength(j) - needleLines[0].length(); 0063 for (int k = 0; k < needleLines.count(); k++) { 0064 // which lines to compare 0065 const auto &needleLine = needleLines[k]; 0066 const QString &hayLine = m_document->line(j + k); 0067 0068 // position specific comparison (first, middle, last) 0069 if (k == 0) { 0070 // first line 0071 if (forMin == j && startCol < inputRange.start().column()) { 0072 break; 0073 } 0074 0075 // NOTE: QString("")::endsWith("") is false in Qt, therefore we need the additional checks. 0076 const bool endsWith = hayLine.endsWith(needleLine, m_caseSensitivity) || (hayLine.isEmpty() && needleLine.isEmpty()); 0077 if (!endsWith) { 0078 break; 0079 } 0080 } else if (k == needleLines.count() - 1) { 0081 // last line 0082 const int maxRight = (j + k == inputRange.end().line()) ? inputRange.end().column() : hayLine.length(); 0083 0084 // NOTE: QString("")::startsWith("") is false in Qt, therefore we need the additional checks. 0085 const bool startsWith = hayLine.startsWith(needleLine, m_caseSensitivity) || (hayLine.isEmpty() && needleLine.isEmpty()); 0086 if (startsWith && needleLine.length() <= maxRight) { 0087 return KTextEditor::Range(j, startCol, j + k, needleLine.length()); 0088 } 0089 } else { 0090 // mid lines 0091 if (hayLine.compare(needleLine, m_caseSensitivity) != 0) { 0092 break; 0093 } 0094 } 0095 } 0096 } 0097 0098 // not found 0099 return KTextEditor::Range::invalid(); 0100 } else { 0101 // single-line plaintext search (both forward of backward mode) 0102 const int startCol = inputRange.start().column(); 0103 const int endCol = inputRange.end().column(); // first not included 0104 const int startLine = inputRange.start().line(); 0105 const int endLine = inputRange.end().line(); 0106 const int forInc = backwards ? -1 : +1; 0107 0108 for (int line = backwards ? endLine : startLine; (startLine <= line) && (line <= endLine); line += forInc) { 0109 if ((line < 0) || (m_document->lines() <= line)) { 0110 qCWarning(LOG_KTE) << "line " << line << " is not within interval [0.." << m_document->lines() << ") ... returning invalid range"; 0111 return KTextEditor::Range::invalid(); 0112 } 0113 0114 const QString textLine = m_document->line(line); 0115 0116 const int offset = (line == startLine) ? startCol : 0; 0117 const int line_end = (line == endLine) ? endCol : textLine.length(); 0118 const int foundAt = 0119 backwards ? textLine.lastIndexOf(text, line_end - text.length(), m_caseSensitivity) : textLine.indexOf(text, offset, m_caseSensitivity); 0120 0121 if ((offset <= foundAt) && (foundAt + text.length() <= line_end)) { 0122 return KTextEditor::Range(line, foundAt, line, foundAt + text.length()); 0123 } 0124 } 0125 } 0126 return KTextEditor::Range::invalid(); 0127 }