kate: simplify diff review to git-based actions

Remove complex DiffWidget detection and context menu injection.
Add 'Review Staged Changes' and 'Review Unstaged Changes' to editor
context menu when in a git repo. Uses git diff directly.

-119 lines of code removed.
This commit is contained in:
Levi Neely 2026-10-09 12:11:44 +02:00
parent f822bf6c32
commit 83cae21957
2 changed files with 15 additions and 134 deletions

View File

@ -16,7 +16,6 @@
#include <QKeyEvent>
#include <QMainWindow>
#include <QMenu>
#include <QPlainTextEdit>
#include <QProcess>
#include <QStatusBar>
#include <QToolButton>
@ -25,33 +24,6 @@
K_PLUGIN_CLASS_WITH_JSON(OllieKatePlugin, "metadata.json")
// Helper: detect if text looks like a unified diff
static bool looksLikeDiff(const QString &text)
{
if (text.isEmpty()) return false;
// Check for unified diff markers
return text.contains(QLatin1String("diff --git")) ||
text.contains(QLatin1String("@@ ")) ||
text.contains(QLatin1String("--- a/")) ||
text.contains(QLatin1String("+++ b/")) ||
(text.contains(QLatin1String("---")) && text.contains(QLatin1String("+++")));
}
// Helper: extract diff content from a widget tree (for Kate's DiffWidget)
static QString extractDiffFromWidget(QWidget *widget)
{
// Kate's DiffWidget uses DiffEditor (QPlainTextEdit subclass) children
// In Raw/Unified mode, one editor contains the actual diff
auto editors = widget->findChildren<QPlainTextEdit *>();
for (auto *editor : editors) {
QString text = editor->toPlainText();
if (looksLikeDiff(text)) {
return text;
}
}
return QString();
}
// --- Plugin ---
OllieKatePlugin::OllieKatePlugin(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
@ -207,10 +179,6 @@ OllieKateView::OllieKateView(OllieKatePlugin *plugin, KTextEditor::MainWindow *m
// Kate project plugin's built-in Git widget integration
setupProjectGitWidget();
// Hook into widget additions for diff widget integration
connect(mainWindow, &KTextEditor::MainWindow::widgetAdded,
this, &OllieKateView::onWidgetAdded);
}
OllieKateView::~OllieKateView()
@ -447,6 +415,20 @@ void OllieKateView::setupContextMenu(KTextEditor::View *view)
startAction->setObjectName(QStringLiteral("ollie_ctx_start"));
connect(startAction, &QAction::triggered, this, &OllieKateView::startSession);
// Git diff review actions (available when in a git repo)
QString baseDir = projectBaseDir();
if (!baseDir.isEmpty()) {
auto *reviewStagedAction = menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-diff")),
QStringLiteral("Ollie: Review Staged Changes"));
reviewStagedAction->setObjectName(QStringLiteral("ollie_ctx_review_staged"));
connect(reviewStagedAction, &QAction::triggered, this, &OllieKateView::reviewStagedChanges);
auto *reviewUnstagedAction = menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-diff")),
QStringLiteral("Ollie: Review Unstaged Changes"));
reviewUnstagedAction->setObjectName(QStringLiteral("ollie_ctx_review_unstaged"));
connect(reviewUnstagedAction, &QAction::triggered, this, &OllieKateView::reviewUnstagedChanges);
}
if (!v->selection()) return;
auto *explainAction = menu->addAction(QIcon::fromTheme(QStringLiteral("help-contents")),
@ -706,67 +688,6 @@ void OllieKateView::onProjectTreeContextMenu(const QPoint &pos)
menu.exec(m_projectTreeView->viewport()->mapToGlobal(pos));
}
void OllieKateView::onWidgetAdded(QWidget *widget)
{
if (!widget) return;
// Detect diff widgets by multiple heuristics:
// 1. Class name contains "Diff"
// 2. Has shouldClose() method (Kate's DiffWidget)
// 3. Object name contains "diff"
QString className = QString::fromLatin1(widget->metaObject()->className());
QString objName = widget->objectName();
qDebug() << "Ollie: widgetAdded:" << className << objName;
bool isDiffWidget = className.contains(QLatin1String("Diff"), Qt::CaseInsensitive) ||
objName.contains(QLatin1String("diff"), Qt::CaseInsensitive) ||
widget->metaObject()->indexOfMethod("shouldClose()") >= 0;
if (isDiffWidget) {
qDebug() << "Ollie: detected DiffWidget, installing event filter on editors";
// Store the diff widget for later use
m_currentDiffWidget = widget;
// Find DiffEditor children (QPlainTextEdit) and install event filter on them
auto editors = widget->findChildren<QPlainTextEdit *>();
qDebug() << "Ollie: found" << editors.size() << "QPlainTextEdit children";
for (auto *editor : editors) {
editor->installEventFilter(this);
}
}
}
void OllieKateView::onDiffWidgetContextMenu(QWidget *diffWidget, const QPoint &pos)
{
QMenu menu(diffWidget);
auto *reviewAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
QStringLiteral("Ollie: Review This Diff"));
connect(reviewAction, &QAction::triggered, this, [this, diffWidget]() {
reviewDiffFromWidget(diffWidget);
});
menu.exec(diffWidget->mapToGlobal(pos));
}
void OllieKateView::reviewDiffFromWidget(QWidget *diffWidget)
{
// Extract diff content from Kate's DiffWidget
// DiffWidget uses DiffEditor (QPlainTextEdit) children to display diff
QString diff = extractDiffFromWidget(diffWidget);
if (diff.isEmpty()) return;
ensureAgent();
if (m_agentId.isEmpty()) return;
QString prompt = QStringLiteral("Read-only task. Review the following diff. Do not edit files. Identify correctness bugs, regressions, security issues, missing tests, and maintainability concerns. For each finding, explain the impact and cite the relevant hunk. If there are no findings, say so explicitly.\n\n```diff\n")
+ diff + QStringLiteral("\n```");
m_9p->write(agentPath() + "/prompt", prompt.toUtf8());
}
void OllieKateView::reviewStagedChanges()
{
QString baseDir = projectBaseDir();
@ -996,46 +917,10 @@ void OllieKateView::setupGhostProvider()
}
}
// --- Event Filter (ghost text key handling + diff context menu) ---
// Helper to check if widget is inside a diff widget
static bool isInsideDiffWidget(QWidget *widget, QWidget *diffWidget)
{
QWidget *parent = widget->parentWidget();
while (parent) {
if (parent == diffWidget) return true;
parent = parent->parentWidget();
}
return false;
}
// --- Event Filter (ghost text key handling) ---
bool OllieKateView::eventFilter(QObject *obj, QEvent *event)
{
// Handle context menu for diff editors (QPlainTextEdit inside DiffWidget)
if (event->type() == QEvent::ContextMenu) {
auto *editor = qobject_cast<QPlainTextEdit *>(obj);
if (editor && m_currentDiffWidget && isInsideDiffWidget(editor, m_currentDiffWidget)) {
auto *contextEvent = static_cast<QContextMenuEvent *>(event);
// Create the standard context menu
QMenu *menu = editor->createStandardContextMenu();
// Add separator and our action at the end
menu->addSeparator();
auto *reviewAction = menu->addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
QStringLiteral("Ollie: Review This Diff"));
connect(reviewAction, &QAction::triggered, this, [this]() {
if (m_currentDiffWidget) {
reviewDiffFromWidget(m_currentDiffWidget);
}
});
menu->exec(contextEvent->globalPos());
delete menu;
return true;
}
}
if (event->type() == QEvent::KeyPress && m_ghost) {
auto *keyEvent = static_cast<QKeyEvent *>(event);

View File

@ -62,7 +62,6 @@ protected:
private slots:
void onProjectChanged();
void heartbeat();
void onWidgetAdded(QWidget *widget);
void onGitTreeContextMenu(const QPoint &pos);
public:
@ -81,7 +80,6 @@ private:
void reviewStagedChanges();
void reviewUnstagedChanges();
void askAboutGitSelection();
void reviewDiffFromWidget(QWidget *diffWidget);
// Agent naming
QString agentNameFromCwd(const QString &cwd);
@ -99,7 +97,6 @@ private:
void setupProjectTreeContextMenu();
void setupGitPlusPlusDiffAction();
void setupProjectGitWidget();
void onDiffWidgetContextMenu(QWidget *diffWidget, const QPoint &pos);
void onProjectTreeContextMenu(const QPoint &pos);
// Ghost text
@ -121,7 +118,6 @@ private:
// Git widget integration
QPointer<QTreeView> m_gitTreeView;
QPointer<QTreeView> m_projectTreeView;
QPointer<QWidget> m_currentDiffWidget;
};
#endif // OLLIE_KATE_PLUGIN_H