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
ctestafter a change. Tests needQT_QPA_PLATFORM=offscreen(they create realKTextEditordocs/views). - The symbol-switcher path and its tests need
ctagsonPATH; 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.shto put the install dir onQT_PLUGIN_PATH(generated from99-deft.sh.in). Don't hand-roll install paths —KDE_INSTALL_PLUGINDIRis pointed atQT6_INSTALL_PLUGINSso plugins land where Kate scans. - An unqualified
cmake -B buildproduces an optimized build on purpose (CMAKE_BUILD_TYPE=RelWithDebInfo); the palette re-rank and indexing are several times slower at-O0. Seedocs/PERF.md.
Conventions that matter
- Namespace
deft,Deft-prefixed names in all code. The project was once called Ollie; nothing should reintroduceOllie/olliein 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_*) andfile_*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:
switchregisters window actionsdeft_goto_fileanddeft_goto_symbol.paletteregistersdeft_command_palette.findActionByNamesearches the window's GUI-factory client action collections andwindow()->actions()(sibling doors are added withQWidget::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 asosort, notsort: the KTextEditor command registry drops an entireCommandobject if any one of its names collides with a built-in, and Kate shipssort.test_deftregistrationguards against reintroducing a colliding name.runVerbstill accepts baresortfor palette entries. - Each plugin keeps its own
FrecencyStorefile (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.