BaseFileFind: Introduce searchInProcessOutput()

To be used by SilverSearcher and GitGrep.

Change-Id: I16a5fa18a90e6c895658ebc9dd8fd209235e17d3
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 20:43:37 +02:00
parent 7b2c1fdab0
commit 8da3575d72
2 changed files with 104 additions and 0 deletions

View File

@@ -21,6 +21,7 @@
#include <utils/fadingindicator.h> #include <utils/fadingindicator.h>
#include <utils/filesearch.h> #include <utils/filesearch.h>
#include <utils/futuresynchronizer.h> #include <utils/futuresynchronizer.h>
#include <utils/process.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/stylehelper.h> #include <utils/stylehelper.h>
@@ -38,6 +39,98 @@ using namespace Utils;
using namespace Core; using namespace Core;
namespace TextEditor { namespace TextEditor {
static std::optional<QRegularExpression> regExpFromParameters(const FileFindParameters &parameters)
{
if (!(parameters.flags & FindRegularExpression))
return {};
const QRegularExpression::PatternOptions options = parameters.flags & FindCaseSensitively
? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption;
QRegularExpression regExp;
regExp.setPattern(parameters.text);
regExp.setPatternOptions(options);
return regExp;
}
void searchInProcessOutput(QPromise<SearchResultItems> &promise,
const FileFindParameters &parameters,
const ProcessSetupHandler &processSetupHandler,
const ProcessOutputParser &processOutputParser)
{
if (promise.isCanceled())
return;
QEventLoop loop;
Process process;
processSetupHandler(process);
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;
int reportedItemsCount = 0;
QFuture<void> future(promise.future());
process.setStdOutCallback([&](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 {
const SearchResultItems items = processOutputParser(future, output, regExp);
if (!items.isEmpty())
promise.addResult(items);
reportedItemsCount += items.size();
if (state == BelowLimit && reportedItemsCount > 200000)
state = AboveLimit;
}
});
QObject::connect(&process, &Process::done, &loop, [&loop, &promise, &state] {
if (state == BelowLimit || state == Resumed || promise.isCanceled())
loop.quit();
});
if (promise.isCanceled())
return;
process.start();
if (process.state() == QProcess::NotRunning)
return;
QFutureWatcher<void> watcher;
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, [&] {
state = Resumed;
for (const QString &output : outputBuffer) {
if (promise.isCanceled()) {
process.close();
loop.quit();
}
const SearchResultItems items = processOutputParser(future, output, regExp);
if (!items.isEmpty())
promise.addResult(items);
}
outputBuffer.clear();
if (process.state() == QProcess::NotRunning)
loop.quit();
});
watcher.setFuture(future);
if (promise.isCanceled())
return;
loop.exec(QEventLoop::ExcludeUserInputEvents);
}
namespace Internal { namespace Internal {
class InternalEngine : public TextEditor::SearchEngine class InternalEngine : public TextEditor::SearchEngine

View File

@@ -20,6 +20,7 @@ class SearchResult;
namespace Utils { namespace Utils {
class FileIterator; class FileIterator;
class Process;
} }
namespace TextEditor { namespace TextEditor {
@@ -41,6 +42,16 @@ public:
Core::FindFlags flags; Core::FindFlags flags;
}; };
using ProcessSetupHandler = std::function<void(Utils::Process &)>;
using ProcessOutputParser = std::function<Utils::SearchResultItems(
const QFuture<void> &, const QString &, const std::optional<QRegularExpression> &)>;
// Call it from a non-main thread only, it's a blocking invocation.
void TEXTEDITOR_EXPORT searchInProcessOutput(QPromise<Utils::SearchResultItems> &promise,
const FileFindParameters &parameters,
const ProcessSetupHandler &processSetupHandler,
const ProcessOutputParser &processOutputParser);
class BaseFileFind; class BaseFileFind;
class TEXTEDITOR_EXPORT SearchEngine : public QObject class TEXTEDITOR_EXPORT SearchEngine : public QObject