forked from qt-creator/qt-creator
DiffEditorController: Make it possible to setup task tree
Instead of reloader function. Use task tree for ShowController. Make diff processing a part of task tree. Change-Id: I732d0a14eaf8de7ce0d1be891fb4700b22ea24b7 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/editormanager/ieditor.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/progressmanager/taskprogress.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
@@ -122,12 +123,22 @@ void DiffEditorController::requestReload()
|
||||
{
|
||||
m_isReloading = true;
|
||||
m_document->beginReload();
|
||||
QTC_ASSERT(m_reloader, reloadFinished(false); return);
|
||||
if (m_reloader) {
|
||||
m_reloader();
|
||||
return;
|
||||
}
|
||||
m_taskTree.reset(new TaskTree(reloadRecipe()));
|
||||
connect(m_taskTree.get(), &TaskTree::done, this, [this] { reloadFinished(true); });
|
||||
connect(m_taskTree.get(), &TaskTree::errorOccurred, this, [this] { reloadFinished(false); });
|
||||
auto progress = new TaskProgress(m_taskTree.get());
|
||||
progress->setDisplayName(displayName());
|
||||
m_taskTree->start();
|
||||
}
|
||||
|
||||
void DiffEditorController::reloadFinished(bool success)
|
||||
{
|
||||
if (m_taskTree)
|
||||
m_taskTree.release()->deleteLater();
|
||||
m_document->endReload(success);
|
||||
m_isReloading = false;
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "diffutils.h"
|
||||
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/tasktree.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
@@ -57,6 +58,9 @@ signals:
|
||||
const ChunkSelection &selection);
|
||||
|
||||
protected:
|
||||
void setDisplayName(const QString &name) { m_displayName = name; }
|
||||
QString displayName() const { return m_displayName; }
|
||||
|
||||
// reloadFinished() should be called inside the reloader (for synchronous reload)
|
||||
// or later (for asynchronous reload)
|
||||
void setReloader(const std::function<void ()> &reloader);
|
||||
@@ -72,7 +76,10 @@ protected:
|
||||
private:
|
||||
Internal::DiffEditorDocument *const m_document;
|
||||
bool m_isReloading = false;
|
||||
QString m_displayName;
|
||||
std::function<void()> m_reloader;
|
||||
std::unique_ptr<Utils::TaskTree> m_taskTree;
|
||||
virtual Utils::Tasking::Group reloadRecipe() { return {}; } // TODO: make pure abstract
|
||||
|
||||
friend class Internal::DiffEditorDocument;
|
||||
};
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <coreplugin/iversioncontrol.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
|
||||
#include <utils/asynctask.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/checkablemessagebox.h>
|
||||
#include <utils/commandline.h>
|
||||
@@ -61,8 +62,6 @@
|
||||
#include <QToolButton>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <vector>
|
||||
|
||||
const char GIT_DIRECTORY[] = ".git";
|
||||
const char HEAD[] = "HEAD";
|
||||
const char CHERRY_PICK_HEAD[] = "CHERRY_PICK_HEAD";
|
||||
@@ -424,131 +423,171 @@ class ShowController : public GitBaseDiffEditorController
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShowController(IDocument *document, const QString &id) :
|
||||
GitBaseDiffEditorController(document, {}, {}),
|
||||
m_id(id),
|
||||
m_state(Idle)
|
||||
ShowController(IDocument *document, const QString &id)
|
||||
: GitBaseDiffEditorController(document, {}, {})
|
||||
, m_id(id)
|
||||
{
|
||||
setDisplayName("Git Show");
|
||||
setReloader([this] {
|
||||
m_state = GettingDescription;
|
||||
const QStringList args = {"show", "-s", noColorOption, showFormatC, m_id};
|
||||
runCommand({args}, m_instance->encoding(workingDirectory(), "i18n.commitEncoding"));
|
||||
setStartupFile(VcsBase::source(this->document()));
|
||||
});
|
||||
}
|
||||
~ShowController()
|
||||
{
|
||||
abortCommands();
|
||||
}
|
||||
|
||||
void processCommandOutput(const QString &output) override;
|
||||
|
||||
private:
|
||||
void processDescription(const QString &output);
|
||||
void updateDescription();
|
||||
void abortCommands();
|
||||
Tasking::Group reloadRecipe() final;
|
||||
const QString m_id;
|
||||
enum State { Idle, GettingDescription, GettingDiff };
|
||||
State m_state;
|
||||
};
|
||||
|
||||
Tasking::Group ShowController::reloadRecipe()
|
||||
{
|
||||
static const QString busyMessage = Tr::tr("<resolving>");
|
||||
using namespace Tasking;
|
||||
|
||||
struct ReloadStorage {
|
||||
bool m_postProcessDescription = false;
|
||||
QString m_commit;
|
||||
|
||||
QString m_header;
|
||||
QString m_body;
|
||||
QString m_precedes;
|
||||
std::vector<QString> m_follows;
|
||||
QList<QtcProcess *> m_commands;
|
||||
};
|
||||
QStringList m_follows;
|
||||
|
||||
void ShowController::processCommandOutput(const QString &output)
|
||||
{
|
||||
QTC_ASSERT(m_state != Idle, return);
|
||||
if (m_state == GettingDescription) {
|
||||
processDescription(output);
|
||||
// stage 2
|
||||
m_state = GettingDiff;
|
||||
const QStringList args = {"show", "--format=format:", // omit header, already generated
|
||||
noColorOption, decorateOption, m_id};
|
||||
runCommand(QList<QStringList>() << addConfigurationArguments(args));
|
||||
} else if (m_state == GettingDiff) {
|
||||
m_state = Idle;
|
||||
GitBaseDiffEditorController::processCommandOutput(output);
|
||||
}
|
||||
}
|
||||
QString m_diffOutput;
|
||||
};
|
||||
|
||||
void ShowController::processDescription(const QString &output)
|
||||
{
|
||||
abortCommands();
|
||||
if (!output.startsWith("commit ")) {
|
||||
setDescription(output);
|
||||
return;
|
||||
}
|
||||
QString modText = output;
|
||||
int lastHeaderLine = modText.indexOf("\n\n") + 1;
|
||||
m_header = output.left(lastHeaderLine) + Constants::EXPAND_BRANCHES + '\n';
|
||||
m_body = output.mid(lastHeaderLine + 1);
|
||||
m_precedes = Tr::tr("<resolving>");
|
||||
m_follows.push_back(m_precedes);
|
||||
updateDescription();
|
||||
const QString commit = modText.mid(7, 8);
|
||||
|
||||
QtcProcess *precedesProcess = new QtcProcess(this);
|
||||
m_commands.append(precedesProcess);
|
||||
precedesProcess->setEnvironment(m_instance->processEnvironment());
|
||||
precedesProcess->setCommand({m_instance->vcsBinary(), {"describe", "--contains", commit}});
|
||||
precedesProcess->setWorkingDirectory(workingDirectory());
|
||||
connect(precedesProcess, &QtcProcess::done, this, [this, precedesProcess] {
|
||||
m_precedes = precedesProcess->result() == ProcessResult::FinishedWithSuccess
|
||||
? precedesProcess->cleanedStdOut().trimmed() : QString();
|
||||
const int tilde = m_precedes.indexOf('~');
|
||||
if (tilde != -1)
|
||||
m_precedes.truncate(tilde);
|
||||
if (m_precedes.endsWith("^0"))
|
||||
m_precedes.chop(2);
|
||||
updateDescription();
|
||||
});
|
||||
precedesProcess->start();
|
||||
|
||||
QStringList parents;
|
||||
QString errorMessage;
|
||||
m_instance->synchronousParentRevisions(workingDirectory(), commit, &parents, &errorMessage);
|
||||
m_follows.resize(parents.size());
|
||||
for (int i = 0, total = parents.size(); i < total; ++i) {
|
||||
QtcProcess *followsProcess = new QtcProcess(this);
|
||||
m_commands.append(followsProcess);
|
||||
followsProcess->setEnvironment(m_instance->processEnvironment());
|
||||
followsProcess->setCommand({m_instance->vcsBinary(),
|
||||
{"describe", "--tags", "--abbrev=0", parents[i]}});
|
||||
followsProcess->setWorkingDirectory(workingDirectory());
|
||||
connect(followsProcess, &QtcProcess::done, this, [this, followsProcess, i] {
|
||||
if (followsProcess->result() != ProcessResult::FinishedWithSuccess)
|
||||
return;
|
||||
m_follows[i] = followsProcess->cleanedStdOut().trimmed();
|
||||
updateDescription();
|
||||
});
|
||||
followsProcess->start();
|
||||
}
|
||||
}
|
||||
|
||||
void ShowController::updateDescription()
|
||||
{
|
||||
QString desc = m_header;
|
||||
if (!m_precedes.isEmpty())
|
||||
desc.append("Precedes: " + m_precedes + '\n');
|
||||
const auto updateDescription = [this](const ReloadStorage &storage) {
|
||||
QString desc = storage.m_header;
|
||||
if (!storage.m_precedes.isEmpty())
|
||||
desc.append("Precedes: " + storage.m_precedes + '\n');
|
||||
QStringList follows;
|
||||
for (const QString &str : m_follows) {
|
||||
for (const QString &str : storage.m_follows) {
|
||||
if (!str.isEmpty())
|
||||
follows.append(str);
|
||||
}
|
||||
if (!follows.isEmpty())
|
||||
desc.append("Follows: " + follows.join(", ") + '\n');
|
||||
desc.append('\n' + m_body);
|
||||
|
||||
desc.append('\n' + storage.m_body);
|
||||
setDescription(desc);
|
||||
}
|
||||
};
|
||||
|
||||
void ShowController::abortCommands()
|
||||
{
|
||||
qDeleteAll(m_commands);
|
||||
m_commands.clear();
|
||||
const TreeStorage<ReloadStorage> storage;
|
||||
|
||||
const auto setupDescription = [this](QtcProcess &process) {
|
||||
process.setCodec(m_instance->encoding(workingDirectory(), "i18n.commitEncoding"));
|
||||
setStartupFile(VcsBase::source(this->document()));
|
||||
setupCommand(process, {"show", "-s", noColorOption, showFormatC, m_id});
|
||||
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
|
||||
setDescription(Tr::tr("Waiting for data..."));
|
||||
};
|
||||
const auto onDescriptionDone = [this, storage, updateDescription](const QtcProcess &process) {
|
||||
ReloadStorage *data = storage.activeStorage();
|
||||
const QString output = process.cleanedStdOut();
|
||||
data->m_postProcessDescription = output.startsWith("commit ");
|
||||
if (!data->m_postProcessDescription) {
|
||||
setDescription(output);
|
||||
return;
|
||||
}
|
||||
const int lastHeaderLine = output.indexOf("\n\n") + 1;
|
||||
data->m_commit = output.mid(7, 12);
|
||||
data->m_header = output.left(lastHeaderLine) + Constants::EXPAND_BRANCHES + '\n';
|
||||
data->m_body = output.mid(lastHeaderLine + 1);
|
||||
updateDescription(*data);
|
||||
};
|
||||
|
||||
const auto desciptionDetailsSetup = [storage] {
|
||||
if (!storage->m_postProcessDescription)
|
||||
return GroupConfig{GroupAction::StopWithDone};
|
||||
return GroupConfig();
|
||||
};
|
||||
|
||||
const auto setupPrecedes = [this, storage](QtcProcess &process) {
|
||||
storage->m_precedes = busyMessage;
|
||||
setupCommand(process, {"describe", "--contains", storage->m_commit});
|
||||
};
|
||||
const auto onPrecedesDone = [storage, updateDescription](const QtcProcess &process) {
|
||||
ReloadStorage *data = storage.activeStorage();
|
||||
data->m_precedes = process.cleanedStdOut().trimmed();
|
||||
const int tilde = data->m_precedes.indexOf('~');
|
||||
if (tilde != -1)
|
||||
data->m_precedes.truncate(tilde);
|
||||
if (data->m_precedes.endsWith("^0"))
|
||||
data->m_precedes.chop(2);
|
||||
updateDescription(*data);
|
||||
};
|
||||
const auto onPrecedesError = [storage, updateDescription](const QtcProcess &) {
|
||||
ReloadStorage *data = storage.activeStorage();
|
||||
data->m_precedes.clear();
|
||||
updateDescription(*data);
|
||||
};
|
||||
|
||||
const auto setupFollows = [this, storage, updateDescription](TaskTree &taskTree) {
|
||||
ReloadStorage *data = storage.activeStorage();
|
||||
QStringList parents;
|
||||
QString errorMessage;
|
||||
// TODO: it's trivial now to call below asynchonously, too
|
||||
m_instance->synchronousParentRevisions(workingDirectory(), data->m_commit,
|
||||
&parents, &errorMessage);
|
||||
data->m_follows = {busyMessage};
|
||||
data->m_follows.resize(parents.size());
|
||||
|
||||
const auto setupFollow = [this](QtcProcess &process, const QString &parent) {
|
||||
setupCommand(process, {"describe", "--tags", "--abbrev=0", parent});
|
||||
};
|
||||
const auto onFollowDone = [data, updateDescription](const QtcProcess &process, int index) {
|
||||
data->m_follows[index] = process.cleanedStdOut().trimmed();
|
||||
updateDescription(*data);
|
||||
};
|
||||
const auto onFollowsError = [data, updateDescription] {
|
||||
data->m_follows.clear();
|
||||
updateDescription(*data);
|
||||
};
|
||||
|
||||
using namespace std::placeholders;
|
||||
QList<TaskItem> tasks {parallel, continueOnDone, OnGroupError(onFollowsError)};
|
||||
for (int i = 0, total = parents.size(); i < total; ++i) {
|
||||
tasks.append(Process(std::bind(setupFollow, _1, parents.at(i)),
|
||||
std::bind(onFollowDone, _1, i)));
|
||||
}
|
||||
taskTree.setupRoot(tasks);
|
||||
};
|
||||
|
||||
const auto setupDiff = [this](QtcProcess &process) {
|
||||
setupCommand(process, addConfigurationArguments(
|
||||
{"show", "--format=format:", // omit header, already generated
|
||||
noColorOption, decorateOption, m_id}));
|
||||
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
|
||||
};
|
||||
const auto onDiffDone = [storage](const QtcProcess &process) {
|
||||
storage->m_diffOutput = process.cleanedStdOut();
|
||||
};
|
||||
|
||||
const auto setupProcessDiff = [this, storage](AsyncTask<QList<FileData>> &async) {
|
||||
setupDiffProcessor(async, storage->m_diffOutput);
|
||||
};
|
||||
const auto onProcessDiffDone = [this, storage](const AsyncTask<QList<FileData>> &async) {
|
||||
setDiffFiles(async.result(), workingDirectory(), startupFile());
|
||||
};
|
||||
const auto onProcessDiffError = [this, storage](const AsyncTask<QList<FileData>> &) {
|
||||
setDiffFiles({}, workingDirectory(), startupFile());
|
||||
};
|
||||
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
parallel,
|
||||
Group {
|
||||
optional,
|
||||
Process(setupDescription, onDescriptionDone),
|
||||
Group {
|
||||
parallel,
|
||||
optional,
|
||||
DynamicSetup(desciptionDetailsSetup),
|
||||
Process(setupPrecedes, onPrecedesDone, onPrecedesError),
|
||||
Tree(setupFollows)
|
||||
}
|
||||
},
|
||||
Group {
|
||||
Process(setupDiff, onDiffDone),
|
||||
Async<QList<FileData>>(setupProcessDiff, onProcessDiffDone, onProcessDiffError)
|
||||
}
|
||||
};
|
||||
return root;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
@@ -4,13 +4,17 @@
|
||||
#include "vcsbasediffeditorcontroller.h"
|
||||
#include "vcsbaseclient.h"
|
||||
#include "vcscommand.h"
|
||||
#include "vcsplugin.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
|
||||
#include <utils/asynctask.h>
|
||||
#include <utils/commandline.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/futuresynchronizer.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
#include <QPointer>
|
||||
@@ -21,8 +25,7 @@ using namespace Utils;
|
||||
|
||||
namespace VcsBase {
|
||||
|
||||
static void readPatch(QFutureInterface<QList<FileData>> &futureInterface,
|
||||
const QString &patch)
|
||||
static void readPatch(QFutureInterface<QList<FileData>> &futureInterface, const QString &patch)
|
||||
{
|
||||
bool ok;
|
||||
const QList<FileData> &fileDataList = DiffUtils::readPatch(patch, &ok, &futureInterface);
|
||||
@@ -48,7 +51,6 @@ public:
|
||||
FilePath m_vcsBinary;
|
||||
int m_vscTimeoutS;
|
||||
QString m_startupFile;
|
||||
QString m_displayName;
|
||||
QPointer<VcsCommand> m_command;
|
||||
QFutureWatcher<QList<FileData>> *m_processWatcher = nullptr;
|
||||
};
|
||||
@@ -134,6 +136,20 @@ VcsBaseDiffEditorController::~VcsBaseDiffEditorController()
|
||||
delete d;
|
||||
}
|
||||
|
||||
void VcsBaseDiffEditorController::setupCommand(QtcProcess &process, const QStringList &args) const
|
||||
{
|
||||
process.setEnvironment(d->m_processEnvironment);
|
||||
process.setWorkingDirectory(workingDirectory());
|
||||
process.setCommand({d->m_vcsBinary, args});
|
||||
}
|
||||
|
||||
void VcsBaseDiffEditorController::setupDiffProcessor(AsyncTask<QList<FileData>> &processor,
|
||||
const QString &patch) const
|
||||
{
|
||||
processor.setAsyncCallData(readPatch, patch);
|
||||
processor.setFutureSynchronizer(Internal::VcsPlugin::futureSynchronizer());
|
||||
}
|
||||
|
||||
void VcsBaseDiffEditorController::runCommand(const QList<QStringList> &args, RunFlags flags, QTextCodec *codec)
|
||||
{
|
||||
// Cancel the possible ongoing reload without the commandFinished() nor
|
||||
@@ -144,7 +160,7 @@ void VcsBaseDiffEditorController::runCommand(const QList<QStringList> &args, Run
|
||||
d->cancelReload();
|
||||
|
||||
d->m_command = VcsBaseClient::createVcsCommand(workingDirectory(), d->m_processEnvironment);
|
||||
d->m_command->setDisplayName(d->m_displayName);
|
||||
d->m_command->setDisplayName(displayName());
|
||||
d->m_command->setCodec(codec ? codec : EditorManager::defaultTextCodec());
|
||||
connect(d->m_command.data(), &VcsCommand::done, this, [this] {
|
||||
d->commandFinished(d->m_command->result() == ProcessResult::FinishedWithSuccess);
|
||||
@@ -180,11 +196,6 @@ QString VcsBaseDiffEditorController::startupFile() const
|
||||
return d->m_startupFile;
|
||||
}
|
||||
|
||||
void VcsBaseDiffEditorController::setDisplayName(const QString &displayName)
|
||||
{
|
||||
d->m_displayName = displayName;
|
||||
}
|
||||
|
||||
void VcsBase::VcsBaseDiffEditorController::setWorkingDirectory(const FilePath &workingDir)
|
||||
{
|
||||
d->m_directory = workingDir;
|
||||
|
@@ -15,8 +15,10 @@ QT_END_NAMESPACE
|
||||
namespace Core { class IDocument; }
|
||||
|
||||
namespace Utils {
|
||||
template <typename R> class AsyncTask;
|
||||
class Environment;
|
||||
class FilePath;
|
||||
class QtcProcess;
|
||||
} // Utils
|
||||
|
||||
namespace VcsBase {
|
||||
@@ -37,13 +39,15 @@ public:
|
||||
void setWorkingDirectory(const Utils::FilePath &workingDir);
|
||||
|
||||
protected:
|
||||
void setupCommand(Utils::QtcProcess &process, const QStringList &args) const;
|
||||
void setupDiffProcessor(Utils::AsyncTask<QList<DiffEditor::FileData>> &processor,
|
||||
const QString &patch) const;
|
||||
void runCommand(const QList<QStringList> &args, RunFlags flags, QTextCodec *codec = nullptr);
|
||||
virtual void processCommandOutput(const QString &output);
|
||||
|
||||
Utils::FilePath workingDirectory() const;
|
||||
void setStartupFile(const QString &startupFile);
|
||||
QString startupFile() const;
|
||||
void setDisplayName(const QString &displayName);
|
||||
|
||||
private:
|
||||
friend class VcsBaseDiffEditorControllerPrivate;
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projecttree.h>
|
||||
|
||||
#include <utils/futuresynchronizer.h>
|
||||
#include <utils/macroexpander.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
@@ -39,6 +40,7 @@ class VcsPluginPrivate
|
||||
public:
|
||||
CommonOptionsPage m_settingsPage;
|
||||
QStandardItemModel *m_nickNameModel = nullptr;
|
||||
FutureSynchronizer m_futureSynchronizer;
|
||||
};
|
||||
|
||||
static VcsPlugin *m_instance = nullptr;
|
||||
@@ -125,6 +127,12 @@ CommonVcsSettings &VcsPlugin::settings() const
|
||||
return d->m_settingsPage.settings();
|
||||
}
|
||||
|
||||
FutureSynchronizer *VcsPlugin::futureSynchronizer()
|
||||
{
|
||||
QTC_ASSERT(m_instance, return nullptr);
|
||||
return &m_instance->d->m_futureSynchronizer;
|
||||
}
|
||||
|
||||
/* Delayed creation/update of the nick name model. */
|
||||
QStandardItemModel *VcsPlugin::nickNameModel()
|
||||
{
|
||||
|
@@ -11,6 +11,8 @@ QT_BEGIN_NAMESPACE
|
||||
class QStandardItemModel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils { class FutureSynchronizer; }
|
||||
|
||||
namespace VcsBase {
|
||||
|
||||
class VcsBaseSubmitEditor;
|
||||
@@ -34,6 +36,8 @@ public:
|
||||
|
||||
CommonVcsSettings &settings() const;
|
||||
|
||||
static Utils::FutureSynchronizer *futureSynchronizer();
|
||||
|
||||
// Model of user nick names used for the submit
|
||||
// editor. Stored centrally here to achieve delayed
|
||||
// initialization and updating on settings change.
|
||||
|
Reference in New Issue
Block a user