FileSearch: Remove duplicated code between regexp and non-regexp search

Only the way individual files are searched is different, so the rest of
the code is now shared.

Change-Id: Ia550de1b5d381c5f7c26c3f3e7a943fea7d0efa9
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
This commit is contained in:
Eike Ziller
2015-03-23 10:56:39 +01:00
parent fb9cea18a8
commit 9b49152fec

View File

@@ -37,6 +37,8 @@
#include "runextensions.h" #include "runextensions.h"
#include <functional>
using namespace Utils; using namespace Utils;
static inline QString msgCanceled(const QString &searchTerm, int numMatches, int numFilesSearched) static inline QString msgCanceled(const QString &searchTerm, int numMatches, int numFilesSearched)
@@ -64,56 +66,57 @@ QString clippedText(const QString &text, int maxLength)
return text; return text;
} }
void runFileSearch(QFutureInterface<FileSearchResultList> &future, class FileSearch : public std::binary_function<QString, QTextStream, FileSearchResultList>
QString searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
QMap<QString, QString> fileToContentsMap)
{ {
int numFilesSearched = 0; public:
int numMatches = 0; FileSearch(const QString &searchTerm, QTextDocument::FindFlags flags,
future.setProgressRange(0, files->maxProgress()); QFutureInterface<FileSearchResultList> *futureInterface);
future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches, numFilesSearched)); const FileSearchResultList operator()(const QString &filePath, QTextStream &stream) const;
const bool caseInsensitive = !(flags & QTextDocument::FindCaseSensitively); private:
const bool wholeWord = (flags & QTextDocument::FindWholeWords); QFutureInterface<FileSearchResultList> *future;
QString searchTermLower;
QString searchTermUpper;
int termMaxIndex;
const QChar *termData;
const QChar *termDataLower;
const QChar *termDataUpper;
bool caseSensitive;
bool wholeWord;
};
const QString searchTermLower = searchTerm.toLower(); class FileSearchRegExp : public std::binary_function<QString, QTextStream, FileSearchResultList>
const QString searchTermUpper = searchTerm.toUpper(); {
public:
FileSearchRegExp(const QString &searchTerm, QTextDocument::FindFlags flags,
QFutureInterface<FileSearchResultList> *futureInterface);
const FileSearchResultList operator()(const QString &filePath, QTextStream &stream) const;
const int termLength = searchTerm.length(); private:
const int termMaxIndex = termLength - 1; QFutureInterface<FileSearchResultList> *future;
const QChar *termData = searchTerm.constData(); QRegExp expression;
const QChar *termDataLower = searchTermLower.constData(); };
const QChar *termDataUpper = searchTermUpper.constData();
QFile file; FileSearch::FileSearch(const QString &searchTerm, QTextDocument::FindFlags flags,
QString str; QFutureInterface<FileSearchResultList> *futureInterface)
QTextStream stream; {
FileSearchResultList results; caseSensitive = (flags & QTextDocument::FindCaseSensitively);
while (files->hasNext()) { wholeWord = (flags & QTextDocument::FindWholeWords);
const QString &s = files->next(); future = futureInterface;
if (future.isPaused()) searchTermLower = searchTerm.toLower();
future.waitForResume(); searchTermUpper = searchTerm.toUpper();
if (future.isCanceled()) { termMaxIndex = searchTerm.length() - 1;
future.setProgressValueAndText(files->currentProgress(), msgCanceled(searchTerm, numMatches, numFilesSearched)); termData = searchTerm.constData();
break; termDataLower = searchTermLower.constData();
} termDataUpper = searchTermUpper.constData();
bool needsToCloseFile = false;
if (fileToContentsMap.contains(s)) {
str = fileToContentsMap.value(s);
stream.setString(&str);
} else {
file.setFileName(s);
if (!file.open(QIODevice::ReadOnly))
continue;
needsToCloseFile = true;
stream.setDevice(&file);
stream.setCodec(files->encoding());
} }
const FileSearchResultList FileSearch::operator()(const QString &filePath,
QTextStream &stream) const
{
int lineNr = 0; int lineNr = 0;
FileSearchResultList results;
while (!stream.atEnd()) { while (!stream.atEnd()) {
++lineNr; ++lineNr;
const QString chunk = stream.readLine(); const QString chunk = stream.readLine();
@@ -121,55 +124,155 @@ void runFileSearch(QFutureInterface<FileSearchResultList> &future,
int chunkLength = chunk.length(); int chunkLength = chunk.length();
const QChar *chunkPtr = chunk.constData(); const QChar *chunkPtr = chunk.constData();
const QChar *chunkEnd = chunkPtr + chunkLength - 1; const QChar *chunkEnd = chunkPtr + chunkLength - 1;
for (const QChar *regionPtr = chunkPtr; for (const QChar *regionPtr = chunkPtr; regionPtr + termMaxIndex <= chunkEnd; ++regionPtr) {
regionPtr + termMaxIndex <= chunkEnd;
++regionPtr) {
const QChar *regionEnd = regionPtr + termMaxIndex; const QChar *regionEnd = regionPtr + termMaxIndex;
if ( /* optimization check for start and end of region */ if ( /* optimization check for start and end of region */
// case sensitive // case sensitive
(!caseInsensitive && *regionPtr == termData[0] && *regionEnd == termData[termMaxIndex]) (caseSensitive && *regionPtr == termData[0]
&& *regionEnd == termData[termMaxIndex])
|| ||
// case insensitive // case insensitive
(caseInsensitive && (*regionPtr == termDataLower[0] || *regionPtr == termDataUpper[0]) (!caseSensitive && (*regionPtr == termDataLower[0]
&& (*regionEnd == termDataLower[termMaxIndex] || *regionEnd == termDataUpper[termMaxIndex])) || *regionPtr == termDataUpper[0])
&& (*regionEnd == termDataLower[termMaxIndex]
|| *regionEnd == termDataUpper[termMaxIndex]))
) { ) {
bool equal = true; bool equal = true;
// whole word check // whole word check
const QChar *beforeRegion = regionPtr - 1; const QChar *beforeRegion = regionPtr - 1;
const QChar *afterRegion = regionEnd + 1; const QChar *afterRegion = regionEnd + 1;
if (wholeWord && ( if (wholeWord
((beforeRegion >= chunkPtr) && (beforeRegion->isLetterOrNumber() || ((*beforeRegion) == QLatin1Char('_')))) || && (((beforeRegion >= chunkPtr)
((afterRegion <= chunkEnd) && (afterRegion->isLetterOrNumber() || ((*afterRegion) == QLatin1Char('_')))) && (beforeRegion->isLetterOrNumber()
|| ((*beforeRegion) == QLatin1Char('_'))))
||
((afterRegion <= chunkEnd)
&& (afterRegion->isLetterOrNumber()
|| ((*afterRegion) == QLatin1Char('_'))))
)) { )) {
equal = false; equal = false;
} } else {
if (equal) {
// check all chars // check all chars
int regionIndex = 1; int regionIndex = 1;
for (const QChar *regionCursor = regionPtr + 1; regionCursor < regionEnd; ++regionCursor, ++regionIndex) { for (const QChar *regionCursor = regionPtr + 1;
regionCursor < regionEnd;
++regionCursor, ++regionIndex) {
if ( // case sensitive if ( // case sensitive
(!caseInsensitive && *regionCursor != termData[regionIndex]) (caseSensitive
&& *regionCursor != termData[regionIndex])
|| ||
// case insensitive // case insensitive
(caseInsensitive && *regionCursor != termData[regionIndex] (!caseSensitive
&& *regionCursor != termDataLower[regionIndex] && *regionCursor != termDataUpper[regionIndex]) && *regionCursor != termDataLower[regionIndex]
&& *regionCursor != termDataUpper[regionIndex])
) { ) {
equal = false; equal = false;
} }
} }
} }
if (equal) { if (equal) {
results << FileSearchResult(s, lineNr, resultItemText, results << FileSearchResult(filePath, lineNr, resultItemText,
regionPtr - chunkPtr, termLength, regionPtr - chunkPtr, termMaxIndex + 1,
QStringList()); QStringList());
regionPtr += termLength - 1; // another +1 done by for-loop regionPtr += termMaxIndex; // another +1 done by for-loop
++numMatches;
} }
} }
} }
if (future->isPaused())
future->waitForResume();
if (future->isCanceled())
break;
} }
return results;
}
FileSearchRegExp::FileSearchRegExp(const QString &searchTerm, QTextDocument::FindFlags flags,
QFutureInterface<FileSearchResultList> *futureInterface)
{
future = futureInterface;
QString term = searchTerm;
if (flags & QTextDocument::FindWholeWords)
term = QString::fromLatin1("\\b%1\\b").arg(term);
const Qt::CaseSensitivity caseSensitivity = (flags & QTextDocument::FindCaseSensitively)
? Qt::CaseSensitive : Qt::CaseInsensitive;
expression = QRegExp(term, caseSensitivity);
}
const FileSearchResultList FileSearchRegExp::operator()(const QString &filePath,
QTextStream &stream) const
{
int lineNr = 0;
FileSearchResultList results;
QString line;
while (!stream.atEnd()) {
line = stream.readLine();
const QString resultItemText = clippedText(line, MAX_LINE_SIZE);
int lengthOfLine = line.size();
int pos = 0;
while ((pos = expression.indexIn(line, pos)) != -1) {
results << FileSearchResult(filePath, lineNr, resultItemText,
pos, expression.matchedLength(),
expression.capturedTexts());
if (expression.matchedLength() == 0)
break;
pos += expression.matchedLength();
if (pos >= lengthOfLine)
break;
}
++lineNr;
if (future->isPaused())
future->waitForResume();
if (future->isCanceled())
break;
}
return results;
}
void runFileSearch(QFutureInterface<FileSearchResultList> &future,
QString searchTerm,
FileIterator *files,
QMap<QString, QString> fileToContentsMap,
const std::function<FileSearchResultList(QString, QTextStream&)> &searchFunction)
{
int numFilesSearched = 0;
int numMatches = 0;
future.setProgressRange(0, files->maxProgress());
future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches,
numFilesSearched));
QFile file;
QString str;
QTextStream stream;
FileSearchResultList results;
while (files->hasNext()) {
const QString &filePath = files->next();
if (future.isPaused())
future.waitForResume();
if (future.isCanceled()) {
future.setProgressValueAndText(files->currentProgress(), msgCanceled(searchTerm,
numMatches,
numFilesSearched));
break;
}
bool needsToCloseFile = false;
if (fileToContentsMap.contains(filePath)) {
str = fileToContentsMap.value(filePath);
stream.setString(&str);
} else {
file.setFileName(filePath);
if (!file.open(QIODevice::ReadOnly))
continue;
needsToCloseFile = true;
stream.setDevice(&file);
stream.setCodec(files->encoding());
}
const FileSearchResultList singleFileResults = searchFunction(filePath, stream);
numMatches += singleFileResults.size();
results << singleFileResults;
++numFilesSearched; ++numFilesSearched;
if (future.isProgressUpdateNeeded() if (future.isProgressUpdateNeeded()
@@ -179,7 +282,9 @@ void runFileSearch(QFutureInterface<FileSearchResultList> &future,
results.clear(); results.clear();
} }
future.setProgressRange(0, files->maxProgress()); future.setProgressRange(0, files->maxProgress());
future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches, numFilesSearched)); future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm,
numMatches,
numFilesSearched));
} }
// clean up // clean up
@@ -192,94 +297,32 @@ void runFileSearch(QFutureInterface<FileSearchResultList> &future,
results.clear(); results.clear();
} }
if (!future.isCanceled()) if (!future.isCanceled())
future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches, numFilesSearched)); future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches,
numFilesSearched));
delete files; delete files;
if (future.isPaused()) if (future.isPaused())
future.waitForResume(); future.waitForResume();
} }
void runFileSearch(QFutureInterface<FileSearchResultList> &future,
QString searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
QMap<QString, QString> fileToContentsMap)
{
runFileSearch(future, searchTerm, files, fileToContentsMap,
FileSearch(searchTerm, flags, &future));
}
void runFileSearchRegExp(QFutureInterface<FileSearchResultList> &future, void runFileSearchRegExp(QFutureInterface<FileSearchResultList> &future,
QString searchTerm, QString searchTerm,
FileIterator *files, FileIterator *files,
QTextDocument::FindFlags flags, QTextDocument::FindFlags flags,
QMap<QString, QString> fileToContentsMap) QMap<QString, QString> fileToContentsMap)
{ {
int numFilesSearched = 0; runFileSearch(future, searchTerm, files, fileToContentsMap,
int numMatches = 0; FileSearchRegExp(searchTerm, flags, &future));
future.setProgressRange(0, files->maxProgress());
future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches, numFilesSearched));
if (flags & QTextDocument::FindWholeWords)
searchTerm = QString::fromLatin1("\\b%1\\b").arg(searchTerm);
const Qt::CaseSensitivity caseSensitivity = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive;
QRegExp expression(searchTerm, caseSensitivity);
QFile file;
QString str;
QTextStream stream;
FileSearchResultList results;
while (files->hasNext()) {
const QString &s = files->next();
if (future.isPaused())
future.waitForResume();
if (future.isCanceled()) {
future.setProgressValueAndText(files->currentProgress(), msgCanceled(searchTerm, numMatches, numFilesSearched));
break;
}
bool needsToCloseFile = false;
if (fileToContentsMap.contains(s)) {
str = fileToContentsMap.value(s);
stream.setString(&str);
} else {
file.setFileName(s);
if (!file.open(QIODevice::ReadOnly))
continue;
needsToCloseFile = true;
stream.setDevice(&file);
stream.setCodec(files->encoding());
}
int lineNr = 1;
QString line;
while (!stream.atEnd()) {
line = stream.readLine();
const QString resultItemText = clippedText(line, MAX_LINE_SIZE);
int lengthOfLine = line.size();
int pos = 0;
while ((pos = expression.indexIn(line, pos)) != -1) {
results << FileSearchResult(s, lineNr, resultItemText,
pos, expression.matchedLength(),
expression.capturedTexts());
++numMatches;
if (expression.matchedLength() == 0)
break;
pos += expression.matchedLength();
if (pos >= lengthOfLine)
break;
}
++lineNr;
}
++numFilesSearched;
if (future.isProgressUpdateNeeded()
|| future.progressValue() == 0 /*workaround for regression in Qt*/) {
if (!results.isEmpty()) {
future.reportResult(results);
results.clear();
}
future.setProgressRange(0, files->maxProgress());
future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches, numFilesSearched));
}
if (needsToCloseFile)
file.close();
}
if (!results.isEmpty()) {
future.reportResult(results);
results.clear();
}
if (!future.isCanceled())
future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches, numFilesSearched));
delete files;
if (future.isPaused())
future.waitForResume();
} }
} // namespace } // namespace