File indexing completed on 2024-05-19 05:28:16

0001 /*
0002     SPDX-FileCopyrightText: 2013 Christian Surlykke
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 
0006     This program is distributed in the hope that it will be useful,
0007     but WITHOUT ANY WARRANTY; without even the implied warranty of
0008     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0009     GNU General Public License for more details.
0010 
0011     You should have received a copy of the GNU General Public License
0012     along with this program; if not, write to the Free Software
0013     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0014     02110-1301  USA.
0015 */
0016 #include <QApplication>
0017 #include <QDebug>
0018 #include <QRegExp>
0019 #include <QTextStream>
0020 
0021 #include "Emulation.h"
0022 #include "HistorySearch.h"
0023 #include "TerminalCharacterDecoder.h"
0024 
0025 HistorySearch::HistorySearch(EmulationPtr emulation, const QRegExp &regExp, bool forwards, int startColumn, int startLine, QObject *parent)
0026     : QObject(parent)
0027     , m_emulation(emulation)
0028     , m_regExp(regExp)
0029     , m_forwards(forwards)
0030     , m_startColumn(startColumn)
0031     , m_startLine(startLine)
0032 {
0033 }
0034 
0035 HistorySearch::~HistorySearch()
0036 {
0037 }
0038 
0039 void HistorySearch::search()
0040 {
0041     bool found = false;
0042 
0043     if (!m_regExp.isEmpty()) {
0044         if (m_forwards) {
0045             found = search(m_startColumn, m_startLine, -1, m_emulation->lineCount()) || search(0, 0, m_startColumn, m_startLine);
0046         } else {
0047             found = search(0, 0, m_startColumn, m_startLine) || search(m_startColumn, m_startLine, -1, m_emulation->lineCount());
0048         }
0049 
0050         if (found) {
0051             Q_EMIT matchFound(m_foundStartColumn, m_foundStartLine, m_foundEndColumn, m_foundEndLine);
0052         } else {
0053             Q_EMIT noMatchFound();
0054         }
0055     }
0056 
0057     deleteLater();
0058 }
0059 
0060 bool HistorySearch::search(int startColumn, int startLine, int endColumn, int endLine)
0061 {
0062     qDebug() << "search from" << startColumn << "," << startLine << "to" << endColumn << "," << endLine;
0063 
0064     int linesRead = 0;
0065     int linesToRead = endLine - startLine + 1;
0066 
0067     qDebug() << "linesToRead:" << linesToRead;
0068 
0069     // We read process history from (and including) startLine to (and including) endLine in
0070     // blocks of at most 10K lines so that we do not use unhealthy amounts of memory
0071     int blockSize;
0072     while ((blockSize = qMin(10000, linesToRead - linesRead)) > 0) {
0073         QString string;
0074         QTextStream searchStream(&string);
0075         PlainTextDecoder decoder;
0076         decoder.begin(&searchStream);
0077         decoder.setRecordLinePositions(true);
0078 
0079         // Calculate lines to read and read them
0080         int blockStartLine = m_forwards ? startLine + linesRead : endLine - linesRead - blockSize + 1;
0081         int chunkEndLine = blockStartLine + blockSize - 1;
0082         m_emulation->writeToStream(&decoder, blockStartLine, chunkEndLine);
0083 
0084         // We search between startColumn in the first line of the string and endColumn in the last
0085         // line of the string. First we calculate the position (in the string) of endColumn in the
0086         // last line of the string
0087         int endPosition;
0088 
0089         // The String that Emulator.writeToStream produces has a newline at the end, and so ends with an
0090         // empty line - we ignore that.
0091         int numberOfLinesInString = decoder.linePositions().size() - 1;
0092         if (numberOfLinesInString > 0 && endColumn > -1) {
0093             endPosition = decoder.linePositions().at(numberOfLinesInString - 1) + endColumn;
0094         } else {
0095             endPosition = string.size();
0096         }
0097 
0098         // So now we can log for m_regExp in the string between startColumn and endPosition
0099         int matchStart;
0100         if (m_forwards) {
0101             matchStart = m_regExp.indexIn(string, startColumn);
0102             if (matchStart >= endPosition)
0103                 matchStart = -1;
0104         } else {
0105             matchStart = m_regExp.lastIndexIn(string, endPosition - 1);
0106             if (matchStart < startColumn)
0107                 matchStart = -1;
0108         }
0109 
0110         if (matchStart > -1) {
0111             int matchEnd = matchStart + m_regExp.matchedLength() - 1;
0112             qDebug() << "Found in string from" << matchStart << "to" << matchEnd;
0113 
0114             // Translate startPos and endPos to startColum, startLine, endColumn and endLine in history.
0115             int startLineNumberInString = findLineNumberInString(decoder.linePositions(), matchStart);
0116             m_foundStartColumn = matchStart - decoder.linePositions().at(startLineNumberInString);
0117             m_foundStartLine = startLineNumberInString + startLine + linesRead;
0118 
0119             int endLineNumberInString = findLineNumberInString(decoder.linePositions(), matchEnd);
0120             m_foundEndColumn = matchEnd - decoder.linePositions().at(endLineNumberInString);
0121             m_foundEndLine = endLineNumberInString + startLine + linesRead;
0122 
0123             qDebug() << "m_foundStartColumn" << m_foundStartColumn << "m_foundStartLine" << m_foundEndLine << "m_foundEndColumn" << m_foundEndColumn
0124                      << "m_foundEndLine" << m_foundEndLine;
0125 
0126             return true;
0127         }
0128 
0129         linesRead += blockSize;
0130     }
0131 
0132     qDebug() << "Not found";
0133     return false;
0134 }
0135 
0136 int HistorySearch::findLineNumberInString(QList<int> linePositions, int position)
0137 {
0138     int lineNum = 0;
0139     while (lineNum + 1 < linePositions.size() && linePositions[lineNum + 1] <= position)
0140         lineNum++;
0141 
0142     return lineNum;
0143 }