SilverSearcher: Reuse searchInProcessOutput()

Change-Id: Ifc28a88c7bd0de94ea78c5f3eaaa2179b0aee600
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
Jarek Kobus
2023-06-01 21:04:18 +02:00
parent 8da3575d72
commit 64d209c24b
3 changed files with 48 additions and 125 deletions

View File

@@ -59,114 +59,51 @@ static bool isSilverSearcherAvailable()
&& silverSearcherProcess.cleanedStdOut().contains("ag version"); && silverSearcherProcess.cleanedStdOut().contains("ag version");
} }
static std::optional<QRegularExpression> regExpFromParameters(const FileFindParameters &parameters)
{
if (!(parameters.flags & FindRegularExpression))
return {};
const QRegularExpression::PatternOptions patternOptions
= (parameters.flags & FindCaseSensitively)
? QRegularExpression::NoPatternOption
: QRegularExpression::CaseInsensitiveOption;
QRegularExpression regExp;
regExp.setPattern(parameters.text);
regExp.setPatternOptions(patternOptions);
return regExp;
}
static void runSilverSeacher(QPromise<SearchResultItems> &promise, static void runSilverSeacher(QPromise<SearchResultItems> &promise,
const FileFindParameters &parameters) const FileFindParameters &parameters)
{ {
const FilePath directory = FilePath::fromUserInput(parameters.additionalParameters.toString()); const auto setupProcess = [parameters](Process &process) {
QStringList arguments = {"--parallel", "--ackmate"}; const FilePath directory
= FilePath::fromUserInput(parameters.additionalParameters.toString());
QStringList arguments = {"--parallel", "--ackmate"};
if (parameters.flags & FindCaseSensitively) if (parameters.flags & FindCaseSensitively)
arguments << "-s"; arguments << "-s";
else else
arguments << "-i"; arguments << "-i";
if (parameters.flags & FindWholeWords) if (parameters.flags & FindWholeWords)
arguments << "-w"; arguments << "-w";
if (!(parameters.flags & FindRegularExpression)) if (!(parameters.flags & FindRegularExpression))
arguments << "-Q"; arguments << "-Q";
for (const QString &filter : std::as_const(parameters.exclusionFilters)) for (const QString &filter : std::as_const(parameters.exclusionFilters))
arguments << "--ignore" << filter; arguments << "--ignore" << filter;
QString nameFiltersAsRegExp; QString nameFiltersAsRegExp;
for (const QString &filter : std::as_const(parameters.nameFilters)) for (const QString &filter : std::as_const(parameters.nameFilters))
nameFiltersAsRegExp += QString("(%1)|").arg(convertWildcardToRegex(filter)); nameFiltersAsRegExp += QString("(%1)|").arg(convertWildcardToRegex(filter));
nameFiltersAsRegExp.remove(nameFiltersAsRegExp.length() - 1, 1); nameFiltersAsRegExp.remove(nameFiltersAsRegExp.length() - 1, 1);
arguments << "-G" << nameFiltersAsRegExp; arguments << "-G" << nameFiltersAsRegExp;
const SilverSearcherSearchOptions params = parameters.searchEngineParameters const SilverSearcherSearchOptions params = parameters.searchEngineParameters
.value<SilverSearcherSearchOptions>(); .value<SilverSearcherSearchOptions>();
if (!params.searchOptions.isEmpty()) if (!params.searchOptions.isEmpty())
arguments << params.searchOptions.split(' '); arguments << params.searchOptions.split(' ');
arguments << "--" << parameters.text << directory.normalizedPathName().toString(); arguments << "--" << parameters.text << directory.normalizedPathName().toString();
process.setCommand({"ag", arguments});
};
QEventLoop loop; FilePath lastFilePath;
const auto outputParser = [&lastFilePath](const QFuture<void> &future, const QString &input,
const std::optional<QRegularExpression> &regExp) {
return SilverSearcher::parse(future, input, regExp, &lastFilePath);
};
Process process; TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser);
process.setCommand({"ag", arguments});
ParserState parserState;
const std::optional<QRegularExpression> regExp = regExpFromParameters(parameters);
QStringList outputBuffer;
// The states transition exactly in this order:
enum State { BelowLimit, AboveLimit, Paused, Resumed };
State state = BelowLimit;
process.setStdOutCallback([&process, &loop, &promise, &state, &outputBuffer, &parserState,
regExp](const QString &output) {
if (promise.isCanceled()) {
process.close();
loop.quit();
return;
}
// The SearchResultWidget is going to pause the search anyway, so start buffering
// the output.
if (state == AboveLimit || state == Paused) {
outputBuffer.append(output);
} else {
SilverSearcher::parse(promise, output, &parserState, regExp);
if (state == BelowLimit && parserState.m_reportedResultsCount > 200000)
state = AboveLimit;
}
});
QObject::connect(&process, &Process::done, &loop, [&loop, &promise, &state] {
if (state == BelowLimit || state == Resumed || promise.isCanceled())
loop.quit();
});
process.start();
if (process.state() == QProcess::NotRunning)
return;
QFutureWatcher<void> watcher;
QFuture<void> future(promise.future());
QObject::connect(&watcher, &QFutureWatcherBase::canceled, &loop, [&process, &loop] {
process.close();
loop.quit();
});
QObject::connect(&watcher, &QFutureWatcherBase::paused, &loop, [&state] { state = Paused; });
QObject::connect(&watcher, &QFutureWatcherBase::resumed, &loop,
[&process, &loop, &promise, &state, &outputBuffer, &parserState, regExp] {
state = Resumed;
for (const QString &output : outputBuffer) {
if (promise.isCanceled()) {
process.close();
loop.quit();
}
SilverSearcher::parse(promise, output, &parserState, regExp);
}
outputBuffer.clear();
if (process.state() == QProcess::NotRunning)
loop.quit();
});
watcher.setFuture(future);
loop.exec(QEventLoop::ExcludeUserInputEvents);
} }
} // namespace } // namespace

View File

@@ -3,8 +3,6 @@
#include "silversearcherparser.h" #include "silversearcherparser.h"
#include <QFuture>
using namespace Utils; using namespace Utils;
namespace SilverSearcher { namespace SilverSearcher {
@@ -96,28 +94,28 @@ static bool parseLineHits(QStringView *remainingInput, QList<QPair<int, int>> *h
return true; return true;
} }
void parse(QPromise<SearchResultItems> &promise, const QString &input, SearchResultItems parse(const QFuture<void> &future, const QString &input,
ParserState *parserState, const std::optional<QRegularExpression> &regExp) const std::optional<QRegularExpression> &regExp, FilePath *lastFilePath)
{ {
QTC_ASSERT(parserState, return); QTC_ASSERT(lastFilePath, return {});
SearchResultItems items; SearchResultItems items;
QStringView remainingInput(input); QStringView remainingInput(input);
while (true) { while (true) {
if (promise.isCanceled()) if (future.isCanceled())
return; return {};
if (remainingInput.isEmpty()) if (remainingInput.isEmpty())
break; break;
const QStringView filePathLine = nextLine(&remainingInput); const QStringView filePathLine = nextLine(&remainingInput);
if (filePathLine.isEmpty()) { if (filePathLine.isEmpty()) {
parserState->m_lastFilePath = {}; // Clear the parser state *lastFilePath = {}; // Clear the parser state
continue; continue;
} }
if (filePathLine.startsWith(':')) if (filePathLine.startsWith(':'))
parserState->m_lastFilePath = FilePath::fromPathPart(filePathLine.mid(1)); *lastFilePath = FilePath::fromPathPart(filePathLine.mid(1));
while (true) { while (true) {
QStringView hitLine = nextLine(&remainingInput); QStringView hitLine = nextLine(&remainingInput);
@@ -133,7 +131,7 @@ void parse(QPromise<SearchResultItems> &promise, const QString &input,
break; break;
SearchResultItem item; SearchResultItem item;
item.setFilePath(parserState->m_lastFilePath); item.setFilePath(*lastFilePath);
item.setDisplayText(hitLine.toString()); item.setDisplayText(hitLine.toString());
item.setUseTextEditorFont(true); item.setUseTextEditorFont(true);
for (const QPair<int, int> &hit : hits) { for (const QPair<int, int> &hit : hits) {
@@ -145,20 +143,15 @@ void parse(QPromise<SearchResultItems> &promise, const QString &input,
} }
} }
} }
if (!items.isEmpty()) return items;
promise.addResult(items);
parserState->m_reportedResultsCount += items.count();
} }
SearchResultItems parse(const QString &input, const std::optional<QRegularExpression> &regExp) SearchResultItems parse(const QString &input, const std::optional<QRegularExpression> &regExp)
{ {
QPromise<SearchResultItems> promise; QPromise<void> promise;
promise.start(); promise.start();
ParserState dummy; FilePath dummy;
SilverSearcher::parse(promise, input, &dummy, regExp); return SilverSearcher::parse(promise.future(), input, regExp, &dummy);
promise.finish();
return promise.future().resultCount() ? promise.future().result() : SearchResultItems();
} }
} // namespace SilverSearcher } // namespace SilverSearcher

View File

@@ -5,23 +5,16 @@
#include <utils/searchresultitem.h> #include <utils/searchresultitem.h>
#include <QPromise> #include <QFuture>
#include <QRegularExpression> #include <QRegularExpression>
namespace Utils { class FilePath; } namespace Utils { class FilePath; }
namespace SilverSearcher { namespace SilverSearcher {
class ParserState Utils::SearchResultItems parse(const QFuture<void> &future, const QString &input,
{ const std::optional<QRegularExpression> &regExp,
public: Utils::FilePath *lastFilePath);
Utils::FilePath m_lastFilePath;
int m_reportedResultsCount = 0;
};
void parse(QPromise<Utils::SearchResultItems> &promise, const QString &input,
ParserState *parserState = nullptr,
const std::optional<QRegularExpression> &regExp = {});
Utils::SearchResultItems parse(const QString &input, Utils::SearchResultItems parse(const QString &input,
const std::optional<QRegularExpression> &regExp = {}); const std::optional<QRegularExpression> &regExp = {});