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"