From b8218878c5ba502902be20c6b560d6cd08eb65c0 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Thu, 23 Jul 2026 14:52:56 +0200 Subject: [PATCH] add diff action extension mechanism for external plugins Expose Q_PROPERTY lastDiff, signal diffShown(), and Q_INVOKABLE registerDiffAction() so other KTextEditor plugins (e.g. ollie) can register actions that appear in the tree view context menu. When a registered action is picked, computeDiffForFiles() runs git diff synchronously for the selected files before triggering the action, ensuring the consumer always gets a fresh diff regardless of whether the user previously viewed it. --- gitplusplus.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ gitplusplus.h | 18 ++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/gitplusplus.cpp b/gitplusplus.cpp index 2dc3853..2f53153 100644 --- a/gitplusplus.cpp +++ b/gitplusplus.cpp @@ -544,6 +544,16 @@ GitPlusPlusView::GitPlusPlusView(GitPlusPlusPlugin *plugin, KTextEditor::MainWin auto *diffAction = menu.addAction(QIcon::fromTheme(QStringLiteral("vcs-diff")), diffLabel); auto *openAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-open")), openLabel); + // Registered diff actions (e.g. "Ollie: Review Diff") — always available + QList proxyActions; + if (!m_diffActions.isEmpty()) { + menu.addSeparator(); + for (auto *a : m_diffActions) { + auto *proxy = menu.addAction(a->icon(), a->text()); + proxyActions.append(proxy); + } + } + auto *chosen = menu.exec(m_treeView->viewport()->mapToGlobal(pos)); if (chosen == diffAction) { diffSelected(); @@ -552,6 +562,10 @@ GitPlusPlusView::GitPlusPlusView(GitPlusPlusPlugin *plugin, KTextEditor::MainWin QString absPath = m_gitDir + QLatin1Char('/') + file; m_mainWindow->openUrl(QUrl::fromLocalFile(absPath)); } + } else if (int idx = proxyActions.indexOf(chosen); idx >= 0) { + // Compute diff for selected files, then trigger the registered action + computeDiffForFiles(files); + m_diffActions[idx]->trigger(); } }); @@ -694,8 +708,40 @@ void GitPlusPlusView::setMessage(const QString &text, bool isError) m_statusLabel->setStyleSheet(isError ? QStringLiteral("color: red;") : QStringLiteral("color: green;")); } +void GitPlusPlusView::registerDiffAction(QAction *action) +{ + if (action && !m_diffActions.contains(action)) { + m_diffActions.append(action); + connect(action, &QObject::destroyed, this, [this, action]() { + m_diffActions.removeOne(action); + }); + } +} + +void GitPlusPlusView::computeDiffForFiles(const QStringList &files) +{ + if (m_gitDir.isEmpty() || files.isEmpty()) return; + + // Compute unstaged diff + QStringList args = {QStringLiteral("diff"), QStringLiteral("--")}; + args.append(files); + QByteArray unstaged = GitProcess::run(m_gitDir, args); + + // Compute staged diff + QStringList cachedArgs = {QStringLiteral("diff"), QStringLiteral("--cached"), QStringLiteral("--")}; + cachedArgs.append(files); + QByteArray staged = GitProcess::run(m_gitDir, cachedArgs); + + m_lastDiff = staged + unstaged; + if (!m_lastDiff.isEmpty()) + Q_EMIT diffShown(m_lastDiff, QStringLiteral("Review: %1").arg(files.join(QStringLiteral(", ")))); +} + void GitPlusPlusView::showDiffOutput(const QByteArray &diff, const QString &title, const QStringList &gitArgs, int extraFlags) { + m_lastDiff = diff; + Q_EMIT diffShown(diff, title); + #ifdef HAVE_KATE_DIFF DiffParams params; params.tabTitle = title; diff --git a/gitplusplus.h b/gitplusplus.h index 3602a6c..dfc0e04 100644 --- a/gitplusplus.h +++ b/gitplusplus.h @@ -26,6 +26,7 @@ class QLabel; class QComboBox; class QCheckBox; class QTabWidget; +class QAction; class QSortFilterProxyModel; class QStringListModel; class QFileSystemWatcher; @@ -55,11 +56,23 @@ public: class GitPlusPlusView : public QObject { Q_OBJECT + Q_PROPERTY(QByteArray lastDiff READ lastDiff NOTIFY diffShown) public: explicit GitPlusPlusView(GitPlusPlusPlugin *plugin, KTextEditor::MainWindow *mainWindow); ~GitPlusPlusView() override; + QByteArray lastDiff() const { return m_lastDiff; } + + /** + * Register an external action to appear in the diff context menu. + * Ownership stays with the caller. + */ + Q_INVOKABLE void registerDiffAction(QAction *action); + +Q_SIGNALS: + void diffShown(const QByteArray &diff, const QString &title); + private Q_SLOTS: // Status void updateStatus(); @@ -128,6 +141,7 @@ private: void runSimpleGit(const QStringList &args, const QString &successMsg, const QString &errorMsg); void runRemoteGit(const QStringList &args, const QString &opName); void setMessage(const QString &text, bool isError = false); + void computeDiffForFiles(const QStringList &files); void showDiffOutput(const QByteArray &diff, const QString &title, const QStringList &gitArgs = {}, int extraFlags = 0); void updateIndicators(); void setRemoteBusy(bool busy, const QString &opName = {}); @@ -223,6 +237,10 @@ private: QString m_workspaceDir; // this workspace's directory QString m_workspaceSourceDir; // resolved source workspace path QStringList m_workspaceRepos; // repo subdirs in source workspace + + // Diff extension mechanism + QByteArray m_lastDiff; + QList m_diffActions; }; #endif // GITPLUSPLUS_H