5.5 KiB
5.5 KiB
Plan — kate-custom
Goal
Build a suite of Kate plugins that make the editor usable without menus, tuned to one specific operator profile:
- mouse-heavy, with the mouse used at the caret rather than at the menubar
- one-handed keyboard typing
- no complex chords; single modifier at most
- compact keyboard where F-keys/nav clusters sit behind a layer toggle
- long Emacs history and some Sublime Text;
M-xand type-to-filter palettes are familiar and welcome
The spine of the suite is a single action registry reached through three
interchangeable doors: M-x (keyboard), a radial caret menu (mouse), and the
: command line (optional). Every feature registers its commands once and
becomes reachable from all three. No action depends on an F-key or a chord.
Why a custom matcher is the keystone
Kate's built-in command bar cannot be fixed in place:
KCommandBar(KConfigWidgets) exposes onlysetActions(). There is no hook to replace or configure its matcher.KFuzzyMatcher(KCoreAddons) is single-needle, strictly in-order subsequence, and not typo tolerant. KDE's own docs state"gti"will not match"git". No orderless, no company-style completion.
Therefore the palette is replaced, not extended, and the replacement is built on
FuzzyRanker, a matcher with:
- orderless tokenization (space-separated needles matched in any order)
- layered scoring, strongest tier first:
- exact
- substring
- word-boundary initials (
rs-> Rename Symbol) - in-order subsequence
- bounded typo via a single adjacent transposition (
gti->git; deliberately narrow to avoid false positives likeren-> "Open File")
- merged highlight ranges for the UI
- AND semantics across needles, case-folded, shorter-candidate preference
Milestones
M1 — FuzzyRanker (keystone) — DONE
- Repo scaffold: top-level CMake (KF6/Qt6/ECM),
src/,src/fuzzy/. src/fuzzy/fuzzyranker.{h,cpp}: tokenize + layered scoring + ranges.src/fuzzy/test_fuzzyranker.cpp: QTest suite, 14 cases, all green, includinggti -> gitandren sym -> Rename Symbol.
M2 — Palette widget (replaces KCommandBar) — DONE
PaletteModel(src/palette/palettemodel.{h,cpp}):QAbstractListModel, re-ranks viaFuzzyRankeronsetQuery, exposes id/label/group/score and highlight ranges per row, frecency bonus. No widget dependency.PaletteWidget(src/palette/palettewidget.{h,cpp}): frameless popup, filter line + results list, live company-style updates, top hit preselected, custom delegate bolds matched ranges. Keyboard: Up/Down/PageUp/PageDown, Enter activates, Esc cancels — cursor stays in the filter line, no chords. Mouse: click a row to activate. EmitsactivatedId(id)/cancelled().src/palette/test_palettemodel.cpp: 10 QTest cases, all green (filtering, orderless, typo, best-first ordering, frecency tie-break, highlight ranges).
M3 — KTextEditor plugin + M-x + action registry — DONE
src/plugin/ollieplugin.{h,cpp}+ollieplugin.json: a loadableKTextEditor::Plugin(olliepalette.so) built withkcoreaddons_add_plugin.OllieView(one per MainWindow) registers the M-x action,Alt+X, a single modifier with the keys adjacent on a compact layout;ApplicationShortcutcontext so it is always live.- On trigger it aggregates every enabled, visible
QActionfrom the window'sguiFactory()->clients()action collections, keyed by stableobjectName, and feeds them toPaletteWidget. The action's current shortcut is shown in the group column. Accepting a row triggers the underlyingQAction. - Installs to
kf6/ktexteditor; metadata verified valid viaKPluginMetaData. - Not yet wired: frecency (the model supports a bonus, but the plugin currently passes 0 for every action — usage tracking is deferred).
- Duplicate handling: actions are deduplicated only by pointer identity (the exact same action object reachable through several GUI clients is listed once — functionally lossless). Distinct actions are never dropped. When two distinct actions would render with the same label, the label is enriched through a fallback chain until every visible row is unique: component name → objectName → shortcut → numeric suffix. Each entry also gets a unique id so activation always triggers the intended action.
M4 — Radial caret menu (mouse door) — NEXT
- Mouse-triggered radial menu positioned at the caret
(
View::cursorToCoordinate), flick-to-select by angle. - Slices invoke registry actions; one slice opens the palette / command line, so the keyboard-expensive paths are never required.
M5 — Command vocabulary + project model + switchers
:-verb pack viaKTextEditor::Command:sort,align,json,b64,uuid,case,pipe <shell>, etc. These populate M-x and the radial.- Project = directory. "Open folder == open project" (VSCode/Sublime model);
no
.kateprojectceremony. - File / symbol switchers scoped to the opened folder (e.g.
git ls-files/fdfor files; LSP or ctags for symbols), all through the M2 palette.
Build and test
cmake -B build -S .
cmake --build build
ctest --test-dir build
# or directly:
QT_QPA_PLATFORM=offscreen ./build/bin/test_fuzzyranker
Environment facts (verified)
- Kate 25.12.3, KDE Frameworks 6, Qt 6.10, cmake 4.2.3, g++, extra-cmake-modules.
- KTextEditor + KCoreAddons + KConfigWidgets + KXmlGui + KRunner dev headers present.
- No Python/Pâté binding installed; C++ is the only first-class plugin path.