/* * SPDX-License-Identifier: LGPL-2.0-or-later * * The Ollie command-palette plugin for Kate / KTextEditor. * * OlliePlugin is the KTextEditor::Plugin entry point. For every MainWindow the * host creates an OllieView, which registers the "Command Palette (M-x)" action * (Alt+X) and, when triggered, gathers every QAction currently exposed by the * window's GUI clients into a PaletteWidget for orderless fuzzy selection. */ #ifndef KATECUSTOM_OLLIEPLUGIN_H #define KATECUSTOM_OLLIEPLUGIN_H #include #include "frecencystore.h" #include "kateprojectbridge.h" #include "projectindexer.h" #include "palettemodel.h" // PaletteItem #include "plumber.h" #include "sampanel.h" #include "waylandcursorwarp.h" #include #include #include #include class QAction; class QKeyEvent; namespace KTextEditor { class MainWindow; class View; class Document; } namespace katecustom { class PaletteWidget; class RadialMenu; class OllieCommands; struct RadialDef; class OlliePlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit OlliePlugin(QObject *parent, const QVariantList &args = {}); ~OlliePlugin() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; private: OllieCommands *m_commands = nullptr; // ":"-verb pack, registered once globally }; class OllieView : public QObject { Q_OBJECT public: OllieView(OlliePlugin *plugin, KTextEditor::MainWindow *mainWindow); ~OllieView() override; private Q_SLOTS: void showCommandPalette(); void runAction(const QString &actionId); void showFileSwitcher(); void openProjectFile(const QString &relativePath); void showSymbolSwitcher(); void goToSymbol(const QString &locationId); // Plumb the text under the caret (or the current selection) through the // plan9 plumber. Bound to F2 for testing. void plumbAtCursor(); // A plumb message arrived on the edit port: open \a file and jump to \a // addr (a plan9 edit-port address). Routed from the Plumber reader thread. void onPlumbEdit(const QString &file, const QString &addr, const QString &wdir); // Run a sam program from the panel. Dispatches to the multi-file X/Y driver // when the program begins with X or Y, else runs against the active doc. void runSamProgram(const QString &program); private: // Record one activation of \a id and persist. Feeds the frecency bonus so // habitual choices float up in every palette. void recordUsage(const QString &id); // The effective project root: Kate's loaded project (via the bridge) when // available, otherwise derived from the active document's directory. QString currentProjectRoot() const; // Directory of the active document, or empty when none/untitled. QString activeDocumentDir() const; // Build palette items from raw index results (shared by the cache-hit and // async-arrival paths). Root is used to project-qualify the frecency key. QList buildFileItems(const QString &root, const QStringList &files) const; QList buildSymbolItems(const QString &root, const QList &symbols) const; // Size and place a palette: it spans the active window's width (minus a // small margin), anchored near the top. void positionPalette(PaletteWidget *palette); // Move the OS pointer onto the center of the active view — used after a // radial pane-focus action so the mouse follows the keyboard focus. void warpCursorToActiveView(); // One harvested, runnable action with the provenance needed to build a // unique id and to disambiguate labels shared by several commands. struct ActionEntry { QPointer action; QString label; // visible text, ampersands stripped QString component; // owning collection's display name (provenance) QString shortcut; // current shortcut, native text QString id; // unique, stable key used on activation }; // Collect every unique, enabled, visible QAction reachable from the // window's GUI factory clients, deduplicated by pointer identity. QList collectActions() const; // Find a Kate action by its raw objectName across all GUI clients. Used to // resolve radial-menu slice ids (which reference Kate action names). QAction *findActionByName(const QString &objectName) const; // Build the configured radials and bind each to its trigger key. void setupRadials(); void showRadial(int defIndex); // centre on the caret void showRadialAt(int defIndex, const QPoint &globalPos); // centre on a point void onRadialActivated(const QString &actionId); // Acme-style line-editing keys, handled in the event filter (no QActions, // so there is nothing for Qt to report as an ambiguous shortcut). void acmeBeginningOfLine(); // C-a void acmeEndOfLine(); // C-e void acmeEraseChar(); // C-h void acmeEraseLine(); // C-u void acmeEraseWord(); // C-w // Dispatch an Acme key combo. Returns true if it was one of ours. bool dispatchAcmeKey(QKeyEvent *ke); // Catches configured mouse-button gestures inside the active view. bool eventFilter(QObject *obj, QEvent *event) override; // On double-click adjacent to a bracket, select the text between the // matching pair (multi-line aware). Returns true if a pair was selected. bool selectBracketPairAt(KTextEditor::View *view, const QPoint &globalPos); // Apply a sam program to a single document, returning the engine result. // Edits are applied inside one EditingTransaction (a single undo step). // \a doc must be non-null. Does not touch the panel. bool applySamToDocument(KTextEditor::Document *doc, const QString &program, int dotStart, int dotEnd, QString *output, QString *error); // Drive a multi-file X/Y sweep over the project's files, opening each match // and applying \a inner. Reports progress/errors to the panel. void runSamFileLoop(bool negate, const QString &fileRegex, const QString &inner); KTextEditor::MainWindow *m_mainWindow; PaletteWidget *m_palette = nullptr; PaletteWidget *m_filePalette = nullptr; // project "Go to File" switcher QString m_fileSwitcherRoot; // project root for the open switcher PaletteWidget *m_symbolPalette = nullptr; // project "Go to Symbol" switcher QString m_symbolSwitcherRoot; // project root for the symbol switcher RadialMenu *m_radial = nullptr; QList m_radialDefs; QHash m_buttonToRadial; // Qt::MouseButton (int) -> def index bool m_gestureActive = false; // an RMB drag gesture is in progress bool m_leftDownInView = false; // LMB held inside the view (Acme chord anchor) bool m_swallowMiddleRelease = false; // swallow the release paired with a consumed middle press bool m_swallowRightRelease = false; // swallow the release paired with a left+right paste chord QHash> m_actionsById; FrecencyStore m_frecency; // persisted usage feeding the palette ranking KateProjectBridge m_projectBridge; // read-only view of Kate's project plugin ProjectIndexer m_indexer; // async, cached file/symbol indexing WaylandCursorWarp m_cursorWarp; // pointer warp on Wayland (no-op on X11) Plumber m_plumber; // plan9 plumbing client (send + edit-port reader) SamPanel *m_samPanel = nullptr; // sam command tool view (owned by the host) QWidget *m_samToolView = nullptr; // the dockable container from createToolView }; } // namespace katecustom #endif