kate-deft/README.md

159 lines
7.2 KiB
Markdown

# Deft
A suite of Kate plugins for people who despise menus. The aim is a text editor
that is driven by the mouse at the cursor and by plain-letter keyboard input —
never by hunting through nested menubars or memorizing complex chords.
## Design constraints
These are hard constraints that shape every decision:
- **Mouse-heavy.** The fastest input is the mouse, used *locally* at the caret
(radial / floating surfaces), not a trek to the menubar.
- **One-handed typing.** Keyboard input must work comfortably with one hand.
- **No complex chords.** Single modifier at most (`Ctrl`+letter). No leader-key
chord trees, no multi-cursor keybinds.
- **Compact keyboard.** F-keys and nav clusters live on a layer toggle, so they
carry extra cognitive cost and are avoided as primary triggers.
- **Emacs/Sublime fluency.** `M-x` (`Alt+x`) is comfortable and welcome as a
primary keyboard entry point, as is a Sublime-style type-to-filter palette.
### Unifying principle
> **One action registry. Three doors onto it:**
> **`M-x` (keyboard), a radial caret menu (mouse), and the `:` command line (optional).**
> **No action requires an F-key or a chord.**
## Why not just use Kate's command bar?
Kate's `KCommandBar` is a sealed widget: it accepts a list of actions via
`setActions()` and exposes **no hook** to customize its matcher. Its matcher,
`KFuzzyMatcher`, is single-needle, strictly in-order subsequence, with **no typo
tolerance** — by KDE's own documentation, `"gti"` will not match `"git"`. There
is no "orderless" (space-separated tokens in any order) and no company-style
live completion.
So the project replaces the palette rather than extending it, built on a custom
matcher (`FuzzyRanker`).
## Environment
- Kate 25.12.3, KDE Frameworks 6, Qt 6
- C++20, CMake, extra-cmake-modules
- C++ is the only first-class plugin path on this install (no Python/Pâté binding present)
## Build
```sh
cmake -B build -S .
cmake --build build
QT_QPA_PLATFORM=offscreen ./build/bin/test_fuzzyranker # run the matcher tests
ctest --test-dir build # or via ctest
```
## Install the plugins into Kate
Deft is **one `.so` per feature**, not a monolith. The build produces six
KTextEditor plugins under `build/bin/kf6/ktexteditor/`:
| Plugin file | Display name | Feature |
|------------------|-----------------------|---------|
| `deft_palette.so`| `[deft:util] palette` | Command palette (`Alt+X` / M-x) over every window action + the `:`-verbs + the switchers, frecency-ranked |
| `deft_switch.so` | `[deft:util] switch` | Project **Go to File** (`Alt+P`) and **Go to Symbol** (`Alt+G`) |
| `deft_radial.so` | `[deft:util] radial` | Radial caret menu (`Alt+R` / right-mouse) |
| `deft_acme.so` | `[deft:nineify] acme` | Acme editing gestures (line keys, pair selection, mouse chords) |
| `deft_sam.so` | `[deft:nineify] sam` | `sam`-style structural regular expressions |
| `deft_plumb.so` | `[deft:nineify] plumb`| Plan 9 plumber (open under cursor) |
Drop whichever features you want where Kate scans for KTextEditor plugins (a
user path on `QT_PLUGIN_PATH`):
```sh
for so in build/bin/kf6/ktexteditor/deft_*.so; do
install -D "$so" ~/.local/lib/x86_64-linux-gnu/qt6/plugins/kf6/ktexteditor/"$(basename "$so")"
done
```
Then in Kate: Settings → Configure Kate → Plugins → enable each **`[deft:…]`**
plugin you installed. Press **Alt+X** (M-x) to open the command palette; type to
filter, Enter or click to run. Open the editing **radial menu** either by
pressing **Alt+R** (opens at the caret) or by **pressing the right mouse button**
in the editor (opens at the pointer; the normal context menu is suppressed while
this is enabled). Move toward a slice and release — or click; a branch slice
(marked ▸) opens a submenu, the centre hub goes back/cancels.
The plugins cooperate but do not depend on one another: palette's "Go to
File/Symbol" entries and radial's launcher slices resolve `switch`'s actions by
object name at runtime, so each `.so` works on its own and simply gains those
entry points when its sibling is also enabled.
## Status
See [docs/PLAN.md](docs/PLAN.md) for the full roadmap.
- **Milestone 1 — FuzzyRanker (keystone): DONE.** Orderless, layered scoring
(exact / substring / word-initials / subsequence / bounded typo), merged
highlight ranges. 14 unit tests, all green.
- **Milestone 2 — custom palette widget: DONE.** `PaletteModel` +
`PaletteWidget` (frameless popup, live filtering, highlighted matches,
keyboard + mouse activation). 10 model unit tests, all green.
- Milestone 3 — KTextEditor plugin + M-x: **DONE.** `Alt+X` opens the palette
over every action in the window's GUI clients.
- Milestone 4 — radial caret menu: **DONE (first increment).** Multi-level
radial menu at the caret (`Alt+R`) or at the pointer via a **right-mouse
gesture**, mouse-driven selection, config tree with per-radial key + mouse
triggers. Free-form stroke gestures next.
- Milestone 5 — command vocabulary + project-as-directory + switchers: **DONE.**
`:`-verb pack, project file (`Alt+P`) and symbol (`Alt+G`) switchers over an
async, cached index.
- Milestone 6 — one `.so` per feature: **DONE.** The monolithic plugin is split
into six independent KTextEditor plugins (`palette`, `switch`, `radial`,
`acme`, `sam`, `plumb`), tagged `[deft:util]` / `[deft:nineify]`, cooperating
by resolving each other's actions by object name at runtime.
## Layout
Shared building blocks are static libraries (`fuzzy`, `palette`, `project`,
`commands`, …); each feature is a self-contained KTextEditor plugin that links
the libraries it needs.
```
CMakeLists.txt top-level KF6/Qt6/ECM project
src/
fuzzy/
fuzzyranker.{h,cpp} orderless + layered fuzzy matcher
test_fuzzyranker.cpp QTest suite
palette/
palettemodel.{h,cpp} ranked, filterable list model (headless)
palettewidget.{h,cpp} frameless command-palette popup
frecencystore.{h,cpp} persisted frecency ranking
paletteplugin.{h,cpp} [deft:util] palette — M-x, :-verbs, switcher doors
paletteplugin.json plugin metadata
test_*.cpp QTest suites
switch/
switchplugin.{h,cpp} [deft:util] switch — Go to File (Alt+P) / Symbol (Alt+G)
switchplugin.json plugin metadata
radial/
radialmodel.{h,cpp} radial tree + hit-test geometry (headless)
radialmenu.{h,cpp} frameless multi-level radial widget
radialconfig.{h,cpp} JSON → radial tree + built-in default
radialplugin.{h,cpp} [deft:util] radial — caret menu plugin
radialplugin.json plugin metadata
test_*.cpp QTest suites
commands/
texttransforms.{h,cpp} pure text transforms (headless)
olliecommands.{h,cpp} ":"-verb KTextEditor::Command pack (used by palette)
test_*.cpp QTest suites
project/
projectindex / symbolindex / projectindexer / kateprojectbridge
async, cached file & symbol index (used by switch)
acme/
acmeplugin.{h,cpp,json} [deft:nineify] acme — editing gestures
sam/
sam* + samplugin.{h,cpp,json} [deft:nineify] sam — structural regexps
plumb/
plumb* + plumbplugin [deft:nineify] plumb — Plan 9 plumber
docs/
PLAN.md goal, constraints, roadmap
```