diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index eb9d562005354922bd2f4d7cd927ec9460ebf948..c9ce5894ea4cc46d10c8959025f9b2de86509716 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -829,6 +829,26 @@ private: QStringList m_files; }; +class ProgressParser : public VcsBase::ProgressParser +{ +public: + ProgressParser() : + m_progressExp(QLatin1String("\\((\\d+)/(\\d+)\\)")) // e.g. Rebasing (7/42) + { + } + +protected: + void parseProgress(const QString &text) + { + if (m_progressExp.lastIndexIn(text) != -1) + setProgressAndMaximum(m_progressExp.cap(1).toInt(), m_progressExp.cap(2).toInt()); + } + +private: + QRegExp m_progressExp; +}; + + Core::IEditor *locateEditor(const char *property, const QString &entry) { @@ -3487,6 +3507,7 @@ void GitClient::rebase(const QString &workingDirectory, const QString &baseBranc arguments); VcsBase::Command *command = createCommand(workingDirectory, 0, true); new ConflictHandler(command, workingDirectory, gitCommand); + command->setProgressParser(new ProgressParser); command->addJob(arguments, -1); command->execute(); } @@ -3532,6 +3553,7 @@ void GitClient::interactiveRebase(const QString &workingDirectory, const QString m_disableEditor = true; VcsBase::Command *command = createCommand(workingDirectory, 0, true); new ConflictHandler(command, workingDirectory, QLatin1String("rebase")); + command->setProgressParser(new ProgressParser); command->addJob(arguments, -1); command->execute(); command->setCookie(workingDirectory); diff --git a/src/plugins/vcsbase/command.cpp b/src/plugins/vcsbase/command.cpp index 21701174483fbd760a77c23b47a73336a4d710be..57d36c3575c614c4761e8ec374b94fe1d6add2d0 100644 --- a/src/plugins/vcsbase/command.cpp +++ b/src/plugins/vcsbase/command.cpp @@ -37,6 +37,7 @@ #include <vcsbase/vcsbaseoutputwindow.h> #include <utils/synchronousprocess.h> #include <utils/runextensions.h> +#include <utils/qtcassert.h> #include <QDebug> #include <QProcess> @@ -48,11 +49,29 @@ #include <QVariant> #include <QStringList> #include <QTextCodec> +#include <QMutex> Q_DECLARE_METATYPE(QVariant) enum { debugExecution = 0 }; +/*! + \fn void VcsBase::ProgressParser::parseProgress(const QString &text) + + Reimplement to parse progress as it appears in the standard output. + If a progress string is detected, call \c setProgressAndMaximum() to update + the progress bar accordingly. + + \sa VcsBase::ProgressParser::setProgressAndMaximum() +*/ + +/*! + \fn void VcsBase::ProgressParser::setProgressAndMaximum(int value, int maximum) + + Sets progress \a value and \a maximum for current command. Called by \c parseProgress() + when a progress string is detected. +*/ + namespace VcsBase { namespace Internal { @@ -69,6 +88,7 @@ public: CommandPrivate(const QString &binary, const QString &workingDirectory, const QProcessEnvironment &environment); + ~CommandPrivate(); const QString m_binaryPath; const QString m_workingDirectory; @@ -78,6 +98,7 @@ public: unsigned m_flags; QTextCodec *m_codec; const QString m_sshPasswordPrompt; + ProgressParser *m_progressParser; QList<Job> m_jobs; @@ -95,11 +116,17 @@ CommandPrivate::CommandPrivate(const QString &binary, m_flags(0), m_codec(0), m_sshPasswordPrompt(VcsBasePlugin::sshPrompt()), + m_progressParser(0), m_lastExecSuccess(false), m_lastExecExitCode(-1) { } +CommandPrivate::~CommandPrivate() +{ + delete m_progressParser; +} + CommandPrivate::Job::Job(const QStringList &a, int t) : arguments(a), timeout(t) @@ -207,6 +234,8 @@ void Command::run(QFutureInterface<void> &future) QString stdOut; QString stdErr; + if (d->m_progressParser) + d->m_progressParser->setFuture(&future); const int count = d->m_jobs.size(); d->m_lastExecExitCode = -1; d->m_lastExecSuccess = true; @@ -233,6 +262,8 @@ void Command::run(QFutureInterface<void> &future) emit success(cookie()); } + if (d->m_progressParser) + d->m_progressParser->setFuture(0); // As it is used asynchronously, we need to delete ourselves this->deleteLater(); } @@ -342,10 +373,10 @@ Utils::SynchronousProcessResponse Command::runVcs(const QStringList &arguments, } // connect stdout to the output window if desired - if (d->m_flags & VcsBasePlugin::ShowStdOutInLogWindow) { - process.setStdOutBufferedSignalsEnabled(true); + process.setStdOutBufferedSignalsEnabled(true); + connect(&process, SIGNAL(stdOutBuffered(QString,bool)), this, SLOT(bufferedOutput(QString))); + if (d->m_flags & VcsBasePlugin::ShowStdOutInLogWindow) connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString))); - } process.setTimeOutMessageBoxEnabled(true); @@ -479,6 +510,12 @@ bool Command::runFullySynchronous(const QStringList &arguments, int timeoutMS, return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; } +void Command::bufferedOutput(const QString &text) +{ + if (d->m_progressParser) + d->m_progressParser->parseProgress(text); +} + const QVariant &Command::cookie() const { return d->m_cookie; @@ -499,6 +536,39 @@ void Command::setCodec(QTextCodec *codec) d->m_codec = codec; } +//! Use \a parser to parse progress data from stdout. Command takes ownership of \a parser +void Command::setProgressParser(ProgressParser *parser) +{ + QTC_ASSERT(!d->m_progressParser, return); + d->m_progressParser = parser; +} + +ProgressParser::ProgressParser() : + m_future(0), + m_futureMutex(new QMutex) +{ +} + +ProgressParser::~ProgressParser() +{ + delete m_futureMutex; +} + +void ProgressParser::setProgressAndMaximum(int value, int maximum) +{ + QMutexLocker lock(m_futureMutex); + if (!m_future) + return; + m_future->setProgressRange(0, maximum); + m_future->setProgressValue(value); +} + +void ProgressParser::setFuture(QFutureInterface<void> *future) +{ + QMutexLocker lock(m_futureMutex); + m_future = future; +} + } // namespace VcsBase #include "command.moc" diff --git a/src/plugins/vcsbase/command.h b/src/plugins/vcsbase/command.h index 84b4d7c398cd0e1881691479e0f8ee4818163ef1..7931cc974eec116edc3e4217c485f4ee222794e1 100644 --- a/src/plugins/vcsbase/command.h +++ b/src/plugins/vcsbase/command.h @@ -37,6 +37,7 @@ #include <QObject> QT_BEGIN_NAMESPACE +class QMutex; class QStringList; class QVariant; class QProcessEnvironment; @@ -48,6 +49,24 @@ namespace VcsBase { namespace Internal { class CommandPrivate; } +class VCSBASE_EXPORT ProgressParser +{ +public: + ProgressParser(); + virtual ~ProgressParser(); + +protected: + virtual void parseProgress(const QString &text) = 0; + void setProgressAndMaximum(int value, int maximum); + +private: + void setFuture(QFutureInterface<void> *future); + + QFutureInterface<void> *m_future; + QMutex *m_futureMutex; + friend class Command; +}; + class VCSBASE_EXPORT Command : public QObject { Q_OBJECT @@ -80,6 +99,7 @@ public: QTextCodec *codec() const; void setCodec(QTextCodec *codec); + void setProgressParser(ProgressParser *parser); Utils::SynchronousProcessResponse runVcs(const QStringList &arguments, int timeoutMS); // Make sure to not pass through the event loop at all: @@ -90,6 +110,9 @@ private: void run(QFutureInterface<void> &future); Utils::SynchronousProcessResponse runSynchronous(const QStringList &arguments, int timeoutMS); +private slots: + void bufferedOutput(const QString &text); + signals: void output(const QString &); void errorText(const QString &);