File indexing completed on 2024-05-12 04:38:56

0001 /*
0002     SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "vcsdiffpatchsources.h"
0008 
0009 #include <QHBoxLayout>
0010 #include <QVBoxLayout>
0011 #include <QDir>
0012 #include <QFontDatabase>
0013 #include <QLabel>
0014 #include <QTemporaryFile>
0015 
0016 #include <KComboBox>
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 #include <KMessageBox>
0020 #include <KTextEdit>
0021 
0022 #include <interfaces/ibasicversioncontrol.h>
0023 #include <interfaces/icore.h>
0024 #include <interfaces/iprojectcontroller.h>
0025 #include <interfaces/iplugincontroller.h>
0026 #include <interfaces/iruncontroller.h>
0027 #include <interfaces/isession.h>
0028 #include "vcsdiff.h"
0029 #include "vcsjob.h"
0030 #include "debug.h"
0031 
0032 
0033 using namespace KDevelop;
0034 
0035 VCSCommitDiffPatchSource::VCSCommitDiffPatchSource(VCSDiffUpdater* updater)
0036     : VCSDiffPatchSource(updater), m_vcs(updater->vcs())
0037 {
0038     Q_ASSERT(m_vcs);
0039     m_commitMessageWidget = new QWidget;
0040     auto* layout = new QVBoxLayout(m_commitMessageWidget.data());
0041     layout->setContentsMargins(0, 0, 0, 0);
0042 
0043     m_commitMessageEdit = new KTextEdit;
0044     m_commitMessageEdit.data()->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
0045     m_commitMessageEdit.data()->setLineWrapMode(QTextEdit::NoWrap);
0046     m_vcs->setupCommitMessageEditor(updater->url(), m_commitMessageEdit.data());
0047 
0048     auto* titleLayout = new QHBoxLayout;
0049     titleLayout->addWidget(new QLabel(i18nc("@label:textbox", "Commit message:")));
0050 
0051     m_oldMessages = new KComboBox(m_commitMessageWidget.data());
0052 
0053     m_oldMessages->addItem(i18n("Old Messages"));
0054     const auto oldMessages = this->oldMessages();
0055     for (const QString& message : oldMessages) {
0056         m_oldMessages->addItem(message, message);
0057     }
0058     m_oldMessages->setMaximumWidth(200);
0059 
0060     connect(m_oldMessages, &QComboBox::currentTextChanged,
0061             this, &VCSCommitDiffPatchSource::oldMessageChanged);
0062 
0063     titleLayout->addWidget(m_oldMessages);
0064 
0065     layout->addLayout(titleLayout);
0066     layout->addWidget(m_commitMessageEdit.data());
0067     connect(this, &VCSCommitDiffPatchSource::reviewCancelled, this, &VCSCommitDiffPatchSource::addMessageToHistory);
0068     connect(this, &VCSCommitDiffPatchSource::reviewFinished, this, &VCSCommitDiffPatchSource::addMessageToHistory);
0069 }
0070 
0071 QStringList VCSCommitDiffPatchSource::oldMessages() const
0072 {
0073     KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS");
0074     return vcsGroup.readEntry("OldCommitMessages", QStringList());
0075 }
0076 
0077 void VCSCommitDiffPatchSource::addMessageToHistory(const QString& message)
0078 {
0079     if(ICore::self()->shuttingDown())
0080         return;
0081 
0082     KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS");
0083 
0084     const int maxMessages = 10;
0085     QStringList oldMessages = vcsGroup.readEntry("OldCommitMessages", QStringList());
0086 
0087     oldMessages.removeAll(message);
0088     oldMessages.push_front(message);
0089     oldMessages = oldMessages.mid(0, maxMessages);
0090 
0091     vcsGroup.writeEntry("OldCommitMessages", oldMessages);
0092 }
0093 
0094 void VCSCommitDiffPatchSource::oldMessageChanged(const QString& text)
0095 {
0096     if(m_oldMessages->currentIndex() != 0)
0097     {
0098         m_oldMessages->setCurrentIndex(0);
0099         m_commitMessageEdit.data()->setText(text);
0100     }
0101 }
0102 
0103 void VCSCommitDiffPatchSource::jobFinished(KJob *job)
0104 {
0105     if (!job || job->error() != 0 )
0106     {
0107         QString details = job ? job->errorText() : QString();
0108         if (details.isEmpty()) {    //errorText may be empty
0109             details = i18n("For more detailed information please see the Version Control tool view.");
0110         }
0111         KMessageBox::detailedError(nullptr, i18n("Unable to commit"), details, i18nc("@title:window", "Commit Unsuccessful"));
0112     }
0113 
0114     deleteLater();
0115 }
0116 
0117 VCSDiffPatchSource::VCSDiffPatchSource(VCSDiffUpdater* updater)
0118     : m_updater(updater)
0119 {
0120     update();
0121     KDevelop::IBasicVersionControl* vcs = m_updater->vcs();
0122     QUrl url = m_updater->url();
0123 
0124     QScopedPointer<VcsJob> statusJob(vcs->status(QList<QUrl>() << url));
0125     QVariant varlist;
0126 
0127     if( statusJob->exec() && statusJob->status() == VcsJob::JobSucceeded )
0128     {
0129         varlist = statusJob->fetchResults();
0130 
0131         const auto vars = varlist.toList();
0132         m_infos.reserve(m_infos.size() + vars.size());
0133         for (const auto& var : vars) {
0134             VcsStatusInfo info = var.value<KDevelop::VcsStatusInfo>();
0135 
0136             m_infos += info;
0137             if(info.state()!=VcsStatusInfo::ItemUpToDate)
0138                 m_selectable[info.url()] = info.state();
0139         }
0140     }
0141     else
0142         qCDebug(VCS) << "Couldn't get status for urls: " << url;
0143 }
0144 
0145 VCSDiffPatchSource::VCSDiffPatchSource(const KDevelop::VcsDiff& diff)
0146     : m_updater(nullptr)
0147 {
0148     updateFromDiff(diff);
0149 }
0150 
0151 VCSDiffPatchSource::~VCSDiffPatchSource()
0152 {
0153     QFile::remove(m_file.toLocalFile());
0154     delete m_updater;
0155 }
0156 
0157 QUrl VCSDiffPatchSource::baseDir() const {
0158     return m_base;
0159 }
0160 
0161 QUrl VCSDiffPatchSource::file() const {
0162     return m_file;
0163 }
0164 
0165 QString VCSDiffPatchSource::name() const {
0166     return m_name;
0167 }
0168 
0169 uint VCSDiffPatchSource::depth() const {
0170     return m_depth;
0171 }
0172 
0173 void VCSDiffPatchSource::updateFromDiff(const VcsDiff& vcsdiff)
0174 {
0175     if(!m_file.isValid())
0176     {
0177         QTemporaryFile temp2(QDir::tempPath() + QLatin1String("/kdevelop_XXXXXX.patch"));
0178         temp2.setAutoRemove(false);
0179         temp2.open();
0180         QTextStream t2(&temp2);
0181         t2 << vcsdiff.diff();
0182         qCDebug(VCS) << "filename:" << temp2.fileName();
0183         m_file = QUrl::fromLocalFile(temp2.fileName());
0184         temp2.close();
0185     }else{
0186         QFile file(m_file.path());
0187         file.open(QIODevice::WriteOnly);
0188         QTextStream t2(&file);
0189         t2 << vcsdiff.diff();
0190     }
0191 
0192     qCDebug(VCS) << "using file" << m_file << vcsdiff.diff() << "base" << vcsdiff.baseDiff();
0193 
0194     m_name = QStringLiteral("VCS Diff");
0195     m_base = vcsdiff.baseDiff();
0196     m_depth = vcsdiff.depth();
0197 
0198     emit patchChanged();
0199 }
0200 
0201 void VCSDiffPatchSource::update() {
0202     if(!m_updater)
0203         return;
0204     updateFromDiff(m_updater->update());
0205 }
0206 
0207 VCSCommitDiffPatchSource::~VCSCommitDiffPatchSource() {
0208     delete m_commitMessageWidget.data();
0209 }
0210 
0211 bool VCSCommitDiffPatchSource::canSelectFiles() const {
0212     return true;
0213 }
0214 
0215 QMap< QUrl, KDevelop::VcsStatusInfo::State> VCSDiffPatchSource::additionalSelectableFiles() const {
0216     return m_selectable;
0217 }
0218 
0219 QWidget* VCSCommitDiffPatchSource::customWidget() const {
0220     return m_commitMessageWidget.data();
0221 }
0222 
0223 QString VCSCommitDiffPatchSource::finishReviewCustomText() const {
0224     return i18nc("@action:button To make a commit", "Commit");
0225 }
0226 
0227 bool VCSCommitDiffPatchSource::canCancel() const {
0228     return true;
0229 }
0230 
0231 void VCSCommitDiffPatchSource::cancelReview() {
0232 
0233     QString message;
0234 
0235     if (m_commitMessageEdit)
0236         message = m_commitMessageEdit.data()->toPlainText();
0237 
0238     emit reviewCancelled(message);
0239 
0240     deleteLater();
0241 }
0242 
0243 bool VCSCommitDiffPatchSource::finishReview(const QList<QUrl>& selection)
0244 {
0245     QString message;
0246 
0247     if (m_commitMessageEdit)
0248         message = m_commitMessageEdit.data()->toPlainText();
0249 
0250     qCDebug(VCS) << "Finishing with selection" << selection;
0251     QString files;
0252     files.reserve(selection.size());
0253     for (const QUrl& url : selection) {
0254         files += QLatin1String("<li>") + ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain) + QLatin1String("</li>");
0255     }
0256 
0257     QString text = i18n("<qt>Files will be committed:\n<ul>%1</ul>\nWith message:\n <pre>%2</pre></qt>", files, message);
0258 
0259     int res = KMessageBox::warningContinueCancel(nullptr, text, i18nc("@title:window", "About to Commit to Repository"),
0260                                                  KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
0261                                                  QStringLiteral("ShouldAskConfirmCommit"));
0262     if (res != KMessageBox::Continue) {
0263         return false;
0264     }
0265 
0266     emit reviewFinished(message, selection);
0267 
0268     VcsJob* job = m_vcs->commit(message, selection, KDevelop::IBasicVersionControl::NonRecursive);
0269     if (!job) {
0270         return false;
0271     }
0272 
0273     connect (job, &VcsJob::finished,
0274              this, &VCSCommitDiffPatchSource::jobFinished);
0275     ICore::self()->runController()->registerJob(job);
0276     return true;
0277 }
0278 
0279 bool showVcsDiff(IPatchSource* vcsDiff)
0280 {
0281     auto* patchReview = ICore::self()->pluginController()->extensionForPlugin<IPatchReview>(QStringLiteral("org.kdevelop.IPatchReview"));
0282 
0283     if( patchReview ) {
0284         patchReview->startReview(vcsDiff);
0285         return true;
0286     } else {
0287         qCWarning(VCS) << "Patch review plugin not found";
0288         return false;
0289     }
0290 }
0291 
0292 VcsDiff VCSStandardDiffUpdater::update() const
0293 {
0294     QScopedPointer<VcsJob> diffJob(m_vcs->diff(m_url,
0295                                    KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Base),
0296                                    KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Working)));
0297     const bool success = diffJob ? diffJob->exec() : false;
0298     if (!success) {
0299         KMessageBox::error(nullptr, i18n("Could not create a patch for the current version."));
0300         return {};
0301     }
0302 
0303     return diffJob->fetchResults().value<VcsDiff>();
0304 }
0305 
0306 VCSStandardDiffUpdater::VCSStandardDiffUpdater(IBasicVersionControl* vcs, const QUrl& url)
0307     : m_vcs(vcs)
0308     , m_url(url)
0309 {
0310 }
0311 
0312 VCSStandardDiffUpdater::~VCSStandardDiffUpdater() {
0313 }
0314 
0315 VCSDiffUpdater::~VCSDiffUpdater() {
0316 }
0317 
0318 #include "moc_vcsdiffpatchsources.cpp"