// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "vcsbasediffeditorcontroller.h" #include "vcsbaseclient.h" #include "vcscommand.h" #include #include #include #include #include #include #include using namespace DiffEditor; using namespace Core; using namespace Utils; namespace VcsBase { static void readPatch(QFutureInterface> &futureInterface, const QString &patch) { bool ok; const QList &fileDataList = DiffUtils::readPatch(patch, &ok, &futureInterface); futureInterface.reportResult(fileDataList); } ///////////////////// class VcsBaseDiffEditorControllerPrivate { public: VcsBaseDiffEditorControllerPrivate(VcsBaseDiffEditorController *q) : q(q) {} ~VcsBaseDiffEditorControllerPrivate(); void processingFinished(); void processDiff(const QString &patch); void cancelReload(); void commandFinished(bool success); VcsBaseDiffEditorController *q; FilePath m_directory; Environment m_processEnvironment; FilePath m_vcsBinary; int m_vscTimeoutS; QString m_startupFile; QString m_displayName; QPointer m_command; QFutureWatcher> *m_processWatcher = nullptr; }; VcsBaseDiffEditorControllerPrivate::~VcsBaseDiffEditorControllerPrivate() { delete m_command; cancelReload(); } void VcsBaseDiffEditorControllerPrivate::processingFinished() { QTC_ASSERT(m_processWatcher, return); // success is false when the user clicked the cancel micro button // inside the progress indicator const bool success = !m_processWatcher->future().isCanceled(); const QList fileDataList = success ? m_processWatcher->future().result() : QList(); // Prevent direct deletion of m_processWatcher since // processingFinished() is called directly by the m_processWatcher. m_processWatcher->deleteLater(); m_processWatcher = nullptr; q->setDiffFiles(fileDataList, q->workingDirectory(), q->startupFile()); q->reloadFinished(success); } void VcsBaseDiffEditorControllerPrivate::processDiff(const QString &patch) { cancelReload(); m_processWatcher = new QFutureWatcher>(); QObject::connect(m_processWatcher, &QFutureWatcherBase::finished, q, [this] { processingFinished(); }); m_processWatcher->setFuture(Utils::runAsync(&readPatch, patch)); ProgressManager::addTask(m_processWatcher->future(), VcsBaseDiffEditorController::tr("Processing diff"), "DiffEditor"); } void VcsBaseDiffEditorControllerPrivate::cancelReload() { m_command.clear(); if (m_processWatcher) { // Cancel the running process without the further processingFinished() // notification for this process. m_processWatcher->future().cancel(); delete m_processWatcher; m_processWatcher = nullptr; } } void VcsBaseDiffEditorControllerPrivate::commandFinished(bool success) { const QString output = m_command->cleanedStdOut(); // Don't delete here, as it is called from command finished signal. // Clear it only, as we may call runCommand() again from inside processCommandOutput overload. m_command.clear(); if (!success) { cancelReload(); q->reloadFinished(success); return; } q->processCommandOutput(output); } ///////////////////// VcsBaseDiffEditorController::VcsBaseDiffEditorController(Core::IDocument *document) : DiffEditorController(document) , d(new VcsBaseDiffEditorControllerPrivate(this)) {} VcsBaseDiffEditorController::~VcsBaseDiffEditorController() { delete d; } void VcsBaseDiffEditorController::runCommand(const QList &args, unsigned flags, QTextCodec *codec) { // Cancel the possible ongoing reload without the commandFinished() nor // processingFinished() notifications, as right after that // we re-reload it from scratch. So no intermediate "Retrieving data failed." // and "Waiting for data..." will be shown. delete d->m_command; d->cancelReload(); d->m_command = VcsBaseClient::createVcsCommand(workingDirectory(), d->m_processEnvironment); d->m_command->setDisplayName(d->m_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); }); d->m_command->addFlags(flags); for (const QStringList &arg : args) { QTC_ASSERT(!arg.isEmpty(), continue); d->m_command->addJob({d->m_vcsBinary, arg}, d->m_vscTimeoutS); } d->m_command->start(); } void VcsBaseDiffEditorController::processCommandOutput(const QString &output) { d->processDiff(output); } FilePath VcsBaseDiffEditorController::workingDirectory() const { return d->m_directory; } void VcsBaseDiffEditorController::setStartupFile(const QString &startupFile) { d->m_startupFile = startupFile; } 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; setBaseDirectory(workingDir); } void VcsBaseDiffEditorController::setVcsTimeoutS(int value) { d->m_vscTimeoutS = value; } void VcsBaseDiffEditorController::setVcsBinary(const FilePath &path) { d->m_vcsBinary = path; } void VcsBaseDiffEditorController::setProcessEnvironment(const Environment &value) { d->m_processEnvironment = value; } } // namespace VcsBase