kate-deft/src/plugin/ollieplugin.h

188 lines
7.6 KiB
C++

/*
* 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 <KTextEditor/Plugin>
#include "frecencystore.h"
#include "kateprojectbridge.h"
#include "projectindexer.h"
#include "palettemodel.h" // PaletteItem
#include "plumber.h"
#include "sampanel.h"
#include "waylandcursorwarp.h"
#include <QObject>
#include <QPointer>
#include <QHash>
#include <QList>
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<PaletteItem> buildFileItems(const QString &root, const QStringList &files) const;
QList<PaletteItem> buildSymbolItems(const QString &root, const QList<Symbol> &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<QAction> 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<ActionEntry> 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<RadialDef> m_radialDefs;
QHash<int, int> 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<QString, QPointer<QAction>> 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