radial: add a conditional LSP submenu to the right-button radial

When Kate's LSP Client plugin is active, the right-button (editing) radial now
grows an 'LSP' branch: Go to Definition/Declaration/Type, Find References/
Implementations, Rename, Format, Code Action, Symbol Info, Hover.

The branch is built in showRadialAt() at open time, not in setupRadials(), so
it tracks the plugin being enabled/disabled without a restart. Each entry is
included only if its action currently resolves via findActionByName — the LSP
actions are referenced by their visible menu labels (the objectNames are not a
stable contract, the labels are) and the text-fallback resolver triggers them
like any other slice. When none resolve (LSP not loaded) the branch is empty and
the caller omits it, so nothing shows for non-LSP buffers.

Only the def with button == RightButton gets the branch (the editing radial),
not the middle-button window radial. 18/18 ctest green; deft_radial reinstalled.
This commit is contained in:
Levi Neely 2026-10-09 09:49:18 +02:00
parent efa1fe74c2
commit 36e9890ec7
3 changed files with 67 additions and 1 deletions

View File

@ -212,6 +212,12 @@ through a list.
- **Choose:** move toward a slice and release, or click it. A branch slice
(marked ▸) opens a submenu; the centre hub goes back / cancels.
- Slices can trigger any action by name, including `palette`/`switch` doors.
- **LSP branch (conditional).** When Kate's **LSP Client** plugin is active for
the document, the right-button radial grows an **LSP** submenu — Go to
Definition / Declaration / Type, Find References / Implementations, Rename,
Format, Code Action, Symbol Info, Hover. It is decided at open time (resolving
the actions live), so it appears only when a language server is backing the
buffer and is hidden entirely otherwise.
- **Pointer follows focus.** When a slice changes the active pane (split, close,
or focus-move), the pointer is warped to the centre of the new pane so the
mouse stays where your attention is — the same cursor-follows-focus feel as

View File

@ -25,6 +25,8 @@
#include <QWidget>
#include <QWindow>
#include <utility>
namespace deft
{
namespace
@ -143,7 +145,58 @@ void RadialView::showRadialAt(int defIndex, const QPoint &globalPos)
m_radial = new RadialMenu(m_mainWindow->window());
connect(m_radial, &RadialMenu::activatedId, this, &RadialView::onRadialActivated);
}
m_radial->popup(m_radialDefs.at(defIndex).root, globalPos);
// Start from the configured tree, then — for the right-button (editing)
// radial — splice in an "LSP" branch when Kate's LSP Client plugin is
// active. This is decided at *show* time, not at setup, so the branch
// appears and disappears as the user enables/disables the LSP plugin, and
// is hidden entirely when no language server is backing the document.
RadialNode root = m_radialDefs.at(defIndex).root;
if (m_radialDefs.at(defIndex).mouseButton == QLatin1String("RightButton")) {
if (RadialNode lsp = lspBranchIfActive(); !lsp.children.isEmpty()) {
root.children.push_back(lsp);
}
}
m_radial->popup(root, globalPos);
}
RadialNode RadialView::lspBranchIfActive() const
{
// The LSP actions, referenced by their visible menu text (findActionByName
// resolves by objectName first, then by label). We key off the labels
// because the LSP Client plugin's action objectNames are not a stable,
// documented contract, whereas the menu labels are user-facing and steady.
// An entry is included only when its action currently resolves — i.e. the
// LSP plugin is loaded in this window — so the whole branch is empty (and
// therefore omitted by the caller) when LSP is not active.
static const std::pair<const char *, const char *> kItems[] = {
{"Def", "Go to Definition"},
{"Decl", "Go to Declaration"},
{"Type", "Go to Type Definition"},
{"Refs", "Find References"},
{"Impl", "Find Implementations"},
{"Rename", "Rename"},
{"Format", "Format"},
{"Action", "Code Action"},
{"Info", "Symbol Info"},
{"Hover", "Show hover information"},
};
RadialNode branch;
branch.label = QStringLiteral("LSP");
for (const auto &[shortLabel, actionText] : kItems) {
const QString text = QString::fromLatin1(actionText);
if (findActionByName(text)) {
RadialNode leaf;
leaf.label = QString::fromLatin1(shortLabel);
// Store the resolvable identifier (the visible text); onRadialActivated
// re-resolves and triggers it the same way as any other slice.
leaf.actionId = text;
branch.children.push_back(leaf);
}
}
return branch;
}
void RadialView::onRadialActivated(const QString &actionId)

View File

@ -63,6 +63,13 @@ private:
void showRadial(int defIndex); // centre on the caret
void showRadialAt(int defIndex, const QPoint &globalPos); // centre on a point
// Build an "LSP" submenu of the language-server actions that currently
// resolve (i.e. Kate's LSP Client plugin is active for this window),
// referenced by their visible menu labels. Returns a branch with no
// children when LSP is not active, so the caller can omit it. Spliced into
// the right-button editing radial at show time.
RadialNode lspBranchIfActive() const;
// Find a Kate action by its raw objectName (then visible text) across all
// GUI clients. Resolves radial-slice ids, including the launcher actions
// that sibling deft plugins register on the window.