File indexing completed on 2024-04-14 05:44:14
0001 /* 0002 SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com> 0003 SPDX-FileCopyrightText: 2009 Thomas Dreibholz <dreibh@iem.uni-due.de> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "SearchHistoryTask.h" 0009 0010 #include <QApplication> 0011 #include <QTextStream> 0012 0013 #include "../decoders/PlainTextDecoder.h" 0014 #include "Emulation.h" 0015 0016 namespace Konsole 0017 { 0018 void SearchHistoryTask::addScreenWindow(Session *session, ScreenWindow *searchWindow) 0019 { 0020 _windows.insert(session, searchWindow); 0021 } 0022 0023 void SearchHistoryTask::execute() 0024 { 0025 auto iter = QMapIterator<QPointer<Session>, ScreenWindowPtr>(_windows); 0026 0027 while (iter.hasNext()) { 0028 iter.next(); 0029 executeOnScreenWindow(iter.key(), iter.value()); 0030 } 0031 0032 if (autoDelete()) { 0033 deleteLater(); 0034 } 0035 } 0036 0037 void SearchHistoryTask::executeOnScreenWindow(const QPointer<Session> &session, const ScreenWindowPtr &window) 0038 { 0039 Q_ASSERT(session); 0040 Q_ASSERT(window); 0041 0042 Emulation *emulation = session->emulation(); 0043 0044 if (!_regExp.pattern().isEmpty()) { 0045 int pos = -1; 0046 const bool forwards = (_direction == Enum::ForwardsSearch); 0047 const int lastLine = window->lineCount() - 1; 0048 0049 int startLine; 0050 if (forwards && (_startLine == lastLine)) { 0051 startLine = 0; 0052 } else if (!forwards && (_startLine == 0)) { 0053 startLine = lastLine; 0054 } else { 0055 startLine = _startLine + (forwards ? 1 : -1); 0056 } 0057 0058 QString string; 0059 0060 // text stream to read history into string for pattern or regular expression searching 0061 QTextStream searchStream(&string); 0062 0063 PlainTextDecoder decoder; 0064 decoder.setRecordLinePositions(true); 0065 0066 // setup first and last lines depending on search direction 0067 int line = startLine; 0068 0069 // read through and search history in blocks of 10K lines. 0070 // this balances the need to retrieve lots of data from the history each time 0071 //(for efficient searching) 0072 // without using silly amounts of memory if the history is very large. 0073 const int maxDelta = qMin(window->lineCount(), 10000); 0074 int delta = forwards ? maxDelta : -maxDelta; 0075 0076 int endLine = line; 0077 bool hasWrapped = false; // set to true when we reach the top/bottom 0078 // of the output and continue from the other 0079 // end 0080 0081 // loop through history in blocks of <delta> lines. 0082 do { 0083 // ensure that application does not appear to hang 0084 // if searching through a lengthy output 0085 QApplication::processEvents(); 0086 0087 // calculate lines to search in this iteration 0088 if (hasWrapped) { 0089 if (endLine == lastLine) { 0090 line = 0; 0091 } else if (endLine == 0) { 0092 line = lastLine; 0093 } 0094 0095 endLine += delta; 0096 0097 if (forwards) { 0098 endLine = qMin(startLine, endLine); 0099 } else { 0100 endLine = qMax(startLine, endLine); 0101 } 0102 } else { 0103 endLine += delta; 0104 0105 if (endLine > lastLine) { 0106 hasWrapped = true; 0107 endLine = lastLine; 0108 } else if (endLine < 0) { 0109 hasWrapped = true; 0110 endLine = 0; 0111 } 0112 } 0113 0114 decoder.begin(&searchStream); 0115 emulation->writeToStream(&decoder, qMin(endLine, line), qMax(endLine, line)); 0116 decoder.end(); 0117 0118 // line number search below assumes that the buffer ends with a new-line 0119 string.append(QLatin1Char('\n')); 0120 0121 if (forwards) { 0122 pos = string.indexOf(_regExp); 0123 } else { 0124 pos = string.lastIndexOf(_regExp); 0125 } 0126 0127 // if a match is found, position the cursor on that line and update the screen 0128 if (pos != -1) { 0129 int newLines = 0; 0130 QList<int> linePositions = decoder.linePositions(); 0131 while (newLines < linePositions.count() && linePositions[newLines] <= pos) { 0132 newLines++; 0133 } 0134 0135 // ignore the new line at the start of the buffer 0136 newLines--; 0137 0138 int findPos = qMin(line, endLine) + newLines; 0139 0140 highlightResult(window, findPos); 0141 0142 Q_EMIT completed(true); 0143 0144 return; 0145 } 0146 0147 // clear the current block of text and move to the next one 0148 string.clear(); 0149 line = endLine; 0150 } while (startLine != endLine); 0151 if (!session->getSelectMode()) { 0152 // if no match was found, clear selection to indicate this, 0153 window->clearSelection(); 0154 window->notifyOutputChanged(); 0155 } 0156 } 0157 0158 Q_EMIT completed(false); 0159 } 0160 void SearchHistoryTask::highlightResult(const ScreenWindowPtr &window, int findPos) 0161 { 0162 // work out how many lines into the current block of text the search result was found 0163 //- looks a little painful, but it only has to be done once per search. 0164 0165 ////qDebug() << "Found result at line " << findPos; 0166 0167 // update display to show area of history containing selection 0168 if ((findPos < window->currentLine()) || (findPos >= (window->currentLine() + window->windowLines()))) { 0169 int centeredScrollPos = findPos - window->windowLines() / 2; 0170 if (centeredScrollPos < 0) { 0171 centeredScrollPos = 0; 0172 } 0173 0174 window->scrollTo(centeredScrollPos); 0175 } 0176 0177 window->setTrackOutput(false); 0178 window->notifyOutputChanged(); 0179 window->setCurrentResultLine(findPos); 0180 } 0181 0182 SearchHistoryTask::SearchHistoryTask(QObject *parent) 0183 : SessionTask(parent) 0184 , _direction(Enum::BackwardsSearch) 0185 , _startLine(0) 0186 { 0187 } 0188 0189 void SearchHistoryTask::setSearchDirection(Enum::SearchDirection direction) 0190 { 0191 _direction = direction; 0192 } 0193 0194 void SearchHistoryTask::setStartLine(int line) 0195 { 0196 _startLine = line; 0197 } 0198 0199 Enum::SearchDirection SearchHistoryTask::searchDirection() const 0200 { 0201 return _direction; 0202 } 0203 0204 void SearchHistoryTask::setRegExp(const QRegularExpression &expression) 0205 { 0206 _regExp = expression; 0207 } 0208 0209 QRegularExpression SearchHistoryTask::regExp() const 0210 { 0211 return _regExp; 0212 } 0213 0214 } 0215 0216 #include "moc_SearchHistoryTask.cpp"