kate-deft/AGENTS.md

5.7 KiB

AGENTS.md

Working notes for anyone (human or agent) hacking on Deft, a suite of Kate / KTextEditor plugins. Read this before changing code.

What this repo is

Six independent KTextEditor plugins, one .so per feature, plus a few shared static libraries. Each plugin is a KTextEditor::Plugin with its own K_PLUGIN_FACTORY_WITH_JSON factory and .json metadata. Kate loads one plugin per .so.

.so display name source dir links
deft_palette [deft:util] palette src/palette/ palette, deftcommands_lib
deft_switch [deft:util] switch src/switch/ palette, project_lib
deft_radial [deft:util] radial src/radial/ radial
deft_acme [deft:nineify] acme src/acme/ self-contained
deft_sam [deft:nineify] sam src/sam/ sam_lib
deft_plumb [deft:nineify] plumb src/plumb/ plumb_lib

Shared static libs (headless, unit-tested in isolation): fuzzyranker, palette (model + widget + FrecencyStore), deftcommands_lib (text transforms + the :-verb KTextEditor::Command), project_lib (project/symbol index, ProjectIndexer, KateProjectBridge), sam_lib, plumb_lib.

Build / test / install

cmake -B build -S .                 # configure (defaults: RelWithDebInfo, prefix ~/.local)
cmake --build build -j              # build everything
QT_QPA_PLATFORM=offscreen ctest --test-dir build   # 18 tests, must stay green
cmake --install build               # installs the 6 .so to ~/.local/.../qt6/plugins/kf6/ktexteditor
  • Always build and run the full ctest after a change. Tests need QT_QPA_PLATFORM=offscreen (they create real KTextEditor docs/views).
  • The symbol-switcher path and its tests need ctags on PATH; those tests skip gracefully when it is absent.
  • Installing is wired for a user-local prefix: see CMakeLists.txt. It also drops ~/.config/plasma-workspace/env/99-deft.sh to put the install dir on QT_PLUGIN_PATH (generated from 99-deft.sh.in). Don't hand-roll install paths — KDE_INSTALL_PLUGINDIR is pointed at QT6_INSTALL_PLUGINS so plugins land where Kate scans.
  • An unqualified cmake -B build produces an optimized build on purpose (CMAKE_BUILD_TYPE=RelWithDebInfo); the palette re-rank and indexing are several times slower at -O0. See docs/PERF.md.

Conventions that matter

  • Namespace deft, Deft-prefixed names in all code. The project was once called Ollie; nothing should reintroduce Ollie/ollie in identifiers, config paths (~/.config/deft/…), or id prefixes (deft:cmd:, deft:file:, deft:sym:).
  • Match the existing style. No pointless indirection; file-local helpers duplicated across plugins are preferred over a shared lib for a few trivial functions.
  • Never read a file you haven't read, then rewrite from memory. Use targeted edits. The LSP bridge in this environment is unreliable; prefer the Tree-sitter (code_*) and file_* tools.

Two load-bearing design decisions

Changing either of these will break the suite in non-obvious ways.

1. Launcher keys are intercepted in an eventFilter, not registered shortcuts

The door keys — palette Alt+X, switch Alt+P/Alt+G — are not registered as QAction shortcuts. Kate already binds many Alt+<letter> combos; a registered duplicate makes Qt refuse to fire either and report an "ambiguous shortcut". So each view installs an application eventFilter, matches the key itself (scoped to its own window via isAncestorOf), and consumes it. The QActions still exist for their objectName/label and discoverability, just without a shortcut. plumb's F2 is the exception — it has no Kate collision, so it is a normal registered shortcut.

2. Cross-plugin calls go through action objectName, never direct calls

The plugins are separate .sos with no link dependency on each other. When palette offers "Go to File/Symbol", or radial has a launcher slice, it resolves the target by objectName at runtime and triggers it:

  • switch registers window actions deft_goto_file and deft_goto_symbol.
  • palette registers deft_command_palette.
  • findActionByName searches the window's GUI-factory client action collections and window()->actions() (sibling doors are added with QWidget::addAction, not via an XMLGUI client — so the window-actions search is required, not optional).

If you add a new cross-plugin entry point, register the action under a stable deft_* objectName and resolve it the same way. Never #include another plugin's header or add a link dependency between plugins.

Gotchas

  • The :-verb sort is registered as osort, not sort: the KTextEditor command registry drops an entire Command object if any one of its names collides with a built-in, and Kate ships sort. test_deftregistration guards against reintroducing a colliding name. runVerb still accepts bare sort for palette entries.
  • Each plugin keeps its own FrecencyStore file (deft/palette.frecency.json, deft/switch.frecency.json) — separate files, so two independent rankings can't clobber one another through a shared file.
  • Live Kate behaviour (plugin discovery, the Plasma env snippet pickup, actual gesture handling) can only be verified in a real KDE session. CI/sandbox runs cannot confirm it — flag anything that depends on it as unverified.

Reference docs

  • docs/PERF.md — switcher performance analysis and the fixes applied.
  • docs/PLUMBING.md — configuring the Plan 9 plumber to route into Kate.
  • docs/SAM.md — the sam structural-regexp dialect and semantics.
  • docs/radials.example.json — example radial-menu config.