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.
This commit is contained in:
Levi Neely 2026-07-23 14:52:56 +02:00
parent dcc5218ad7
commit b8218878c5
2 changed files with 64 additions and 0 deletions

View File

@ -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<QAction *> 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;

View File

@ -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<QAction *> m_diffActions;
};
#endif // GITPLUSPLUS_H