forked from qt-creator/qt-creator
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:
@@ -59,114 +59,51 @@ static bool isSilverSearcherAvailable()
|
||||
&& silverSearcherProcess.cleanedStdOut().contains("ag version");
|
||||
}
|
||||
|
||||
static std::optional<QRegularExpression> regExpFromParameters(const FileFindParameters ¶meters)
|
||||
{
|
||||
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,
|
||||
const FileFindParameters ¶meters)
|
||||
{
|
||||
const FilePath directory = FilePath::fromUserInput(parameters.additionalParameters.toString());
|
||||
QStringList arguments = {"--parallel", "--ackmate"};
|
||||
const auto setupProcess = [parameters](Process &process) {
|
||||
const FilePath directory
|
||||
= FilePath::fromUserInput(parameters.additionalParameters.toString());
|
||||
QStringList arguments = {"--parallel", "--ackmate"};
|
||||
|
||||
if (parameters.flags & FindCaseSensitively)
|
||||
arguments << "-s";
|
||||
else
|
||||
arguments << "-i";
|
||||
if (parameters.flags & FindCaseSensitively)
|
||||
arguments << "-s";
|
||||
else
|
||||
arguments << "-i";
|
||||
|
||||
if (parameters.flags & FindWholeWords)
|
||||
arguments << "-w";
|
||||
if (parameters.flags & FindWholeWords)
|
||||
arguments << "-w";
|
||||
|
||||
if (!(parameters.flags & FindRegularExpression))
|
||||
arguments << "-Q";
|
||||
if (!(parameters.flags & FindRegularExpression))
|
||||
arguments << "-Q";
|
||||
|
||||
for (const QString &filter : std::as_const(parameters.exclusionFilters))
|
||||
arguments << "--ignore" << filter;
|
||||
for (const QString &filter : std::as_const(parameters.exclusionFilters))
|
||||
arguments << "--ignore" << filter;
|
||||
|
||||
QString nameFiltersAsRegExp;
|
||||
for (const QString &filter : std::as_const(parameters.nameFilters))
|
||||
nameFiltersAsRegExp += QString("(%1)|").arg(convertWildcardToRegex(filter));
|
||||
nameFiltersAsRegExp.remove(nameFiltersAsRegExp.length() - 1, 1);
|
||||
QString nameFiltersAsRegExp;
|
||||
for (const QString &filter : std::as_const(parameters.nameFilters))
|
||||
nameFiltersAsRegExp += QString("(%1)|").arg(convertWildcardToRegex(filter));
|
||||
nameFiltersAsRegExp.remove(nameFiltersAsRegExp.length() - 1, 1);
|
||||
|
||||
arguments << "-G" << nameFiltersAsRegExp;
|
||||
arguments << "-G" << nameFiltersAsRegExp;
|
||||
|
||||
const SilverSearcherSearchOptions params = parameters.searchEngineParameters
|
||||
.value<SilverSearcherSearchOptions>();
|
||||
if (!params.searchOptions.isEmpty())
|
||||
arguments << params.searchOptions.split(' ');
|
||||
const SilverSearcherSearchOptions params = parameters.searchEngineParameters
|
||||
.value<SilverSearcherSearchOptions>();
|
||||
if (!params.searchOptions.isEmpty())
|
||||
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> ®Exp) {
|
||||
return SilverSearcher::parse(future, input, regExp, &lastFilePath);
|
||||
};
|
||||
|
||||
Process process;
|
||||
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);
|
||||
TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include "silversearcherparser.h"
|
||||
|
||||
#include <QFuture>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace SilverSearcher {
|
||||
@@ -96,28 +94,28 @@ static bool parseLineHits(QStringView *remainingInput, QList<QPair<int, int>> *h
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse(QPromise<SearchResultItems> &promise, const QString &input,
|
||||
ParserState *parserState, const std::optional<QRegularExpression> ®Exp)
|
||||
SearchResultItems parse(const QFuture<void> &future, const QString &input,
|
||||
const std::optional<QRegularExpression> ®Exp, FilePath *lastFilePath)
|
||||
{
|
||||
QTC_ASSERT(parserState, return);
|
||||
QTC_ASSERT(lastFilePath, return {});
|
||||
SearchResultItems items;
|
||||
|
||||
QStringView remainingInput(input);
|
||||
while (true) {
|
||||
if (promise.isCanceled())
|
||||
return;
|
||||
if (future.isCanceled())
|
||||
return {};
|
||||
|
||||
if (remainingInput.isEmpty())
|
||||
break;
|
||||
|
||||
const QStringView filePathLine = nextLine(&remainingInput);
|
||||
if (filePathLine.isEmpty()) {
|
||||
parserState->m_lastFilePath = {}; // Clear the parser state
|
||||
*lastFilePath = {}; // Clear the parser state
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filePathLine.startsWith(':'))
|
||||
parserState->m_lastFilePath = FilePath::fromPathPart(filePathLine.mid(1));
|
||||
*lastFilePath = FilePath::fromPathPart(filePathLine.mid(1));
|
||||
|
||||
while (true) {
|
||||
QStringView hitLine = nextLine(&remainingInput);
|
||||
@@ -133,7 +131,7 @@ void parse(QPromise<SearchResultItems> &promise, const QString &input,
|
||||
break;
|
||||
|
||||
SearchResultItem item;
|
||||
item.setFilePath(parserState->m_lastFilePath);
|
||||
item.setFilePath(*lastFilePath);
|
||||
item.setDisplayText(hitLine.toString());
|
||||
item.setUseTextEditorFont(true);
|
||||
for (const QPair<int, int> &hit : hits) {
|
||||
@@ -145,20 +143,15 @@ void parse(QPromise<SearchResultItems> &promise, const QString &input,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!items.isEmpty())
|
||||
promise.addResult(items);
|
||||
|
||||
parserState->m_reportedResultsCount += items.count();
|
||||
return items;
|
||||
}
|
||||
|
||||
SearchResultItems parse(const QString &input, const std::optional<QRegularExpression> ®Exp)
|
||||
{
|
||||
QPromise<SearchResultItems> promise;
|
||||
QPromise<void> promise;
|
||||
promise.start();
|
||||
ParserState dummy;
|
||||
SilverSearcher::parse(promise, input, &dummy, regExp);
|
||||
promise.finish();
|
||||
return promise.future().resultCount() ? promise.future().result() : SearchResultItems();
|
||||
FilePath dummy;
|
||||
return SilverSearcher::parse(promise.future(), input, regExp, &dummy);
|
||||
}
|
||||
|
||||
} // namespace SilverSearcher
|
||||
|
||||
@@ -5,23 +5,16 @@
|
||||
|
||||
#include <utils/searchresultitem.h>
|
||||
|
||||
#include <QPromise>
|
||||
#include <QFuture>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace Utils { class FilePath; }
|
||||
|
||||
namespace SilverSearcher {
|
||||
|
||||
class ParserState
|
||||
{
|
||||
public:
|
||||
Utils::FilePath m_lastFilePath;
|
||||
int m_reportedResultsCount = 0;
|
||||
};
|
||||
|
||||
void parse(QPromise<Utils::SearchResultItems> &promise, const QString &input,
|
||||
ParserState *parserState = nullptr,
|
||||
const std::optional<QRegularExpression> ®Exp = {});
|
||||
Utils::SearchResultItems parse(const QFuture<void> &future, const QString &input,
|
||||
const std::optional<QRegularExpression> ®Exp,
|
||||
Utils::FilePath *lastFilePath);
|
||||
|
||||
Utils::SearchResultItems parse(const QString &input,
|
||||
const std::optional<QRegularExpression> ®Exp = {});
|
||||
|
||||
Reference in New Issue
Block a user