kate: add Ollie button to git tool view instead of context menu

The GitWidget creates local QMenu objects that can't be intercepted.
Instead, add a QToolButton with dropdown menu to the git tool view:

- Review Staged Changes
- Review Unstaged Changes
- Explain Selected Files

This approach doesn't require Kate source modification.
This commit is contained in:
Levi Neely 2026-10-08 10:36:27 +02:00
parent c3782dd3b7
commit b745170898
2 changed files with 57 additions and 41 deletions

View File

@ -9,8 +9,6 @@
#include <QApplication>
#include <QCheckBox>
#include <QChildEvent>
#include <QContextMenuEvent>
#include <QDir>
#include <QFileInfo>
#include <QHash>
@ -20,6 +18,7 @@
#include <QPlainTextEdit>
#include <QProcess>
#include <QStatusBar>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>
@ -489,13 +488,41 @@ void OllieKateView::setupProjectGitWidget()
m_gitTreeView = treeView;
}
// Install event filter on the git tool view to intercept menus
gitToolView->installEventFilter(this);
// Add Ollie button to the git tool view's button row
// Find the layout and add our button
if (auto *layout = gitToolView->layout()) {
// Create a button for Ollie actions
auto *ollieBtn = new QToolButton(gitToolView);
ollieBtn->setObjectName(QStringLiteral("ollie_git_btn"));
ollieBtn->setIcon(QIcon::fromTheme(QStringLiteral("system-run")));
ollieBtn->setToolTip(QStringLiteral("Ollie AI Actions"));
ollieBtn->setPopupMode(QToolButton::InstantPopup);
ollieBtn->setAutoRaise(true);
auto *menu = new QMenu(ollieBtn);
menu->addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
QStringLiteral("Review Staged Changes"),
this, &OllieKateView::reviewStagedChanges);
menu->addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
QStringLiteral("Review Unstaged Changes"),
this, &OllieKateView::reviewUnstagedChanges);
menu->addAction(QIcon::fromTheme(QStringLiteral("help-about")),
QStringLiteral("Explain Selected Files"),
this, &OllieKateView::askAboutGitSelection);
ollieBtn->setMenu(menu);
// Try to insert at a reasonable position
if (auto *boxLayout = qobject_cast<QBoxLayout *>(layout)) {
boxLayout->insertWidget(0, ollieBtn);
} else {
layout->addWidget(ollieBtn);
}
}
}
void OllieKateView::onGitTreeContextMenu(const QPoint &)
{
// Unused - left for API compatibility
// Unused - context menu integration not possible without Kate modification
}
void OllieKateView::onWidgetAdded(QWidget *widget)
@ -573,19 +600,34 @@ void OllieKateView::reviewStagedChanges()
if (!git.waitForFinished(5000)) return;
QByteArray diff = git.readAllStandardOutput();
if (diff.trimmed().isEmpty()) {
// No staged changes, try unstaged
git.start(QStringLiteral("git"), {QStringLiteral("diff")});
if (!git.waitForFinished(5000)) return;
diff = git.readAllStandardOutput();
}
if (diff.trimmed().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")
QString prompt = QStringLiteral("Read-only task. Review the following staged 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")
+ QString::fromUtf8(diff) + QStringLiteral("\n```");
m_9p->write(agentPath() + "/prompt", prompt.toUtf8());
}
void OllieKateView::reviewUnstagedChanges()
{
QString baseDir = projectBaseDir();
if (baseDir.isEmpty()) return;
// Get unstaged diff via git
QProcess git;
git.setWorkingDirectory(baseDir);
git.start(QStringLiteral("git"), {QStringLiteral("diff")});
if (!git.waitForFinished(5000)) return;
QByteArray diff = git.readAllStandardOutput();
if (diff.trimmed().isEmpty()) return;
ensureAgent();
if (m_agentId.isEmpty()) return;
QString prompt = QStringLiteral("Read-only task. Review the following unstaged 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")
+ QString::fromUtf8(diff) + QStringLiteral("\n```");
m_9p->write(agentPath() + "/prompt", prompt.toUtf8());
}
@ -775,37 +817,10 @@ void OllieKateView::setupGhostProvider()
}
}
// --- Event Filter (ghost text key handling + git widget context menu) ---
// --- Event Filter (ghost text key handling) ---
bool OllieKateView::eventFilter(QObject *obj, QEvent *event)
{
// Watch for menus being created in the git tool view area
if (event->type() == QEvent::ChildAdded) {
auto *childEvent = static_cast<QChildEvent *>(event);
if (auto *menu = qobject_cast<QMenu *>(childEvent->child())) {
// Connect to aboutToShow to add our items
connect(menu, &QMenu::aboutToShow, this, [this, menu]() {
// Only add if we haven't already
bool hasOllieAction = false;
for (auto *action : menu->actions()) {
if (action->text().startsWith(QStringLiteral("Ollie:"))) {
hasOllieAction = true;
break;
}
}
if (!hasOllieAction && menu->actions().count() > 0) {
menu->addSeparator();
menu->addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
QStringLiteral("Ollie: Review Staged Changes"),
this, &OllieKateView::reviewStagedChanges);
menu->addAction(QIcon::fromTheme(QStringLiteral("help-about")),
QStringLiteral("Ollie: Ask About Selection"),
this, &OllieKateView::askAboutGitSelection);
}
}, Qt::UniqueConnection);
}
}
if (event->type() == QEvent::KeyPress && m_ghost) {
auto *keyEvent = static_cast<QKeyEvent *>(event);

View File

@ -74,6 +74,7 @@ private:
void reviewDiff();
void startSession();
void reviewStagedChanges();
void reviewUnstagedChanges();
void askAboutGitSelection();
void reviewDiffFromWidget(QWidget *diffWidget);