# 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 ```sh 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`. ## 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 rewrite a file from memory.** Read it first, then make targeted edits. ## 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+` 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 `QAction`s 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 `.so`s 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/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.