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