kate: add Ollie context menu to project tree view
Right-click on files/directories in Kate's project panel now shows Ollie actions: For directories: - Ask about directory (explain purpose/structure) - Start Session Here For files: - Ask about file (explain purpose/design) - Review file (code review) - Write tests Constructs file paths by walking up the tree model and joining with the project base directory.
This commit is contained in:
parent
2381ba4fbd
commit
a4c659f591
|
|
@ -172,6 +172,9 @@ OllieKateView::OllieKateView(OllieKatePlugin *plugin, KTextEditor::MainWindow *m
|
|||
if (auto *view = mainWindow->activeView())
|
||||
setupContextMenu(view);
|
||||
|
||||
// Project tree context menu
|
||||
setupProjectTreeContextMenu();
|
||||
|
||||
// Git++ "Review Diff" action (third-party plugin)
|
||||
setupGitPlusPlusDiffAction();
|
||||
|
||||
|
|
@ -542,6 +545,140 @@ void OllieKateView::onGitTreeContextMenu(const QPoint &)
|
|||
// Unused - context menu integration not possible without Kate modification
|
||||
}
|
||||
|
||||
void OllieKateView::setupProjectTreeContextMenu()
|
||||
{
|
||||
// Find the project tree view — it's inside the kateproject tool view
|
||||
QWidget *projectToolView = m_mainWindow->window()->findChild<QWidget *>(QStringLiteral("kateproject"));
|
||||
|
||||
if (!projectToolView) {
|
||||
// Schedule retry — plugin view might not be created yet
|
||||
QTimer::singleShot(2000, this, &OllieKateView::setupProjectTreeContextMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the tree view inside the project tool view
|
||||
auto *treeView = projectToolView->findChild<QTreeView *>();
|
||||
if (!treeView) {
|
||||
QTimer::singleShot(2000, this, &OllieKateView::setupProjectTreeContextMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid duplicate setup
|
||||
if (m_projectTreeView == treeView) {
|
||||
return;
|
||||
}
|
||||
m_projectTreeView = treeView;
|
||||
|
||||
// Enable custom context menu
|
||||
treeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(treeView, &QTreeView::customContextMenuRequested,
|
||||
this, &OllieKateView::onProjectTreeContextMenu);
|
||||
}
|
||||
|
||||
void OllieKateView::onProjectTreeContextMenu(const QPoint &pos)
|
||||
{
|
||||
if (!m_projectTreeView) return;
|
||||
|
||||
auto *model = m_projectTreeView->model();
|
||||
if (!model) return;
|
||||
|
||||
QModelIndex index = m_projectTreeView->indexAt(pos);
|
||||
if (!index.isValid()) return;
|
||||
|
||||
// Get the file path from the model
|
||||
// Kate's project model stores the path in UserRole or as a property
|
||||
QString filePath = model->data(index, Qt::UserRole).toString();
|
||||
if (filePath.isEmpty()) {
|
||||
// Try DisplayRole as fallback and construct path
|
||||
QString name = model->data(index, Qt::DisplayRole).toString();
|
||||
QString baseDir = projectBaseDir();
|
||||
if (!baseDir.isEmpty() && !name.isEmpty()) {
|
||||
// Walk up the tree to construct full path
|
||||
QStringList parts;
|
||||
parts.prepend(name);
|
||||
QModelIndex parent = index.parent();
|
||||
while (parent.isValid()) {
|
||||
parts.prepend(model->data(parent, Qt::DisplayRole).toString());
|
||||
parent = parent.parent();
|
||||
}
|
||||
// Remove the project root name (first element) and join
|
||||
if (!parts.isEmpty()) parts.removeFirst();
|
||||
filePath = baseDir + QStringLiteral("/") + parts.join(QStringLiteral("/"));
|
||||
}
|
||||
}
|
||||
|
||||
if (filePath.isEmpty()) return;
|
||||
|
||||
QFileInfo fi(filePath);
|
||||
bool isDir = fi.isDir();
|
||||
|
||||
QMenu menu(m_projectTreeView);
|
||||
|
||||
if (isDir) {
|
||||
auto *askAction = menu.addAction(QIcon::fromTheme(QStringLiteral("help-about")),
|
||||
QStringLiteral("Ollie: Ask about directory"));
|
||||
connect(askAction, &QAction::triggered, this, [this, filePath]() {
|
||||
ensureAgent();
|
||||
QString baseDir = projectBaseDir();
|
||||
QString relPath = filePath;
|
||||
if (!baseDir.isEmpty() && filePath.startsWith(baseDir)) {
|
||||
relPath = filePath.mid(baseDir.length() + 1);
|
||||
}
|
||||
QString prompt = QStringLiteral("Explain the purpose and structure of the directory: ") + relPath;
|
||||
m_9p->write(agentPath() + QStringLiteral("/prompt"), prompt.toUtf8());
|
||||
});
|
||||
|
||||
auto *startAction = menu.addAction(QIcon::fromTheme(QStringLiteral("system-run")),
|
||||
QStringLiteral("Ollie: Start Session Here"));
|
||||
connect(startAction, &QAction::triggered, this, [this, filePath]() {
|
||||
ensureAgent();
|
||||
QString prompt = QStringLiteral("I want to work on code in: ") + filePath;
|
||||
m_9p->write(agentPath() + QStringLiteral("/prompt"), prompt.toUtf8());
|
||||
});
|
||||
} else {
|
||||
auto *askAction = menu.addAction(QIcon::fromTheme(QStringLiteral("help-about")),
|
||||
QStringLiteral("Ollie: Ask about file"));
|
||||
connect(askAction, &QAction::triggered, this, [this, filePath]() {
|
||||
ensureAgent();
|
||||
QString baseDir = projectBaseDir();
|
||||
QString relPath = filePath;
|
||||
if (!baseDir.isEmpty() && filePath.startsWith(baseDir)) {
|
||||
relPath = filePath.mid(baseDir.length() + 1);
|
||||
}
|
||||
QString prompt = QStringLiteral("Explain the purpose and design of the file: ") + relPath;
|
||||
m_9p->write(agentPath() + QStringLiteral("/prompt"), prompt.toUtf8());
|
||||
});
|
||||
|
||||
auto *reviewAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
|
||||
QStringLiteral("Ollie: Review file"));
|
||||
connect(reviewAction, &QAction::triggered, this, [this, filePath]() {
|
||||
ensureAgent();
|
||||
QString baseDir = projectBaseDir();
|
||||
QString relPath = filePath;
|
||||
if (!baseDir.isEmpty() && filePath.startsWith(baseDir)) {
|
||||
relPath = filePath.mid(baseDir.length() + 1);
|
||||
}
|
||||
QString prompt = QStringLiteral("Review the code in ") + relPath + QStringLiteral(" for bugs, issues, and improvements.");
|
||||
m_9p->write(agentPath() + QStringLiteral("/prompt"), prompt.toUtf8());
|
||||
});
|
||||
|
||||
auto *testAction = menu.addAction(QIcon::fromTheme(QStringLiteral("system-run")),
|
||||
QStringLiteral("Ollie: Write tests"));
|
||||
connect(testAction, &QAction::triggered, this, [this, filePath]() {
|
||||
ensureAgent();
|
||||
QString baseDir = projectBaseDir();
|
||||
QString relPath = filePath;
|
||||
if (!baseDir.isEmpty() && filePath.startsWith(baseDir)) {
|
||||
relPath = filePath.mid(baseDir.length() + 1);
|
||||
}
|
||||
QString prompt = QStringLiteral("Write tests for the code in ") + relPath;
|
||||
m_9p->write(agentPath() + QStringLiteral("/prompt"), prompt.toUtf8());
|
||||
});
|
||||
}
|
||||
|
||||
menu.exec(m_projectTreeView->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void OllieKateView::onWidgetAdded(QWidget *widget)
|
||||
{
|
||||
// Check if this is a DiffWidget by looking for characteristic properties
|
||||
|
|
|
|||
|
|
@ -91,9 +91,11 @@ private:
|
|||
|
||||
// Context menu
|
||||
void setupContextMenu(KTextEditor::View *view);
|
||||
void setupProjectTreeContextMenu();
|
||||
void setupGitPlusPlusDiffAction();
|
||||
void setupProjectGitWidget();
|
||||
void onDiffWidgetContextMenu(QWidget *diffWidget, const QPoint &pos);
|
||||
void onProjectTreeContextMenu(const QPoint &pos);
|
||||
|
||||
// Ghost text
|
||||
void setupGhostProvider();
|
||||
|
|
@ -113,6 +115,7 @@ private:
|
|||
|
||||
// Git widget integration
|
||||
QPointer<QTreeView> m_gitTreeView;
|
||||
QPointer<QTreeView> m_projectTreeView;
|
||||
};
|
||||
|
||||
#endif // OLLIE_KATE_PLUGIN_H
|
||||
|
|
|
|||
Loading…
Reference in New Issue