[0.7.0] - 2026-05-11
The 0.7.0 release lands the R2.x P7+P9 cascade
(ADR-039)
removing WireFace from the public plugin API, the ABI 4.0.0 →
4.1.0 wire bump for eval-command at session-ready (ADR-041) and
command-error-event plugin observability (ADR-042), and the
Issue #81 sprout-dogfooding suite (12 children + 1 follow-up
#97) delivering 3 layers of Kakoune-command construction tooling
(kak::*, kak_lint!, kak_cmd::KakCommand), a native plugin
test harness, and a host of SDK ergonomic helpers.
Migration: docs/migration/0.6-to-0.7.md
walks each breaking-change site plus the §§9.1–9.5 opt-in additions.
Added — kak::set_option + kak::set_option_add (closes #97)
Two SDK helpers that close a silent foot-gun surfaced by the second
wave of sprout dogfooding: Kakoune's %X{…} expansions (%opt{…},
%arg{N}, %val{…}, %sh{…}, %reg{c}) are not processed when
glued to a bareword, only inside double-quoted strings or as
standalone tokens. So
set-option -add window ui_options sprout_request_arg=%arg{1}stored the literal text sprout_request_arg=%arg{1} rather than the
expanded value — no Kakoune diagnostic, no compile error, just a
silently propagated literal that broke sprout's request dispatch.
The new helpers always wrap values in "…" so the expansion fires:
use kasane_plugin_sdk::kak::{self, Scope};
let cmd = kak::set_option_add(Scope::Window, "ui_options", &[
("sprout_request_id", "%opt{sprout_request_seq}"),
("sprout_request_kind", "pick"),
("sprout_request_arg", "%arg{1}"),
]);
// → set-option -add window 'ui_options'
// "sprout_request_id=%opt{sprout_request_seq}"
// "sprout_request_kind=pick"
// "sprout_request_arg=%arg{1}"Companion helper [kak::escape_arg_expand] doubles embedded " and
documents the bareword rule (linking
doc/pages/expansions.asciidoc); the docs are deliberately explicit
because the bug is invisible in normal testing. For literal values
where expansions should not happen, fall back to the existing
[kak::escape_arg] (single-quoted) and compose the command yourself.
examples/wasm/kakoune-bindings-demo gains a set_option_add call
demonstrating an expansion-bearing entry.
Added — Structured KakCommand enum (ADR-043)
Closes the last open child of the sprout-dogfooding tracker
(Issue #94). Adds the
kasane_plugin_sdk::kak_cmd module exposing a KakCommand enum that
represents a Kakoune command as a Rust value rather than a string:
use kasane_plugin_sdk::kak_cmd::{KakCommand, DeclareUserMode, DefineCommand, Map, Scope};
let setup: Vec<KakCommand> = vec![
DeclareUserMode::new("sprout").try_idempotent().into(),
DefineCommand::new("bump", "increment-counter")
.override_existing()
.docstring("bump the sprout counter")
.into(),
Map::new(Scope::Global, "sprout", "b", ":bump<ret>")
.docstring("bump")
.into(),
];
// Render each as a Kakoune command string.
let strings: Vec<String> = setup.iter().map(KakCommand::render).collect();The renderer centralizes quoting/escaping (single-quote with ''
escape, balanced %X…Y body delimiters with {} → [] → () → <>
fallback). Each variant has builder methods only for the flags Kakoune
accepts — DeclareUserMode exposes no override_existing() because
Kakoune does not accept that flag (the sprout regression that motivated
this whole tracker).
The new module complements the existing kak::* string builders and
kak_lint! validator; the cookbook in docs/plugin-development.md
documents the three-layer choice. Initial catalog covers 12 commands
(declare-user-mode, define-command, map, declare-option, set-option,
unset-option, evaluate-commands, hook, alias, echo, info, try);
adding a command is additive — one variant + one args struct.
Pure SDK addition: no WIT change, no ABI bump, no plugin recompile.
Opt-in via use kasane_plugin_sdk::kak_cmd;.
Added — Plugin DX long-term: test harness + command linter (Issue #81 long-term, 2026-05-11)
Closes two infrastructural items on the sprout dogfooding tracker —
#92 (plugin test harness) and #93 (static Kakoune-command linter).
The last child, #94 (structured KakCommand enum), is covered by the
ADR-043 entry above.
-
kasane-plugin-sdk-testcrate (#92): mock-host harness for
unit-testing plugins natively, without compiling towasm32-wasip2
or driving a real Kakoune. ProvidesTestHarness(setters mirroring
the WIThost-statesurface),MockHostStatewith structured
Mock{Brush,Style,Atom,Coord,Info,Session}types,
CommandRecord/drain_commandsfor Effects observation, and a
MockElementArenarecording everyelement_builder::*call.
Re-exported askasane_plugin_sdk::testunder the SDK's
test-harnessfeature, so plugin authors can opt in with a single
Cargo.toml line:[features] test-harness = ["kasane-plugin-sdk/test-harness"]
The macros emitted by
define_plugin!/generate!now cfg-switch
thehost_statemodule: undertest-harnesson a non-wasm target
they route into the mock; production WASM builds are unchanged.
examples/wasm/cursor-lineships 4 end-to-end tests demonstrating
the pattern. Seedocs/plugin-testing.mdfor the cookbook. -
kak_lint!proc macro (#93): compile-time validator for raw
Kakoune command strings. Catches the sprout-dogfooding bug —
declare-user-mode -override(Kakoune silently rejects;
declare-user-modedoes not accept-override), which caused all 11
user-mode keymaps to fail to register — before the plugin is ever
loaded:// Compile error: unknown flag `-override` for `declare-user-mode`. let _ = kasane_plugin_sdk::kak_lint!("declare-user-mode -override 'sprout'");
Hand-rolled tokenizer handles all four Kakoune quotation forms
('…'with''escaping,%{…}/%[…]/%(…)/%<…>) and follows
tryinto its body so the idiomatic
try %[ declare-user-mode … ]wrapper still gets linted. The
catalog ships with 12 setup-heavy commands; unknown commands pass
through unchanged (additive policy — no false positives against
real Kakoune). Catalog expansion is a one-line entry in
kasane-plugin-sdk-macros/src/lint.rs.
Added — Plugin DX dogfooding suite (Issue #81, 2026-05-11)
The sprout dogfooding tracker (#81) closes 8/12 children with a focused
SDK + docs landing:
kasane_plugin_sdk::kakmodule (#87): idempotent Kakoune-command
string builders —declare_user_mode(encodestry %[ … ]),
declare_option,define_command(auto-selects balanced delimiter
%{…}→%[…]→%(…)→%<…>→ quoted fallback),map,
escape_arg, plusScopeandOptionKindenums. Encodes the correct
idempotency idiom per command so plugin authors cannot accidentally
pass-overrideto commands that don't accept it (the sprout
regression).kakoune_setup_effects!macro (#88): builds anEffectsvalue
sending each command as its ownCommand::SendKeys— failure
isolation, unlike a singleevaluate-commands %{ … }block which
cascade-fails on the first error.process_event!macro (#91): safe destructure for
IoEvent::Processunder the new variant non-exhaustive policy.
Replaces the irrefutablelet IoEvent::Process(p) = event;pattern
that broke at the 0.5→0.6Httpvariant addition.keys::push_literaldebug-asserts on\n(#86): Kakoune prompts
reject newline; the dev-time panic surfaces the bug in tests.keys::commanddoc-comment rewritten (#83): full escape table,
<esc>mode side-effect note,<ret>-literal round-trip property,
EvalCommandpointer with session-ready availability caveat.examples/wasm/kakoune-bindings-demo(#84): worked example for
the register-Kakoune-APIs-at-startup pattern (option, command,
user-mode, key maps). Documents the-overrideflag asymmetry.docs/abi-versioning.md(#85): two-axis versioning model
(WIT ABI vs SDK semver), host's major.minor exact-match rule, bump
decision table, plus Appendix A (variant non-exhaustive policy) and
Appendix B (safe destructure macros).docs/migration/0.5-to-0.6.md§1.2.1 + §1.5 (#82): 7 face→style
helper renames table + IoEvent irrefutable bind / Style !Copy
hoisting patterns.docs/plugin-cookbook.md+docs/plugin-development.md:
cookbook recipe and profiles-table row pointing plugin authors to the
newkak::*+kakoune_setup_effects!pattern.
Added — Plugin command-error attribution Phase A (ADR-042)
Host-internal half of the plugin command-error observability protocol
(#90). Plugins can wrap their Kakoune-side emissions as
try %[ <cmd> ] catch %[
info -title '__kasane_plugin_error__' %{ <plugin-id>%val{error} }
]
The state-apply layer recognises the reserved title, parses
(plugin-id, message) from the content, emits
tracing::warn!(plugin_id, message, …) with the attribution, and
suppresses the marker so it never reaches the user-visible UI. The
empirical Kakoune-side validation that motivated the design is
captured in ADR-042's Empirical validation section. Phase B (WIT 4.0.0
on-command-error-effects export, host auto-wrap) is RFC-tracked but
unimplemented.
Breaking — Plugin ABI 4.1.0 (ADR-041 + ADR-042 Decided, 2026-05-11)
WIT bumps from kasane:plugin@3.0.0 → @4.1.0 in two coordinated steps:
- ADR-041
(ABI 4.0.0,dd2fbe3a):eval-command(string)added to
session-ready-commandvariant. Plugins can now issue command bodies
directly aton_active_session_ready_effectswithout the
<esc>:cmd<ret>keystroke-simulation wrapping. - ADR-042
(ABI 4.1.0,178eeedd+858581db+cfc13952+4eb241ca): plugin
command-error observability via theinfo_showmarker pattern. Adds
command-errorrecord andon-command-error-effectsexport to WIT.
Host-sidestate/apply.rsrecognises the reserved title, parses
(plugin-id, message), and routes to the originating plugin via
PluginRuntime::deliver_command_error_batch. Plugins opt into host
auto-wrap via[handlers] command_error_observability = truein the
manifest; the host wraps every emittedCommand::EvalCommandwith
try…catchso failures surface as attributed events.
Plugin migration:
- Bump
abi_version = "4.1.0"inkasane-plugin.toml. - Pin
kasane-plugin-sdk = "0.7". cargo component build— pure recompile, no source changes
required. Plugins that want command-error observability opt in via
the new manifest flag and (optionally) override the new export.
All bundled / fixture / example WASMs rebuilt against the new ABI
(23 manifests across examples/wasm/, kasane-wasm/fixtures/, and
kasane-wasm/bundled/).
Breaking — R2.x P7+P9 cascade: WireFace removed from public plugin API (2026-05-10)
Closes the post-ADR-031 visibility-tightening backlog from
roadmap.md §2.2. The wire-format-aware WireFace type is no
longer reachable from plugin_prelude and is #[doc(hidden)] pub
internally; plugin authors construct atoms / element styles via
the post-resolve Style type. See
docs/migration/0.6-to-0.7.md for
each surface's before/after.
Surfaces that changed type:
Element::text(s, face: WireFace)→Element::text(s, style: Style). The auxiliaryElement::text_with_styleconstructor is
gone (its body absorbed intoElement::text).DisplayDirective::StyleInline { face: WireFace }→
{ style: Style }. Same forStyleLine.InlineOp::Style { face: WireFace }→{ style: Style }.CursorEffectOrn { face: WireFace }→{ style: Style }. Same
forSurfaceOrn.Command::RegisterThemeTokens(Vec<(String, WireFace)>)→
Vec<(String, Style)>. The
KakouneSafeCommand::register_theme_tokens(tokens)helper
follows.ColorResolver::resolve_face_colors[_linear](&WireFace)and the
WireFacesync_defaults(&WireFace)are deleted; consumers use
resolve_style_colors[_linear](&Style)and
sync_defaults(&Style).Atom::from_wire(WireFace, _)ispub(crate)(only the protocol
parser andtest_support::wire's cursor fixtures need the
final_*-preserving path). Plugin code uses
Atom::with_style(_, Style).WireFaceis no longer inkasane_core::plugin_prelude.
Plugins that observedfinal_*resolution flags viaWireFace
now read them fromUnresolvedStyle.final_fg/final_bg/
final_style.
Changed — bridge.rs dispatch macros (R2.x P8) (2026-05-10)
Two new dispatch macros (dispatch_state_with_default!,
dispatch_inject_owner_contribution!) consolidate 10 hand-coded
sites in PluginBridge. Visible to plugin authors only as
slightly clearer inline-box / contribution / overlay panic
backtraces; no behavioural change.
Added — kasane.kdl auto-reload for plugins and settings (ADR-040)
Opt-in plugins.auto_reload #true makes edits to the plugins and
settings blocks in kasane.kdl apply live: kasane runs resolve,
rewrites plugins.lock, and live-swaps the plugin set without a
restart. Settings-only changes skip the lock update and refresh
plugins in place. Default is #false to preserve the existing
"resolve, then restart" workflow that CI scripts depend on. See
docs/using-plugins.md
for usage.
Fixed — Per-plugin teardown on hot-reload
Config::restart_required_diffnow comparessettings, surfacing a
diagnostic for changes that were previously ignored silently.PluginVariableStorerecords the owning plugin id when a plugin
exposes a variable, andunload_pluginclears those entries so a
hot-reloaded plugin can't observe the previous instance's values.ProcessDispatcher::kill_all_for_pluginaborts every child process
owned by an unloaded plugin and resets the per-plugin process slot
count, fixing a leak where re-loaded plugins could hit
MAX_PROCESSES_PER_PLUGINfor processes that no longer existed.
What's Changed
- chore(release): update Homebrew formula to 0.5.0 by @github-actions[bot] in #55
- chore(release): update kasane-bin to 0.5.0 by @github-actions[bot] in #56
- deps(deps): update rust patch updates by @renovate[bot] in #54
- deps(deps): update rust minor updates (fixed) by @Yus314 in #60
- deps(deps): update softprops/action-gh-release action to v3 by @renovate[bot] in #57
- ADR-031 closure: Style-native pipeline + WIT 2.0.0 by @Yus314 in #65
- deps(deps): update rust patch updates by @renovate[bot] in #63
- Post-closure cleanup: TUI benches + HostState Style + StyledLine scratch + Face → WireFace by @Yus314 in #66
- deps(deps): update rust minor updates by @renovate[bot] in #64
- test(core): cursor positioning golden snapshots (Phase 12) by @Yus314 in #69
- feat(core): runtime assertion for ShadowCursor × InlineBox overlap by @Yus314 in #71
- test(core): selection rendering golden snapshots (Phase 12) by @Yus314 in #70
- fix(gui): drop zero-size raster glyphs to clear spurious L2 drops by @Yus314 in #67
- feat(core): protocol::wire submodule for ADR-031 visibility migration by @Yus314 in #73
- fix(wasm): widen epoch budget + surface trap as panic in tests by @Yus314 in #74
- test(core): CJK / combining-mark / emoji golden snapshots (Phase 12) by @Yus314 in #68
- docs(roadmap,examples): close Phase 10 paint_inline_box worked-example item by @Yus314 in #72
- chore(release): v0.6.0 by @Yus314 in #77
- chore(release): update kasane-bin to 0.6.0 by @github-actions[bot] in #79
- deps(deps): update rust crate image-compare to 0.5 by @renovate[bot] in #76
- chore(release): update Homebrew formula to 0.6.0 by @github-actions[bot] in #78
- chore(nix): bump contrib/nixpkgs/package.nix to v0.6.0 by @Yus314 in #80
New Contributors
- @github-actions[bot] made their first contribution in #55
Full Changelog: v0.5.0...v0.7.0