Skip to main content

Module buffer_state

Module buffer_state 

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

BufferStateGuard
Drops a buffer’s state entry when its mode deactivates.
BufferStates
Per-buffer state for one magit mode.
MagitViews
Per-buffer MagitView registry — the shared-action peer of BufferStates.
ViewGuard
Wraps a mode’s existing Guard and additionally unpublishes its MagitView on drop.

Enums§

DiffSource
Which tree a stretch of diff text was produced against — the answer MagitView::diff_source gives, and the only thing hunk-level s / u / x need to know beyond the hunk itself.

Traits§

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

MagitViewsHandle
Service alias — register and look up through this exact type (feedback_servicesregistry_arc_typeid).