From 36e9890ec7cadcd8a28a705c6c58135d53002a00 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 9 Oct 2026 09:49:18 +0200 Subject: [PATCH] radial: add a conditional LSP submenu to the right-button radial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- README.md | 6 ++++ src/radial/radialplugin.cpp | 55 ++++++++++++++++++++++++++++++++++++- src/radial/radialplugin.h | 7 +++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d784e88..209d82a 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/radial/radialplugin.cpp b/src/radial/radialplugin.cpp index e4a6968..1d6ca37 100644 --- a/src/radial/radialplugin.cpp +++ b/src/radial/radialplugin.cpp @@ -25,6 +25,8 @@ #include #include +#include + 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 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) diff --git a/src/radial/radialplugin.h b/src/radial/radialplugin.h index 564a039..45a92a2 100644 --- a/src/radial/radialplugin.h +++ b/src/radial/radialplugin.h @@ -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.