kate-deft/README.md

7.2 KiB

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

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):

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 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