diff --git a/kdevplatform/vcs/interfaces/ibasicversioncontrol.h b/kdevplatform/vcs/interfaces/ibasicversioncontrol.h --- a/kdevplatform/vcs/interfaces/ibasicversioncontrol.h +++ b/kdevplatform/vcs/interfaces/ibasicversioncontrol.h @@ -186,10 +186,21 @@ const QList& localLocations, RecursionMode recursion = IBasicVersionControl::Recursive ) = 0; + /** + * change the number of lines of context the patch generated by diff() should show + * + * May not be supported by all VCS plugins. + * @param contextLines The number of context lines included before and after each + * hunk in a unified diff. Special values are -1 for the VCS default (usually 3) + * and 0 for the largest possible value (intended to include the whole file as context). + */ + virtual void setDiffContextLines(int contextLines) { m_contextLines = contextLines == 0 ? INT_MAX : contextLines; } + /** * Retrieves a diff between two revisions of a file * - * The diff is in unified diff format for text files. + * The diff is in unified diff format for text files and may + * use the context size set through setDiffContextLines. */ virtual VcsJob* diff( const QUrl& fileOrDirectory, const VcsRevision& srcRevision, @@ -270,6 +281,8 @@ * The default implementation does nothing. */ virtual void setupCommitMessageEditor(const QUrl&, KTextEdit* edit) const; +protected: + int m_contextLines = -1; }; } diff --git a/kdevplatform/vcs/interfaces/ipatchsource.h b/kdevplatform/vcs/interfaces/ipatchsource.h --- a/kdevplatform/vcs/interfaces/ipatchsource.h +++ b/kdevplatform/vcs/interfaces/ipatchsource.h @@ -49,6 +49,9 @@ ///should re-compare the files or whatever needs to be done ///If the patch has changed, patchChanged needs to be emitted virtual void update() = 0; + ///change the number of lines of context the patch should show + ///virtual so it can be overridden, for instance to call update(). + virtual void setContextLines(int contextLines) { m_contextLines = contextLines; } ///Name of the patch file virtual QUrl file() const = 0; @@ -92,6 +95,8 @@ Q_SIGNALS: ///Should be emitted whenever the patch has changed. void patchChanged(); + protected: + int m_contextLines = -1; }; class KDEVPLATFORMVCS_EXPORT IPatchReview diff --git a/kdevplatform/vcs/widgets/vcsdiffpatchsources.h b/kdevplatform/vcs/widgets/vcsdiffpatchsources.h --- a/kdevplatform/vcs/widgets/vcsdiffpatchsources.h +++ b/kdevplatform/vcs/widgets/vcsdiffpatchsources.h @@ -48,6 +48,13 @@ virtual KDevelop::VcsDiff update() const = 0; virtual KDevelop::IBasicVersionControl* vcs() const = 0; virtual QUrl url() const = 0; + void setContextLines(int n) + { + m_contextLines = n; + } + int contextLines() { return m_contextLines; } +protected: + int m_contextLines=-1; }; class KDEVPLATFORMVCS_EXPORT VCSStandardDiffUpdater : public VCSDiffUpdater { diff --git a/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp b/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp --- a/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp +++ b/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp @@ -242,6 +242,7 @@ void VCSDiffPatchSource::update() { if(!m_updater) return; + m_updater->setContextLines(m_contextLines == 0? INT_MAX : m_contextLines); updateFromDiff(m_updater->update()); } @@ -336,6 +337,7 @@ VcsDiff VCSStandardDiffUpdater::update() const { + m_vcs->setDiffContextLines(m_contextLines); QScopedPointer diffJob(m_vcs->diff(m_url, KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Base), KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Working))); diff --git a/plugins/bazaar/bazaarplugin.cpp b/plugins/bazaar/bazaarplugin.cpp --- a/plugins/bazaar/bazaarplugin.cpp +++ b/plugins/bazaar/bazaarplugin.cpp @@ -124,6 +124,7 @@ VcsJob* BazaarPlugin::diff(const QUrl& fileOrDirectory, const VcsRevision& srcRevision, const VcsRevision& dstRevision, IBasicVersionControl::RecursionMode recursion) { Q_UNUSED(recursion); + // TODO support m_contextLines (less urgent because Phabricator doesn't support Bazaar) VcsJob* job = new DiffJob(BazaarUtils::workingCopy(fileOrDirectory), BazaarUtils::getRevisionSpecRange(srcRevision, dstRevision), fileOrDirectory, this); return job; } diff --git a/plugins/git/gitplugin.cpp b/plugins/git/gitplugin.cpp --- a/plugins/git/gitplugin.cpp +++ b/plugins/git/gitplugin.cpp @@ -391,12 +391,15 @@ { DVcsJob* job = new GitJob(dotGitDirectory(fileOrDirectory), this, KDevelop::OutputJob::Silent); job->setType(VcsJob::Diff); - *job << "git" << "diff" << "--no-color" << "--no-ext-diff"; + *job << "git" << "diff" << "--no-color" << "--no-ext-diff" << "--full-index"; if (!usePrefix()) { // KDE's ReviewBoard now requires p1 patchfiles, so `git diff --no-prefix` to generate p0 patches // has become optional. *job << "--no-prefix"; } + if (m_contextLines > 0) { + *job << QStringLiteral("-U%1").arg(m_contextLines); + } if (dstRevision.revisionType() == VcsRevision::Special && dstRevision.specialType() == VcsRevision::Working) { if (srcRevision.revisionType() == VcsRevision::Special && @@ -417,6 +420,7 @@ } else { *job << preventRecursion(QList() << fileOrDirectory); } + qWarning() << "git diff job:" << job->dvcsCommand(); connect(job, &DVcsJob::readyForParsing, this, &GitPlugin::parseGitDiffOutput); return job; diff --git a/plugins/patchreview/patchreview.h b/plugins/patchreview/patchreview.h --- a/plugins/patchreview/patchreview.h +++ b/plugins/patchreview/patchreview.h @@ -99,6 +99,7 @@ void highlightPatch(); void updateKompareModel(); void forceUpdate(); + void forceUpdateWithContext(int contextLines); void areaChanged(Sublime::Area* area); void executeFileReviewAction(); diff --git a/plugins/patchreview/patchreview.cpp b/plugins/patchreview/patchreview.cpp --- a/plugins/patchreview/patchreview.cpp +++ b/plugins/patchreview/patchreview.cpp @@ -224,6 +224,17 @@ } } +void PatchReviewPlugin::forceUpdateWithContext(int contextLines) { + if( m_patch ) { + // register the requested number of lines of context + // and regenerate the patch. + m_patch->setContextLines(contextLines); + forceUpdate(); + } else { + qWarning() << Q_FUNC_INFO << "called while m_patch==" << m_patch; + } +} + void PatchReviewPlugin::updateKompareModel() { if ( !m_patch ) { ///TODO: this method should be cleaned up, it can be called by the timer and diff --git a/plugins/patchreview/patchreview.ui b/plugins/patchreview/patchreview.ui --- a/plugins/patchreview/patchreview.ui +++ b/plugins/patchreview/patchreview.ui @@ -72,8 +72,34 @@ + + + + lines of context for the unified diff + + + max + + + + + + 0 + + + 999 + + + 3 + + + + + Click to update the patch +Right-click to select the number of lines of context the patch should show. + Update diff --git a/plugins/patchreview/patchreviewtoolview.h b/plugins/patchreview/patchreviewtoolview.h --- a/plugins/patchreview/patchreviewtoolview.h +++ b/plugins/patchreview/patchreviewtoolview.h @@ -30,6 +30,7 @@ class LocalPatchSource; class QModelIndex; class QSortFilterProxyModel; +class DiffContextMenu; class PatchReviewToolView : public QWidget { @@ -99,6 +100,8 @@ class PatchFilesModel* m_fileModel; QSortFilterProxyModel* m_fileSortProxyModel; + DiffContextMenu *m_diffContextMenu; + public Q_SLOTS: void documentActivated( KDevelop::IDocument* ); void customContextMenuRequested(const QPoint& p); diff --git a/plugins/patchreview/patchreviewtoolview.cpp b/plugins/patchreview/patchreviewtoolview.cpp --- a/plugins/patchreview/patchreviewtoolview.cpp +++ b/plugins/patchreview/patchreviewtoolview.cpp @@ -40,6 +40,11 @@ #include #include +#include +#include +#include +#include + #include #include #include @@ -52,6 +57,76 @@ using namespace KDevelop; +class DiffContextMenu : public QObject +{ + Q_OBJECT +public: + DiffContextMenu(PatchReviewPlugin *prPlugin) + : QObject(dynamic_cast(prPlugin)) + , m_prPlugin(prPlugin) + { + m_contextActions = new QActionGroup(this); + m_contextActions->setExclusive(true); + QAction *title = new QAction(i18n("diff context"), m_contextActions); + title->setEnabled(false); + m_contextActions->addAction(title); + for (int i = 1; i <= 5; ++i) { + addContextAction(i); + } + addContextAction(10); + addContextAction(0); + } + + void addContextAction(int contextLines) + { + QAction *action; + if (contextLines == 0) { + action = new QAction(i18n("whole file"), m_contextActions); + } else if (contextLines > 0) { + action = new QAction(i18n("%1 lines").arg(contextLines), m_contextActions); + } else { + return; + } + action->setData(contextLines); + action->setCheckable(true); + connect(action, &QAction::triggered, this, [action, this]() { + action->setChecked(true); + m_prPlugin->forceUpdateWithContext(action->data().toInt()); + }); + m_contextActions->addAction(action); + if (contextLines == 3) { + m_defaultContextAction = action; + action->setChecked(true); + } + } + + bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *gEvent = static_cast(event); + if (QTapAndHoldGesture *heldTap = static_cast(gEvent->gesture(Qt::TapAndHoldGesture))) { + if (heldTap->state() == Qt::GestureFinished) { + QPushButton *btn = dynamic_cast(obj); + if (btn) { + // user clicked and held a widget, send it a simulated ContextMenuEvent: + QContextMenuEvent ce(QContextMenuEvent::Mouse, heldTap->position().toPoint(), + heldTap->hotSpot().toPoint()); + gEvent->accept(); + int ret = QCoreApplication::sendEvent(btn, &ce); + btn->setDown(false); + return ret; + } + } + } + } + return false; + } + + QActionGroup *m_contextActions; + PatchReviewPlugin *m_prPlugin; + QAction *m_defaultContextAction; +}; + class PatchFilesModel : public VcsFileChangesModel { Q_OBJECT @@ -129,6 +204,12 @@ } void PatchReviewToolView::patchChanged() { + if(m_resetCheckedUrls) { + // set the default number of context lines + m_editPatch.diffContext->setValue(3); + m_diffContextMenu->m_defaultContextAction->setChecked(true); + } + fillEditFromPatch(); kompareModelChanged(); @@ -252,6 +333,16 @@ //connect( this, SIGNAL(finished(int)), this, SLOT(slotEditDialogFinished(int)) ); connect( m_editPatch.updateButton, &QPushButton::clicked, m_plugin, &PatchReviewPlugin::forceUpdate ); + m_editPatch.updateButton->setContextMenuPolicy(Qt::ActionsContextMenu); + m_diffContextMenu = new DiffContextMenu(m_plugin); + m_editPatch.updateButton->addActions(m_diffContextMenu->m_contextActions->actions()); + // make it possible to open the context menu with a click-and-hold: + m_editPatch.updateButton->installEventFilter(m_diffContextMenu); + m_editPatch.updateButton->grabGesture(Qt::TapAndHoldGesture); + // FIXME: cleanup after deciding on the GUI + m_editPatch.diffContext->hide(); +// connect( m_editPatch.diffContext, static_cast(&QSpinBox::valueChanged), +// m_plugin, &PatchReviewPlugin::forceUpdateWithContext ); connect( m_editPatch.testsButton, &QPushButton::clicked, this, &PatchReviewToolView::runTests ); diff --git a/plugins/perforce/perforceplugin.cpp b/plugins/perforce/perforceplugin.cpp --- a/plugins/perforce/perforceplugin.cpp +++ b/plugins/perforce/perforceplugin.cpp @@ -320,7 +320,11 @@ case VcsRevision::Special: switch (dstRevision.revisionValue().value()) { case VcsRevision::Working: - *job << m_perforceExecutable << "diff" << "-du" << depotSrcFileName; + if (m_contextLines > 0) { + *job << m_perforceExecutable << "diff" << "-dU" << QString::number(m_contextLines) << depotSrcFileName; + } else { + *job << m_perforceExecutable << "diff" << "-du" << depotSrcFileName; + } break; case VcsRevision::Start: case VcsRevision::UserSpecialType: diff --git a/plugins/subversion/kdevsvnplugin.cpp b/plugins/subversion/kdevsvnplugin.cpp --- a/plugins/subversion/kdevsvnplugin.cpp +++ b/plugins/subversion/kdevsvnplugin.cpp @@ -235,6 +235,7 @@ const KDevelop::VcsRevision& dstRevision, KDevelop::IBasicVersionControl::RecursionMode recurse) { + // TODO: support custom context lines in SvnDiffJob auto* job = new SvnDiffJob(this); job->setSource(src); job->setDestination(dst);