Palette: surface :-verbs as runnable M-x entries

The command palette now lists the :-verb pack alongside QActions: Sort Lines,
Sort Lines (unique), Reverse Lines, Case: UPPER/lower/Title/snake/camel/kebab,
Base64 Encode/Decode, ROT13, Insert UUID. Entries carry an ollie:cmd: id; on
activation runAction executes the command via
Editor::queryCommand(verb)->exec(view, cmd, msg). The free-text pipe verb
stays on the command line. All 6 ctest suites green.
This commit is contained in:
Levi Neely 2026-10-07 18:21:55 +02:00
parent 217d042a1b
commit e1bf0c9432
2 changed files with 61 additions and 3 deletions

View File

@ -179,8 +179,10 @@ Therefore the palette is replaced, not extended, and the replacement is built on
nothing is selected); `pipe <shell>` runs `sh -c <shell>` with the selection
on stdin and replaces it with stdout. 15 transform tests. The command object
is created once by `OlliePlugin` (auto-registers globally).
- Reachable from Kate's command line today; surfacing the verbs in the M-x
palette (they are `Command`s, not `QAction`s) is a follow-up.
- Also surfaced in the M-x palette as directly-runnable entries (`case` is
expanded into one entry per style). The palette id is prefixed `ollie:cmd:`
and executed via `Editor::queryCommand(verb)->exec(view, cmd, msg)`. The
free-text `pipe` verb stays on the command line only.
- Project = directory ("open folder == open project"): TODO.
- Fuzzy file / symbol switchers scoped to the folder: TODO.

View File

@ -11,6 +11,8 @@
#include <KTextEditor/MainWindow>
#include <KTextEditor/View>
#include <KTextEditor/Document>
#include <KTextEditor/Editor>
#include <KTextEditor/Command>
#include <KActionCollection>
#include <KXMLGUIClient>
@ -121,6 +123,36 @@ KTextEditor::Cursor findOpener(KTextEditor::Document *doc, KTextEditor::Cursor p
}
return KTextEditor::Cursor::invalid();
}
// Prefix marking a palette id that is a ":" command string to execute rather
// than a QAction to trigger.
const QLatin1String kCmdPrefix("ollie:cmd:");
// The complete (argument-free) command verbs offered in the palette. Verbs that
// need free-text arguments (pipe) stay on the command line; `case` is expanded
// into one entry per style so each is directly runnable.
struct VerbItem {
QString command; // the full command string to execute
QString label; // palette label
};
QList<VerbItem> paletteVerbs()
{
return {
{QStringLiteral("sort"), QStringLiteral("Sort Lines")},
{QStringLiteral("sort u"), QStringLiteral("Sort Lines (unique)")},
{QStringLiteral("rsort"), QStringLiteral("Reverse Lines")},
{QStringLiteral("case upper"), QStringLiteral("Case: UPPER")},
{QStringLiteral("case lower"), QStringLiteral("Case: lower")},
{QStringLiteral("case title"), QStringLiteral("Case: Title")},
{QStringLiteral("case snake"), QStringLiteral("Case: snake_case")},
{QStringLiteral("case camel"), QStringLiteral("Case: camelCase")},
{QStringLiteral("case kebab"), QStringLiteral("Case: kebab-case")},
{QStringLiteral("b64enc"), QStringLiteral("Base64 Encode")},
{QStringLiteral("b64dec"), QStringLiteral("Base64 Decode")},
{QStringLiteral("rot13"), QStringLiteral("ROT13")},
{QStringLiteral("uuid"), QStringLiteral("Insert UUID")},
};
}
} // namespace
// ---------------------------------------------------------------------------
@ -308,7 +340,7 @@ void OllieView::showCommandPalette()
m_actionsById.clear();
QList<PaletteItem> items;
items.reserve(entries.size());
items.reserve(entries.size() + 16);
for (const ActionEntry &e : entries) {
m_actionsById.insert(e.id, e.action);
@ -319,6 +351,15 @@ void OllieView::showCommandPalette()
items.push_back(item);
}
// Also offer the ":"-verb pack as directly-runnable palette entries.
for (const VerbItem &v : paletteVerbs()) {
PaletteItem item;
item.id = kCmdPrefix + v.command;
item.label = v.label;
item.group = QStringLiteral(":") + v.command;
items.push_back(item);
}
m_palette->setItems(items);
// Center the palette near the top of the active window.
@ -334,6 +375,21 @@ void OllieView::showCommandPalette()
void OllieView::runAction(const QString &actionId)
{
// A ":"-verb entry: execute the command on the active view.
if (actionId.startsWith(kCmdPrefix)) {
const QString command = actionId.mid(QString(kCmdPrefix).size());
KTextEditor::View *view = m_mainWindow->activeView();
KTextEditor::Editor *editor = KTextEditor::Editor::instance();
if (view && editor) {
const QString verb = command.section(QLatin1Char(' '), 0, 0);
if (KTextEditor::Command *c = editor->queryCommand(verb)) {
QString msg;
c->exec(view, command, msg);
}
}
return;
}
QPointer<QAction> action = m_actionsById.value(actionId);
if (action) {
action->trigger();