forked from qt-creator/qt-creator
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:
@@ -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 ¶meters)
|
||||||
|
{
|
||||||
|
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 ¶meters,
|
||||||
|
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
|
||||||
|
|||||||
@@ -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 ¶meters,
|
||||||
|
const ProcessSetupHandler &processSetupHandler,
|
||||||
|
const ProcessOutputParser &processOutputParser);
|
||||||
|
|
||||||
class BaseFileFind;
|
class BaseFileFind;
|
||||||
|
|
||||||
class TEXTEDITOR_EXPORT SearchEngine : public QObject
|
class TEXTEDITOR_EXPORT SearchEngine : public QObject
|
||||||
|
|||||||
Reference in New Issue
Block a user