diff --git a/src/libs/utils/datafromprocess.h b/src/libs/utils/datafromprocess.h index 909252ce505..7cd833a9114 100644 --- a/src/libs/utils/datafromprocess.h +++ b/src/libs/utils/datafromprocess.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ public: public: using OutputParser = std::function(const QString &)>; using ErrorHandler = std::function; + using Callback = std::function &)>; Parameters(const CommandLine &cmdLine, const OutputParser &parser) : commandLine(cmdLine) @@ -43,25 +45,50 @@ public: std::chrono::seconds timeout = std::chrono::seconds(10); OutputParser parser; ErrorHandler errorHandler; + Callback callback; QList allowedResults{ProcessResult::FinishedWithSuccess}; }; + // Use the first variant whenever possible. + static void provideData(const Parameters ¶ms); static std::optional getData(const Parameters ¶ms); - // TODO: async variant. - private: using Key = std::tuple; using Value = std::pair, QDateTime>; + + static std::optional getOrProvideData(const Parameters ¶ms); + static std::optional handleProcessFinished(const Parameters ¶ms, + const QDateTime &exeTimestamp, + const Key &cacheKey, + const std::shared_ptr &process); + static inline QHash m_cache; static inline QMutex m_cacheMutex; }; +template +inline void DataFromProcess::provideData(const Parameters ¶ms) +{ + QTC_ASSERT(params.callback, return); + getOrProvideData(params); +} + template inline std::optional DataFromProcess::getData(const Parameters ¶ms) { - if (params.commandLine.executable().isEmpty()) + QTC_ASSERT(!params.callback, return {}); + return getOrProvideData(params); +} + +template +inline std::optional DataFromProcess::getOrProvideData(const Parameters ¶ms) +{ + if (params.commandLine.executable().isEmpty()) { + if (params.callback) + params.callback({}); return {}; + } const auto key = std::make_tuple(params.commandLine.executable(), params.environment.toStringList(), @@ -74,21 +101,47 @@ inline std::optional DataFromProcess::getData(const Parameters ¶ return it.value().first; } - Process outputRetriever; - outputRetriever.setCommand(params.commandLine); - outputRetriever.runBlocking(params.timeout); - - // Do not store into cache: The next call might succeed. - if (outputRetriever.result() == ProcessResult::Canceled) + const auto outputRetriever = std::make_shared(); + outputRetriever->setCommand(params.commandLine); + if (params.callback) { + QObject::connect(outputRetriever.get(), + &Process::done, + [params, exeTimestamp, key, outputRetriever] { + handleProcessFinished(params, exeTimestamp, key, outputRetriever); + }); + outputRetriever->start(); return {}; + } + + outputRetriever->runBlocking(params.timeout); + return handleProcessFinished(params, exeTimestamp, key, outputRetriever); +} + +template +inline std::optional DataFromProcess::handleProcessFinished( + const Parameters ¶ms, + const QDateTime &exeTimestamp, + const Key &cacheKey, + const std::shared_ptr &process) +{ + // Do not store into cache: The next call might succeed. + if (process->result() == ProcessResult::Canceled) { + if (params.callback) + params.callback({}); + return {}; + } std::optional data; - if (params.allowedResults.contains(outputRetriever.result())) - data = params.parser(outputRetriever.cleanedStdOut()); + if (params.allowedResults.contains(process->result())) + data = params.parser(process->cleanedStdOut()); else if (params.errorHandler) - params.errorHandler(outputRetriever); + params.errorHandler(*process); QMutexLocker cacheLocker(&m_cacheMutex); - m_cache.insert(key, std::make_pair(data, exeTimestamp)); + m_cache.insert(cacheKey, std::make_pair(data, exeTimestamp)); + if (params.callback) { + params.callback(data); + return {}; + } return data; } diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index b8704061cd5..edef66e1f8a 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -85,8 +86,6 @@ public: QStringList arguments() const { return m_arguments; } void setArguments(const QStringList &arguments) { m_arguments = arguments; } - static QString toolVersion(const CommandLine &cmd); - private: // Extension point for concatenating existing tooltips. virtual QString defaultToolTip() const { return QString(); } @@ -108,35 +107,41 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e) QTC_ASSERT(le, return false); const QString binary = le->text(); - if (!binary.isEmpty()) { - const QString version = BinaryVersionToolTipEventFilter::toolVersion( - CommandLine(FilePath::fromString(QDir::cleanPath(binary)), m_arguments)); - if (!version.isEmpty()) { - // Concatenate tooltips. - QString tooltip = ""; - const QString defaultValue = defaultToolTip(); - if (!defaultValue.isEmpty()) { - tooltip += "

"; - tooltip += defaultValue; - tooltip += "

"; - } - tooltip += "
";
-            tooltip += version;
-            tooltip += "
"; - le->setToolTip(tooltip); - } - } - return false; -} + DataFromProcess::Parameters params(CommandLine(FilePath::fromString( + QDir::cleanPath(binary)), + m_arguments), + [](const QString &output) { return output; }); + params.callback = [binary, self = QPointer(this), + le = QPointer(le)](const std::optional &version) { + if (!self || !le) + return; + if (!version || version->isEmpty()) + return; + if (binary != le->text()) + return; -QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd) -{ - DataFromProcess::Parameters params(cmd, [](const QString &output) { return output; }); - using namespace std::chrono_literals; - params.timeout = 1s; - if (const auto version = DataFromProcess::getData(params)) - return *version; - return {}; + // Concatenate tooltips. + QString tooltip = ""; + const QString defaultValue = self->defaultToolTip(); + if (!defaultValue.isEmpty()) { + tooltip += "

"; + tooltip += defaultValue; + tooltip += "

"; + } + tooltip += "
";
+        tooltip += *version;
+        tooltip += "
"; + le->setToolTip(tooltip); + if (QRect(le->mapToGlobal(QPoint(0, 0)), le->size()).contains(QCursor::pos())) { + QCoreApplication::postEvent(le, + new QHelpEvent(QEvent::ToolTip, + le->mapFromGlobal(QCursor::pos()), + QCursor::pos())); + } + }; + DataFromProcess::provideData(params); + + return false; } // Extends BinaryVersionToolTipEventFilter to prepend the existing pathchooser @@ -722,11 +727,6 @@ FancyLineEdit *PathChooser::lineEdit() const return d->m_lineEdit; } -QString PathChooser::toolVersion(const CommandLine &cmd) -{ - return BinaryVersionToolTipEventFilter::toolVersion(cmd); -} - void PathChooser::installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments) { auto ef = new BinaryVersionToolTipEventFilter(le); diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index a91a853938f..a3e259a748d 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -99,8 +99,6 @@ public: QStringList commandVersionArguments() const; void setCommandVersionArguments(const QStringList &arguments); - // Utility to run a tool and return its stdout. - static QString toolVersion(const CommandLine &cmd); // Install a tooltip on lineedits used for binaries showing the version. static void installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments); diff --git a/src/plugins/git/gerrit/gerritparameters.cpp b/src/plugins/git/gerrit/gerritparameters.cpp index 781597e5e78..8aa9f73ca73 100644 --- a/src/plugins/git/gerrit/gerritparameters.cpp +++ b/src/plugins/git/gerrit/gerritparameters.cpp @@ -5,6 +5,7 @@ #include "gerritplugin.h" #include +#include #include #include #include @@ -70,8 +71,12 @@ void GerritParameters::setPortFlagBySshType() { bool isPlink = false; if (!ssh.isEmpty()) { - const QString version = PathChooser::toolVersion({ssh, {"-V"}}); - isPlink = version.contains("plink", Qt::CaseInsensitive); + DataFromProcess::Parameters params({ssh, {"-V"}}, + [](const QString &output) { return output; }); + using namespace std::chrono_literals; + params.timeout = 1s; + if (const auto version = DataFromProcess::getData(params)) + isPlink = version->contains("plink", Qt::CaseInsensitive); } portFlag = QLatin1String(isPlink ? "-P" : defaultPortFlag); }