Expand description
MG.13 — per-buffer mode state, published for boot-registered action handlers.
The problem this closes. magit’s per-buffer modes used to
register their action handlers from inside on_activate, closing
over an Arc<Mutex<…State>> built during activation. on_activate
runs in the cascade future that ModeRegistry::spawn_cascade
spawns, so there is a window after a magit buffer opens in which
the chord resolves through the keymap, the mode reads as active,
and no handler exists — the keypress does nothing. That is not
a test artefact: it is what a user gets pressing d quickly after
:magit-branch. It is also the exact bug MG.8 shipped
(MagitGlobalMode registered from on_activate behind a
OnceLock); the fix there was to move to Mode::action_handlers(),
and this module finishes that migration for the modes that were
left behind because they carry per-buffer state.
The shape. Handlers move to Mode::action_handlers() —
registered once at boot, for the lifetime of the app — and read
their per-buffer state out of a BufferStates service keyed by
BufferId at call time. ActionContext already carries both
buffer_id and services, so the handler has everything it needs.
Chord scoping is unchanged: K.1.c’s per-keystroke filter only
routes a mode’s chords in buffers where that mode is active.
Why the state is there in time. spawn_cascade polls the
cascade future once, synchronously, on the App thread before
spawning it (its try-sync-then-spawn arm). Everything in
on_activate above its first .await therefore runs before
activate_major returns. Publishing state there makes it visible
to the very next keystroke — so each mode’s on_activate must
publish before it awaits anything. Fields that genuinely cannot
be known until after an await (rebase’s resolved upstream,
commit’s diff_end_line) are published with an inert initial value
and filled in through the Arc<Mutex<_>> once known; their
handlers already refuse to act on the inert value.
Caveat — cascade position. The synchronous first poll reaches
only as far as the first pending await in the whole cascade, so
only the root step (the major mode) is guaranteed to publish
synchronously. Implied minors run later. magit-core-mode is a
minor and must therefore not depend on this guarantee — which it
does not: its handlers read the buffer through BufferStoreHandle
and ctx.buffer_id, so they need no published state at all.
Structs§
- Buffer
State Guard - Drops a buffer’s state entry when its mode deactivates.
- Buffer
States - Per-buffer state for one magit mode.
- Magit
Views - Per-buffer
MagitViewregistry — the shared-action peer ofBufferStates. - View
Guard - Wraps a mode’s existing Guard and additionally unpublishes its
MagitViewon drop.
Enums§
- Diff
Source - Which tree a stretch of diff text was produced against — the
answer
MagitView::diff_sourcegives, and the only thing hunk-levels/u/xneed to know beyond the hunk itself.
Traits§
- Magit
View - A magit buffer’s view behaviour, published per buffer alongside its state.
Functions§
- refresh_
all_ views - Refresh every live magit view.
- state_
for - Look up this mode’s state for the buffer the action fired in.
- view_
for - The view for the buffer an action fired in.
Type Aliases§
- Magit
Views Handle - Service alias — register and look up through this exact type
(
feedback_servicesregistry_arc_typeid).