FileSearch: Get rid of FileSearchResult

Use SearchResultItem instead.
This change should reduce the remaining freeze described in
a9eb732ce6 even more.

Change-Id: I102b82ed5677360ccd9e425dd0bdd941d87116f0
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Jarek Kobus
2023-05-05 14:05:10 +02:00
parent 1d4228dfda
commit 936086745a
18 changed files with 289 additions and 294 deletions

View File

@@ -7,6 +7,7 @@
#include "filepath.h"
#include "mapreduce.h"
#include "qtcassert.h"
#include "searchresultitem.h"
#include "stringutils.h"
#include "utilstr.h"
#include "utiltypes.h"
@@ -69,7 +70,7 @@ public:
FileSearch(const QString &searchTerm,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap);
void operator()(QFutureInterface<FileSearchResultList> &futureInterface,
void operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const;
private:
@@ -91,7 +92,7 @@ public:
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap);
FileSearchRegExp(const FileSearchRegExp &other);
void operator()(QFutureInterface<FileSearchResultList> &futureInterface,
void operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const;
private:
@@ -117,7 +118,7 @@ FileSearch::FileSearch(const QString &searchTerm,
termDataUpper = searchTermUpper.constData();
}
void FileSearch::operator()(QFutureInterface<FileSearchResultList> &futureInterface,
void FileSearch::operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const
{
if (futureInterface.isCanceled())
@@ -125,7 +126,7 @@ void FileSearch::operator()(QFutureInterface<FileSearchResultList> &futureInterf
qCDebug(searchLog) << "Searching in" << item.filePath;
futureInterface.setProgressRange(0, 1);
futureInterface.setProgressValue(0);
FileSearchResultList results;
SearchResultItems results;
QString tempString;
if (!getFileContent(item.filePath, item.encoding, &tempString, fileToContentsMap)) {
qCDebug(searchLog) << "- failed to get content for" << item.filePath;
@@ -190,13 +191,13 @@ void FileSearch::operator()(QFutureInterface<FileSearchResultList> &futureInterf
}
}
if (equal) {
const QString resultItemText = clippedText(chunk, MAX_LINE_SIZE);
results << FileSearchResult(item.filePath,
lineNr,
resultItemText,
regionPtr - chunkPtr,
termMaxIndex + 1,
QStringList());
SearchResultItem result;
result.setFilePath(item.filePath);
result.setMainRange(lineNr, regionPtr - chunkPtr, termMaxIndex + 1);
result.setDisplayText(clippedText(chunk, MAX_LINE_SIZE));
result.setUserData(QStringList());
result.setUseTextEditorFont(true);
results << result;
regionPtr += termMaxIndex; // another +1 done by for-loop
}
}
@@ -238,7 +239,7 @@ QRegularExpressionMatch FileSearchRegExp::doGuardedMatch(const QString &line, in
return expression.match(line, offset);
}
void FileSearchRegExp::operator()(QFutureInterface<FileSearchResultList> &futureInterface,
void FileSearchRegExp::operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const
{
if (!expression.isValid()) {
@@ -250,7 +251,7 @@ void FileSearchRegExp::operator()(QFutureInterface<FileSearchResultList> &future
qCDebug(searchLog) << "Searching in" << item.filePath;
futureInterface.setProgressRange(0, 1);
futureInterface.setProgressValue(0);
FileSearchResultList results;
SearchResultItems results;
QString tempString;
if (!getFileContent(item.filePath, item.encoding, &tempString, fileToContentsMap)) {
qCDebug(searchLog) << "- failed to get content for" << item.filePath;
@@ -270,12 +271,13 @@ void FileSearchRegExp::operator()(QFutureInterface<FileSearchResultList> &future
int pos = 0;
while ((match = doGuardedMatch(line, pos)).hasMatch()) {
pos = match.capturedStart();
results << FileSearchResult(item.filePath,
lineNr,
resultItemText,
pos,
match.capturedLength(),
match.capturedTexts());
SearchResultItem result;
result.setFilePath(item.filePath);
result.setMainRange(lineNr, pos, match.capturedLength());
result.setDisplayText(resultItemText);
result.setUserData(match.capturedTexts());
result.setUseTextEditorFont(true);
results << result;
if (match.capturedLength() == 0)
break;
pos += match.capturedLength();
@@ -299,12 +301,12 @@ struct SearchState
SearchState(const QString &term, FileIterator *iterator) : searchTerm(term), files(iterator) {}
QString searchTerm;
FileIterator *files = nullptr;
FileSearchResultList cachedResults;
SearchResultItems cachedResults;
int numFilesSearched = 0;
int numMatches = 0;
};
SearchState initFileSearch(QFutureInterface<FileSearchResultList> &futureInterface,
SearchState initFileSearch(QFutureInterface<SearchResultItems> &futureInterface,
const QString &searchTerm, FileIterator *files)
{
futureInterface.setProgressRange(0, files->maxProgress());
@@ -312,9 +314,9 @@ SearchState initFileSearch(QFutureInterface<FileSearchResultList> &futureInterfa
return SearchState(searchTerm, files);
}
void collectSearchResults(QFutureInterface<FileSearchResultList> &futureInterface,
void collectSearchResults(QFutureInterface<SearchResultItems> &futureInterface,
SearchState &state,
const FileSearchResultList &results)
const SearchResultItems &results)
{
state.numMatches += results.size();
state.cachedResults << results;
@@ -333,7 +335,7 @@ void collectSearchResults(QFutureInterface<FileSearchResultList> &futureInterfac
}
}
void cleanUpFileSearch(QFutureInterface<FileSearchResultList> &futureInterface,
void cleanUpFileSearch(QFutureInterface<SearchResultItems> &futureInterface,
SearchState &state)
{
if (!state.cachedResults.isEmpty()) {
@@ -356,13 +358,13 @@ void cleanUpFileSearch(QFutureInterface<FileSearchResultList> &futureInterface,
} // namespace
QFuture<FileSearchResultList> Utils::findInFiles(const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap)
QFuture<SearchResultItems> Utils::findInFiles(const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap)
{
return mapReduce(files->begin(), files->end(),
[searchTerm, files](QFutureInterface<FileSearchResultList> &futureInterface) {
[searchTerm, files](QFutureInterface<SearchResultItems> &futureInterface) {
return initFileSearch(futureInterface, searchTerm, files);
},
FileSearch(searchTerm, flags, fileToContentsMap),
@@ -370,14 +372,14 @@ QFuture<FileSearchResultList> Utils::findInFiles(const QString &searchTerm,
&cleanUpFileSearch);
}
QFuture<FileSearchResultList> Utils::findInFilesRegExp(
QFuture<SearchResultItems> Utils::findInFilesRegExp(
const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap)
{
return mapReduce(files->begin(), files->end(),
[searchTerm, files](QFutureInterface<FileSearchResultList> &futureInterface) {
[searchTerm, files](QFutureInterface<SearchResultItems> &futureInterface) {
return initFileSearch(futureInterface, searchTerm, files);
},
FileSearchRegExp(searchTerm, flags, fileToContentsMap),

View File

@@ -6,8 +6,8 @@
#include "utils_global.h"
#include "filepath.h"
#include "searchresultitem.h"
#include <QDir>
#include <QMap>
#include <QSet>
#include <QStack>
@@ -152,54 +152,20 @@ private:
QList<Item *> m_items;
};
class QTCREATOR_UTILS_EXPORT FileSearchResult
{
public:
FileSearchResult() = default;
FileSearchResult(const FilePath &fileName, int lineNumber, const QString &matchingLine,
int matchStart, int matchLength,
const QStringList &regexpCapturedTexts)
: fileName(fileName),
lineNumber(lineNumber),
matchingLine(matchingLine),
matchStart(matchStart),
matchLength(matchLength),
regexpCapturedTexts(regexpCapturedTexts)
{}
QTCREATOR_UTILS_EXPORT QFuture<SearchResultItems> findInFiles(const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap = {});
bool operator==(const FileSearchResult &o) const
{
return fileName == o.fileName && lineNumber == o.lineNumber
&& matchingLine == o.matchingLine && matchStart == o.matchStart
&& matchLength == o.matchLength && regexpCapturedTexts == o.regexpCapturedTexts;
}
bool operator!=(const FileSearchResult &o) const { return !(*this == o); }
FilePath fileName;
int lineNumber;
QString matchingLine;
int matchStart;
int matchLength;
QStringList regexpCapturedTexts;
};
using FileSearchResultList = QList<FileSearchResult>;
QTCREATOR_UTILS_EXPORT QFuture<FileSearchResultList> findInFiles(
QTCREATOR_UTILS_EXPORT QFuture<SearchResultItems> findInFilesRegExp(
const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap = QMap<FilePath, QString>());
const QMap<FilePath, QString> &fileToContentsMap = {});
QTCREATOR_UTILS_EXPORT QFuture<FileSearchResultList> findInFilesRegExp(
const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap = QMap<FilePath, QString>());
QTCREATOR_UTILS_EXPORT QString expandRegExpReplacement(const QString &replaceText, const QStringList &capturedTexts);
QTCREATOR_UTILS_EXPORT QString matchCaseReplacement(const QString &originalText, const QString &replaceText);
QTCREATOR_UTILS_EXPORT QString expandRegExpReplacement(const QString &replaceText,
const QStringList &capturedTexts);
QTCREATOR_UTILS_EXPORT QString matchCaseReplacement(const QString &originalText,
const QString &replaceText);
} // namespace Utils
Q_DECLARE_METATYPE(Utils::FileSearchResultList)

View File

@@ -45,10 +45,20 @@ SearchResultColor::SearchResultColor(const QColor &textBg, const QColor &textFg,
containingFunctionForeground = textForeground;
}
QTCREATOR_UTILS_EXPORT size_t qHash(SearchResultColor::Style style, uint seed)
static QString displayText(const QString &line)
{
int a = int(style);
return ::qHash(a, seed);
QString result = line;
auto end = result.end();
for (auto it = result.begin(); it != end; ++it) {
if (!it->isSpace() && !it->isPrint())
*it = QChar('?');
}
return result;
}
void SearchResultItem::setDisplayText(const QString &text)
{
setLineText(displayText(text));
}
void SearchResultItem::setMainRange(int line, int column, int length)
@@ -56,4 +66,29 @@ void SearchResultItem::setMainRange(int line, int column, int length)
m_mainRange = {{line, column}, {line, column + length}};
}
QTCREATOR_UTILS_EXPORT size_t qHash(SearchResultColor::Style style, uint seed)
{
int a = int(style);
return ::qHash(a, seed);
}
bool Search::TextPosition::operator==(const Search::TextPosition &other) const
{
return line == other.line && column == other.column;
}
bool Search::TextRange::operator==(const Search::TextRange &other) const
{
return begin == other.begin && end == other.end;
}
bool SearchResultItem::operator==(const SearchResultItem &other) const
{
return m_path == other.m_path && m_lineText == other.m_lineText
&& m_userData == other.m_userData && m_mainRange == other.m_mainRange
&& m_useTextEditorFont == other.m_useTextEditorFont
&& m_selectForReplacement == other.m_selectForReplacement && m_style == other.m_style
&& m_containingFunctionName == other.m_containingFunctionName;
}
} // namespace Utils

View File

@@ -28,6 +28,8 @@ public:
bool operator<(const TextPosition &other) const
{ return line < other.line || (line == other.line && column < other.column); }
bool operator==(const TextPosition &other) const;
bool operator!=(const TextPosition &other) const { return !(operator==(other)); }
};
class QTCREATOR_UTILS_EXPORT TextRange
@@ -40,6 +42,8 @@ public:
TextPosition end;
bool operator<(const TextRange &other) const { return begin < other.begin; }
bool operator==(const TextRange &other) const;
bool operator!=(const TextRange &other) const { return !(operator==(other)); }
};
} // namespace Search
@@ -76,6 +80,7 @@ public:
QString lineText() const { return m_lineText; }
void setLineText(const QString &text) { m_lineText = text; }
void setDisplayText(const QString &text);
QIcon icon() const { return m_icon; }
void setIcon(const QIcon &icon) { m_icon = icon; }
@@ -103,6 +108,9 @@ public:
m_containingFunctionName = containingFunctionName;
}
bool operator==(const SearchResultItem &other) const;
bool operator!=(const SearchResultItem &other) const { return !(operator==(other)); }
private:
QStringList m_path; // hierarchy to the parent item of this item
QString m_lineText; // text to show for the item itself

View File

@@ -45,7 +45,7 @@ public:
class GitGrepRunner
{
using PromiseType = QPromise<FileSearchResultList>;
using PromiseType = QPromise<SearchResultItems>;
public:
GitGrepRunner(const TextEditor::FileFindParameters &parameters)
@@ -67,23 +67,23 @@ public:
QStringList regexpCapturedTexts;
};
void processLine(const QString &line, FileSearchResultList *resultList) const
void processLine(const QString &line, SearchResultItems *resultList) const
{
if (line.isEmpty())
return;
static const QLatin1String boldRed("\x1b[1;31m");
static const QLatin1String resetColor("\x1b[m");
FileSearchResult single;
SearchResultItem result;
const int lineSeparator = line.indexOf(QChar::Null);
QString filePath = line.left(lineSeparator);
if (!m_ref.isEmpty() && filePath.startsWith(m_ref))
filePath.remove(0, m_ref.length());
single.fileName = m_directory.pathAppended(filePath);
result.setFilePath(m_directory.pathAppended(filePath));
const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1);
single.lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
const int lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
QString text = line.mid(textSeparator + 1);
QRegularExpression regexp;
QVector<Match> matches;
QList<Match> matches;
if (m_parameters.flags & FindRegularExpression) {
const QRegularExpression::PatternOptions patternOptions =
(m_parameters.flags & FindCaseSensitively)
@@ -106,19 +106,18 @@ public:
matches.append(match);
text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size());
}
single.matchingLine = text;
result.setDisplayText(text);
for (const auto &match : std::as_const(matches)) {
single.matchStart = match.matchStart;
single.matchLength = match.matchLength;
single.regexpCapturedTexts = match.regexpCapturedTexts;
resultList->append(single);
result.setMainRange(lineNumber, match.matchStart, match.matchLength);
result.setUserData(match.regexpCapturedTexts);
resultList->append(result);
}
}
void read(PromiseType &fi, const QString &text)
{
FileSearchResultList resultList;
SearchResultItems resultList;
QString t = text;
QTextStream stream(&t);
while (!stream.atEnd() && !fi.isCanceled())
@@ -272,7 +271,7 @@ void GitGrep::writeSettings(QSettings *settings) const
settings->setValue(GitGrepRef, m_treeLineEdit->text());
}
QFuture<FileSearchResultList> GitGrep::executeSearch(const TextEditor::FileFindParameters &parameters,
QFuture<SearchResultItems> GitGrep::executeSearch(const TextEditor::FileFindParameters &parameters,
TextEditor::BaseFileFind * /*baseFileFind*/)
{
return Utils::asyncRun(GitGrepRunner(parameters));

View File

@@ -30,7 +30,7 @@ public:
QVariant parameters() const override;
void readSettings(QSettings *settings) override;
void writeSettings(QSettings *settings) const override;
QFuture<Utils::FileSearchResultList> executeSearch(
QFuture<Utils::SearchResultItems> executeSearch(
const TextEditor::FileFindParameters &parameters,
TextEditor::BaseFileFind *baseFileFind) override;
Core::IEditor *openEditor(const Utils::SearchResultItem &item,

View File

@@ -73,7 +73,7 @@ bool isSilverSearcherAvailable()
return false;
}
void runSilverSeacher(QPromise<FileSearchResultList> &promise, FileFindParameters parameters)
void runSilverSeacher(QPromise<SearchResultItems> &promise, FileFindParameters parameters)
{
const FilePath directory = FilePath::fromUserInput(parameters.additionalParameters.toString());
QStringList arguments = {"--parallel", "--ackmate"};
@@ -120,7 +120,7 @@ void runSilverSeacher(QPromise<FileSearchResultList> &promise, FileFindParameter
regexp.setPatternOptions(patternOptions);
}
SilverSearcher::SilverSearcherOutputParser parser(process.cleanedStdOut(), regexp);
FileSearchResultList items = parser.parse();
const SearchResultItems items = parser.parse();
if (!items.isEmpty())
promise.addResult(items);
} else {
@@ -189,7 +189,7 @@ void FindInFilesSilverSearcher::writeSettings(QSettings *settings) const
settings->setValue(SearchOptionsString, m_searchOptionsLineEdit->text());
}
QFuture<FileSearchResultList> FindInFilesSilverSearcher::executeSearch(
QFuture<SearchResultItems> FindInFilesSilverSearcher::executeSearch(
const FileFindParameters &parameters, BaseFileFind * /*baseFileFind*/)
{
return Utils::asyncRun(runSilverSeacher, parameters);

View File

@@ -31,7 +31,7 @@ public:
QVariant parameters() const override;
void readSettings(QSettings *settings) override;
void writeSettings(QSettings *settings) const override;
QFuture<Utils::FileSearchResultList> executeSearch(
QFuture<Utils::SearchResultItems> executeSearch(
const TextEditor::FileFindParameters &parameters, TextEditor::BaseFileFind *) override;
Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) override;

View File

@@ -11,47 +11,57 @@ using namespace Utils;
namespace SilverSearcher {
namespace Internal {
SearchResultItem searchResult(const FilePath &fileName, const QString &matchingLine,
int lineNumber, int matchStart, int matchLength)
{
SearchResultItem result;
result.setFilePath(fileName);
result.setLineText(matchingLine);
result.setMainRange(lineNumber, matchStart, matchLength);
return result;
}
void OutputParserTest::test_data()
{
QTest::addColumn<QString>("parserOutput");
QTest::addColumn<FileSearchResultList>("results");
QTest::addColumn<SearchResultItems>("results");
QTest::addRow("nothing") << QString("\n") << FileSearchResultList();
QTest::addRow("nothing") << QString("\n") << SearchResultItems();
QTest::addRow("oneFileOneMatch")
<< QString(":/file/path/to/filename.h\n"
"1;1 5:match\n")
<< FileSearchResultList({{"/file/path/to/filename.h", 1, "match", 1, 5, {}}});
<< QString(":/file/path/to/filename.h\n"
"1;1 5:match\n")
<< SearchResultItems{searchResult("/file/path/to/filename.h", "match", 1, 1, 5)};
QTest::addRow("multipleFilesWithOneMatch")
<< QString(":/file/path/to/filename1.h\n"
"1;1 5:match\n"
"\n"
":/file/path/to/filename2.h\n"
"2;2 5: match\n")
<< FileSearchResultList({{"/file/path/to/filename1.h", 1, "match", 1, 5, {}},
{"/file/path/to/filename2.h", 2, " match", 2, 5, {}}});
<< QString(":/file/path/to/filename1.h\n"
"1;1 5:match\n"
"\n"
":/file/path/to/filename2.h\n"
"2;2 5: match\n")
<< SearchResultItems{searchResult("/file/path/to/filename1.h", "match", 1, 1, 5),
searchResult("/file/path/to/filename2.h", " match", 2, 2, 5)};
QTest::addRow("oneFileMultipleMatches")
<< QString(":/file/path/to/filename.h\n"
"1;1 5,7 5:match match\n")
<< FileSearchResultList({{"/file/path/to/filename.h", 1, "match match", 1, 5, {}},
{"/file/path/to/filename.h", 1, "match match", 7, 5, {}}});
<< QString(":/file/path/to/filename.h\n"
"1;1 5,7 5:match match\n")
<< SearchResultItems{searchResult("/file/path/to/filename.h", "match match", 1, 1, 5),
searchResult("/file/path/to/filename.h", "match match", 1, 7, 5)};
QTest::addRow("multipleFilesWithMultipleMatches")
<< QString(":/file/path/to/filename1.h\n"
"1;1 5,7 5:match match\n"
"\n"
":/file/path/to/filename2.h\n"
"2;2 5,8 5: match match\n")
<< FileSearchResultList({{"/file/path/to/filename1.h", 1, "match match", 1, 5, {}},
{"/file/path/to/filename1.h", 1, "match match", 7, 5, {}},
{"/file/path/to/filename2.h", 2, " match match", 2, 5, {}},
{"/file/path/to/filename2.h", 2, " match match", 8, 5, {}}});
<< QString(":/file/path/to/filename1.h\n"
"1;1 5,7 5:match match\n"
"\n"
":/file/path/to/filename2.h\n"
"2;2 5,8 5: match match\n")
<< SearchResultItems{searchResult("/file/path/to/filename1.h", "match match", 1, 1, 5),
searchResult("/file/path/to/filename1.h", "match match", 1, 7, 5),
searchResult("/file/path/to/filename2.h", " match match", 2, 2, 5),
searchResult("/file/path/to/filename2.h", " match match", 2, 8, 5)};
}
void OutputParserTest::test()
{
QFETCH(QString, parserOutput);
QFETCH(FileSearchResultList, results);
QFETCH(SearchResultItems, results);
SilverSearcher::SilverSearcherOutputParser ssop(parserOutput);
const FileSearchResultList items = ssop.parse();
const SearchResultItems items = ssop.parse();
QCOMPARE(items, results);
}

View File

@@ -16,7 +16,7 @@ SilverSearcherOutputParser::SilverSearcherOutputParser(
hasRegexp = !regexp.pattern().isEmpty();
}
Utils::FileSearchResultList SilverSearcherOutputParser::parse()
Utils::SearchResultItems SilverSearcherOutputParser::parse()
{
while (index < outputSize - 1) {
if (output[index] == '\n') {
@@ -25,15 +25,15 @@ Utils::FileSearchResultList SilverSearcherOutputParser::parse()
}
parseFilePath();
while (index < outputSize && output[index] != '\n') {
parseLineNumber();
const int lineNumber = parseLineNumber();
if (index >= outputSize - 1)
break;
int matches = parseMatches();
int matches = parseMatches(lineNumber);
if (index >= outputSize - 1)
break;
parseText();
for (int i = 0; i < matches; ++i)
items[items.size() - i - 1].matchingLine = item.matchingLine;
items[items.size() - i - 1].setDisplayText(item.lineText());
}
}
@@ -45,41 +45,41 @@ bool SilverSearcherOutputParser::parseFilePath()
int startIndex = ++index;
while (index < outputSize && output[index] != '\n')
++index;
item.fileName = Utils::FilePath::fromString(QString(output.data() + startIndex, index - startIndex));
item.setFilePath(Utils::FilePath::fromString(QString(output.data() + startIndex,
index - startIndex)));
++index;
return true;
}
bool SilverSearcherOutputParser::parseLineNumber()
int SilverSearcherOutputParser::parseLineNumber()
{
int startIndex = index;
while (index < outputSize && output[++index] != ';') { }
item.lineNumber = QString(output.data() + startIndex, index - startIndex).toInt();
const int lineNumber = QString(output.data() + startIndex, index - startIndex).toInt();
++index;
return true;
return lineNumber;
}
bool SilverSearcherOutputParser::parseMatchIndex()
int SilverSearcherOutputParser::parseMatchIndex()
{
int startIndex = index;
while (index < outputSize && output[++index] != ' ') { }
item.matchStart = QString(output.data() + startIndex, index - startIndex).toInt();
const int lineStart = QString(output.data() + startIndex, index - startIndex).toInt();
++index;
return true;
return lineStart;
}
bool SilverSearcherOutputParser::parseMatchLength()
int SilverSearcherOutputParser::parseMatchLength()
{
int startIndex = index;
while (index < outputSize && output[++index] != ':' && output[index] != ',') { }
item.matchLength = QString(output.data() + startIndex, index - startIndex).toInt();
return true;
return QString(output.data() + startIndex, index - startIndex).toInt();
}
int SilverSearcherOutputParser::parseMatches()
int SilverSearcherOutputParser::parseMatches(int lineNumber)
{
int matches = 1;
const int colon = output.indexOf(':', index);
@@ -94,11 +94,12 @@ int SilverSearcherOutputParser::parseMatches()
++matches;
++index;
}
parseMatchIndex();
parseMatchLength();
const int lineStart = parseMatchIndex();
const int lineLength = parseMatchLength();
item.setMainRange(lineNumber, lineStart, lineLength);
if (hasRegexp) {
const QString part = QString(text.mid(item.matchStart, item.matchLength));
item.regexpCapturedTexts = regexp.match(part).capturedTexts();
const QString part = QString(text.mid(lineStart, lineLength));
item.setUserData(regexp.match(part).capturedTexts());
}
items << item;
}
@@ -111,7 +112,8 @@ bool SilverSearcherOutputParser::parseText()
{
int startIndex = index;
while (index < outputSize && output[++index] != '\n') { }
item.matchingLine = QString(output.data() + startIndex, index - startIndex);
item.setLineText(QString(output.data() + startIndex, index - startIndex));
++index;
return true;
}

View File

@@ -16,13 +16,13 @@ class SilverSearcherOutputParser
public:
SilverSearcherOutputParser(const QString &output, const QRegularExpression &regexp = {});
Utils::FileSearchResultList parse();
Utils::SearchResultItems parse();
private:
int parseMatches();
bool parseMatchLength();
bool parseMatchIndex();
bool parseLineNumber();
int parseMatches(int lineNumber);
int parseMatchLength();
int parseMatchIndex();
int parseLineNumber();
bool parseFilePath();
bool parseText();
@@ -31,8 +31,8 @@ private:
bool hasRegexp = false;
int outputSize = 0;
int index = 0;
Utils::FileSearchResult item;
Utils::FileSearchResultList items;
Utils::SearchResultItem item;
Utils::SearchResultItems items;
};
} // namespace SilverSearcher

View File

@@ -51,9 +51,8 @@ public:
QVariant parameters() const override { return {}; }
void readSettings(QSettings * /*settings*/) override {}
void writeSettings(QSettings * /*settings*/) const override {}
QFuture<FileSearchResultList> executeSearch(
const TextEditor::FileFindParameters &parameters,
BaseFileFind *baseFileFind) override
QFuture<SearchResultItems> executeSearch(const TextEditor::FileFindParameters &parameters,
BaseFileFind *baseFileFind) override
{
const auto func = parameters.flags & FindRegularExpression ? Utils::findInFilesRegExp
: Utils::findInFiles;
@@ -208,34 +207,6 @@ void BaseFileFind::setCurrentSearchEngine(int index)
emit currentSearchEngineChanged();
}
static QString displayText(const QString &line)
{
QString result = line;
auto end = result.end();
for (auto it = result.begin(); it != end; ++it) {
if (!it->isSpace() && !it->isPrint())
*it = QChar('?');
}
return result;
}
static void displayResult(QFutureWatcher<FileSearchResultList> *watcher,
SearchResult *search, int index)
{
const FileSearchResultList results = watcher->resultAt(index);
SearchResultItems items;
for (const FileSearchResult &result : results) {
SearchResultItem item;
item.setFilePath(result.fileName);
item.setMainRange(result.lineNumber, result.matchStart, result.matchLength);
item.setLineText(displayText(result.matchingLine));
item.setUseTextEditorFont(true);
item.setUserData(result.regexpCapturedTexts);
items << item;
}
search->addResults(items, SearchResult::AddOrdered);
}
void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
SearchResultWindow::SearchMode searchMode)
{
@@ -283,7 +254,7 @@ void BaseFileFind::runSearch(SearchResult *search)
{
const FileFindParameters parameters = search->userData().value<FileFindParameters>();
SearchResultWindow::instance()->popup(IOutputPane::Flags(IOutputPane::ModeSwitch|IOutputPane::WithFocus));
auto watcher = new QFutureWatcher<FileSearchResultList>();
auto watcher = new QFutureWatcher<SearchResultItems>;
watcher->setPendingResultsLimit(1);
// search is deleted if it is removed from search panel
connect(search, &QObject::destroyed, watcher, &QFutureWatcherBase::cancel);
@@ -293,14 +264,14 @@ void BaseFileFind::runSearch(SearchResult *search)
watcher->setPaused(paused);
});
connect(watcher, &QFutureWatcherBase::resultReadyAt, search, [watcher, search](int index) {
displayResult(watcher, search, index);
search->addResults(watcher->resultAt(index), SearchResult::AddOrdered);
});
// auto-delete:
connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
connect(watcher, &QFutureWatcherBase::finished, search, [watcher, search]() {
search->finishSearch(watcher->isCanceled());
});
QFuture<FileSearchResultList> future = executeSearch(parameters);
QFuture<SearchResultItems> future = executeSearch(parameters);
watcher->setFuture(future);
d->m_futureSynchronizer.addFuture(future);
FutureProgress *progress = ProgressManager::addTask(future,
@@ -541,7 +512,7 @@ QVariant BaseFileFind::getAdditionalParameters(SearchResult *search)
return search->userData().value<FileFindParameters>().additionalParameters;
}
QFuture<FileSearchResultList> BaseFileFind::executeSearch(const FileFindParameters &parameters)
QFuture<SearchResultItems> BaseFileFind::executeSearch(const FileFindParameters &parameters)
{
return d->m_searchEngines[parameters.searchEngineIndex]->executeSearch(parameters, this);
}

View File

@@ -56,7 +56,7 @@ public:
virtual QVariant parameters() const = 0;
virtual void readSettings(QSettings *settings) = 0;
virtual void writeSettings(QSettings *settings) const = 0;
virtual QFuture<Utils::FileSearchResultList> executeSearch(
virtual QFuture<Utils::SearchResultItems> executeSearch(
const FileFindParameters &parameters, BaseFileFind *baseFileFind) = 0;
virtual Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const FileFindParameters &parameters) = 0;
@@ -97,7 +97,7 @@ protected:
virtual QString label() const = 0; // see Core::SearchResultWindow::startNewSearch
virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch,
// add %1 placeholder where the find flags should be put
QFuture<Utils::FileSearchResultList> executeSearch(const FileFindParameters &parameters);
QFuture<Utils::SearchResultItems> executeSearch(const FileFindParameters &parameters);
void writeCommonSettings(QSettings *settings);
void readCommonSettings(QSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter);

View File

@@ -61,6 +61,7 @@
#include <utils/minimizableinfobars.h>
#include <utils/multitextcursor.h>
#include <utils/qtcassert.h>
#include <utils/searchresultitem.h>
#include <utils/styledbar.h>
#include <utils/stylehelper.h>
#include <utils/textutils.h>
@@ -790,7 +791,7 @@ public:
QScopedPointer<AutoCompleter> m_autoCompleter;
CommentDefinition m_commentDefinition;
QFutureWatcher<FileSearchResultList> *m_searchWatcher = nullptr;
QFutureWatcher<SearchResultItems> *m_searchWatcher = nullptr;
QVector<SearchResult> m_searchResults;
QTimer m_scrollBarUpdateTimer;
HighlightScrollBarController *m_highlightScrollBarController = nullptr;
@@ -846,10 +847,27 @@ public:
private:
TextEditorWidget * const m_editor;
static QFutureWatcher<FileSearchResultList> *m_selectWatcher;
static QFutureWatcher<SearchResultItems> *m_selectWatcher;
};
QFutureWatcher<FileSearchResultList> *TextEditorWidgetFind::m_selectWatcher = nullptr;
static QTextCursor selectRange(QTextDocument *textDocument, const Search::TextRange &range,
TextEditorWidgetPrivate::SearchResult *searchResult = nullptr)
{
const int startLine = qMax(range.begin.line - 1, 0);
const int startColumn = qMax(range.begin.column, 0);
const int endLine = qMax(range.end.line - 1, 0);
const int endColumn = qMax(range.end.column, 0);
const int startPosition = textDocument->findBlockByNumber(startLine).position() + startColumn;
const int endPosition = textDocument->findBlockByNumber(endLine).position() + endColumn;
QTextCursor textCursor(textDocument);
textCursor.setPosition(startPosition);
textCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
if (searchResult)
*searchResult = {startPosition + 1, endPosition + 1};
return textCursor;
}
QFutureWatcher<SearchResultItems> *TextEditorWidgetFind::m_selectWatcher = nullptr;
void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags)
{
@@ -858,26 +876,24 @@ void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags)
cancelCurrentSelectAll();
m_selectWatcher = new QFutureWatcher<FileSearchResultList>();
connect(m_selectWatcher, &QFutureWatcher<Utils::FileSearchResultList>::finished,
this, [this] {
const QFuture<FileSearchResultList> future = m_selectWatcher->future();
m_selectWatcher->deleteLater();
m_selectWatcher = nullptr;
if (future.resultCount() <= 0)
return;
const FileSearchResultList &results = future.result();
const QTextCursor c(m_editor->document());
auto cursorForResult = [c](const FileSearchResult &r) {
return Utils::Text::selectAt(c, r.lineNumber, r.matchStart + 1, r.matchLength);
};
QList<QTextCursor> cursors = Utils::transform(results, cursorForResult);
cursors = Utils::filtered(cursors, [this](const QTextCursor &c) {
return m_editor->inFindScope(c);
});
m_editor->setMultiTextCursor(MultiTextCursor(cursors));
m_editor->setFocus();
});
m_selectWatcher = new QFutureWatcher<SearchResultItems>();
connect(m_selectWatcher, &QFutureWatcher<SearchResultItems>::finished, this, [this] {
const QFuture<SearchResultItems> future = m_selectWatcher->future();
m_selectWatcher->deleteLater();
m_selectWatcher = nullptr;
if (future.resultCount() <= 0)
return;
const SearchResultItems &results = future.result();
const auto cursorForResult = [this](const SearchResultItem &item) {
return selectRange(m_editor->document(), item.mainRange());
};
QList<QTextCursor> cursors = Utils::transform(results, cursorForResult);
cursors = Utils::filtered(cursors, [this](const QTextCursor &c) {
return m_editor->inFindScope(c);
});
m_editor->setMultiTextCursor(MultiTextCursor(cursors));
m_editor->setFocus();
});
const FilePath &fileName = m_editor->textDocument()->filePath();
QMap<FilePath, QString> fileToContentsMap;
@@ -6671,16 +6687,11 @@ void TextEditorWidgetPrivate::searchResultsReady(int beginIndex, int endIndex)
{
QVector<SearchResult> results;
for (int index = beginIndex; index < endIndex; ++index) {
const FileSearchResultList resultList = m_searchWatcher->resultAt(index);
for (FileSearchResult result : resultList) {
const QTextBlock &block = q->document()->findBlockByNumber(result.lineNumber - 1);
const int matchStart = block.position() + result.matchStart;
QTextCursor cursor(block);
cursor.setPosition(matchStart);
cursor.setPosition(matchStart + result.matchLength, QTextCursor::KeepAnchor);
if (!q->inFindScope(cursor))
continue;
results << SearchResult{matchStart, result.matchLength};
const SearchResultItems resultList = m_searchWatcher->resultAt(index);
for (const SearchResultItem &result : resultList) {
SearchResult searchResult;
if (q->inFindScope(selectRange(q->document(), result.mainRange(), &searchResult)))
results << searchResult;
}
}
m_searchResults << results;
@@ -6726,10 +6737,10 @@ void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar()
adjustScrollBarRanges();
m_searchWatcher = new QFutureWatcher<FileSearchResultList>();
connect(m_searchWatcher, &QFutureWatcher<FileSearchResultList>::resultsReadyAt,
m_searchWatcher = new QFutureWatcher<SearchResultItems>;
connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::resultsReadyAt,
this, &TextEditorWidgetPrivate::searchResultsReady);
connect(m_searchWatcher, &QFutureWatcher<FileSearchResultList>::finished,
connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::finished,
this, &TextEditorWidgetPrivate::searchFinished);
m_searchWatcher->setPendingResultsLimit(10);
@@ -6775,7 +6786,7 @@ void TextEditorWidgetPrivate::addSearchResultsToScrollBar(const QVector<SearchRe
{
if (!m_highlightScrollBarController)
return;
for (SearchResult result : results) {
for (const SearchResult &result : results) {
const QTextBlock &block = q->document()->findBlock(result.start);
if (block.isValid() && block.isVisible()) {
const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber();

View File

@@ -22,66 +22,85 @@ private slots:
void caseSensitive();
void caseInSensitive();
void matchCaseReplacement();
private:
const FilePath m_filePath = FilePath::fromString(":/tst_filesearch/testfile.txt");
};
namespace {
const char * const FILENAME = ":/tst_filesearch/testfile.txt";
SearchResultItem searchResult(const FilePath &fileName, const QString &matchingLine,
int lineNumber, int matchStart, int matchLength,
const QStringList &regexpCapturedTexts = {})
{
SearchResultItem result;
result.setFilePath(fileName);
result.setLineText(matchingLine);
result.setMainRange(lineNumber, matchStart, matchLength);
result.setUserData(regexpCapturedTexts);
result.setUseTextEditorFont(true);
return result;
}
void test_helper(const Utils::FileSearchResultList &expectedResults,
const QString &term,
QTextDocument::FindFlags flags, tst_FileSearch::RegExpFlag regexp = tst_FileSearch::NoRegExp)
{
Utils::FileIterator *it = new Utils::FileListIterator({FilePath::fromString(FILENAME)},
{QTextCodec::codecForLocale()});
QFutureWatcher<Utils::FileSearchResultList> watcher;
QSignalSpy ready(&watcher, &QFutureWatcherBase::resultsReadyAt);
if (regexp == tst_FileSearch::NoRegExp)
watcher.setFuture(Utils::findInFiles(term, it, flags));
else
watcher.setFuture(Utils::findInFilesRegExp(term, it, flags));
watcher.future().waitForFinished();
QTest::qWait(100); // process events
QCOMPARE(ready.count(), 1);
Utils::FileSearchResultList results = watcher.resultAt(0);
QCOMPARE(results.count(), expectedResults.count());
for (int i = 0; i < expectedResults.size(); ++i) {
QCOMPARE(results.at(i), expectedResults.at(i));
}
}
void test_helper(const FilePath &filePath, const SearchResultItems &expectedResults,
const QString &term, QTextDocument::FindFlags flags = {},
tst_FileSearch::RegExpFlag regexp = tst_FileSearch::NoRegExp)
{
FileIterator *it = new FileListIterator({filePath}, {QTextCodec::codecForLocale()});
QFutureWatcher<SearchResultItems> watcher;
QSignalSpy ready(&watcher, &QFutureWatcherBase::resultsReadyAt);
if (regexp == tst_FileSearch::NoRegExp)
watcher.setFuture(Utils::findInFiles(term, it, flags));
else
watcher.setFuture(Utils::findInFilesRegExp(term, it, flags));
watcher.future().waitForFinished();
QTest::qWait(100); // process events
QCOMPARE(ready.count(), 1);
SearchResultItems results = watcher.resultAt(0);
QCOMPARE(results.count(), expectedResults.count());
for (int i = 0; i < expectedResults.size(); ++i)
QCOMPARE(results.at(i), expectedResults.at(i));
}
void tst_FileSearch::multipleResults()
{
Utils::FileSearchResultList expectedResults;
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 2, QLatin1String("search to find multiple find results"), 10, 4, QStringList());
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 2, QLatin1String("search to find multiple find results"), 24, 4, QStringList());
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 4, QLatin1String("here you find another result"), 9, 4, QStringList());
test_helper(expectedResults, QLatin1String("find"), QTextDocument::FindFlags());
SearchResultItems expectedResults;
expectedResults << searchResult(m_filePath, "search to find multiple find results", 2, 10, 4);
expectedResults << searchResult(m_filePath, "search to find multiple find results", 2, 24, 4);
expectedResults << searchResult(m_filePath, "here you find another result", 4, 9, 4);
test_helper(m_filePath, expectedResults, "find");
expectedResults.clear();
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 0, 4, QStringList());
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 4, 4, QStringList());
test_helper(expectedResults, QLatin1String("aaaa"), QTextDocument::FindFlags());
expectedResults << searchResult(m_filePath,
"aaaaaaaa this line has 2 results for four a in a row",
5, 0, 4);
expectedResults << searchResult(m_filePath,
"aaaaaaaa this line has 2 results for four a in a row",
5, 4, 4);
test_helper(m_filePath, expectedResults, "aaaa");
expectedResults.clear();
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 0, 4, QStringList() << QLatin1String("aaaa"));
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 4, 4, QStringList() << QLatin1String("aaaa"));
test_helper(expectedResults, QLatin1String("aaaa"), QTextDocument::FindFlags(), RegExp);
expectedResults << searchResult(m_filePath,
"aaaaaaaa this line has 2 results for four a in a row",
5, 0, 4, {"aaaa"});
expectedResults << searchResult(m_filePath,
"aaaaaaaa this line has 2 results for four a in a row",
5, 4, 4, {"aaaa"});
test_helper(m_filePath, expectedResults, "aaaa", {}, RegExp);
}
void tst_FileSearch::caseSensitive()
{
Utils::FileSearchResultList expectedResults;
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 3, QLatin1String("search CaseSensitively for casesensitive"), 7, 13, QStringList());
test_helper(expectedResults, QLatin1String("CaseSensitive"), QTextDocument::FindCaseSensitively);
SearchResultItems expectedResults;
expectedResults << searchResult(m_filePath, "search CaseSensitively for casesensitive",
3, 7, 13);
test_helper(m_filePath, expectedResults, "CaseSensitive", QTextDocument::FindCaseSensitively);
}
void tst_FileSearch::caseInSensitive()
{
Utils::FileSearchResultList expectedResults;
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 3, QLatin1String("search CaseSensitively for casesensitive"), 7, 13, QStringList());
expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 3, QLatin1String("search CaseSensitively for casesensitive"), 27, 13, QStringList());
test_helper(expectedResults, QLatin1String("CaseSensitive"), QTextDocument::FindFlags());
SearchResultItems expectedResults;
expectedResults << searchResult(m_filePath, "search CaseSensitively for casesensitive", 3, 7, 13);
expectedResults << searchResult(m_filePath, "search CaseSensitively for casesensitive", 3, 27, 13);
test_helper(m_filePath, expectedResults, "CaseSensitive");
}
void tst_FileSearch::matchCaseReplacement()

View File

@@ -38,7 +38,6 @@ add_qtc_test(unittest GTEST
TEST_RELATIVE_LIBEXEC_PATH="${TEST_RELATIVE_LIBEXEC_PATH}"
SOURCES
abstractviewmock.h
compare-operators.h
conditionally-disabled-tests.h
dynamicastmatcherdiagnosticcontainer-matcher.h
eventspy.cpp eventspy.h

View File

@@ -1,25 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <utils/searchresultitem.h>
namespace Utils {
namespace Search {
inline
bool operator==(const TextPosition first, class TextPosition second)
{
return first.line == second.line
&& first.column == second.column;
}
inline
bool operator==(const TextRange first, class TextRange second)
{
return first.begin == second.begin
&& first.end == second.end;
}
}
}

View File

@@ -13,8 +13,6 @@
#include <gtest/gtest-printers.h>
#include <gtest/gtest-typed-test.h>
#include "compare-operators.h"
#include "conditionally-disabled-tests.h"
#include "gtest-creator-printing.h"
#include "gtest-llvm-printing.h"