Skip to main content

Module mode

Module mode 

Source
Expand description

diff-mode minor mode + the host-side bridge that toggles it on participating buffers as DiffSessions open and close.

§The invariant

A buffer participates in any [DiffSession] ⟺ diff-mode is active on that buffer. The toggle lives inside the [DiffSubsystem] (register_with_sources / drop_session) so every present and future :diff* ex-command (:diff <buf>, :diffthis, :diffsplit, future :Gdiff D.7, future :diff-accept / :diff-reject D.6) and the doc-close auto-drop (note_buffer_closed → drop_session) inherit the toggle without each call site repeating the activation logic. See docs/dev/architecture/diff-system.md §3.4.7.

§Why a bridge instead of a direct call

The subsystem holds an Arc<DiffSubsystem> and may be invoked from a tokio worker (the bus-subscription drainer that translates DocumentClosed events into drop_session). The mode-activation API (ModeRegistry::activate_minor / deactivate_minor) needs &mut ActiveModes per buffer plus references to mode-guards, config, event bus, and services — all of which live on Editor and aren’t shareable across threads.

The bridge resolves this by queueing mode changes from any thread into a Mutex<Vec<DiffModeChange>> and exposing a drain_pending call the editor invokes from the dispatch tail (publish_render_state). The dispatch tail applies each change using the existing mode_registry pattern.

§Why ref-counted per buffer

v1’s :diff* flows don’t intentionally put the same buffer in two sessions, but lookup_session_for indexes only the primary side. A baseline-side buffer can therefore be staged into a second session without the existing rejection check catching it; D.6 three-way will plausibly see a MergeBase participant also diffed against working tree. Naive “session opens → mode on / session closes → mode off” would flip diff-mode off prematurely when the first of two sessions sharing a buffer closes.

The bridge therefore keeps a per-buffer refcount keyed by the participating session keys. Activation appends the session key to the buffer’s bucket; deactivation removes it; the bridge enqueues a Deactivate change only when the bucket empties.

Structs§

DiffConflictMode
diff-conflict-mode (smerge-style) minor — DX.8 shell (2026-06-24).
DiffDecorationData
Render-time snapshot for DiffMode::gutter_decorations. Carries the active buffer’s diff sign map; injected by the renderer only for the active-document buffer (sign_map is active-doc only).
DiffMode
diff-mode minor. Empty marker in v1 (D.5.a): the bit other layers consult. D.5.b/c land the do/dp chord bindings against the MinorMode(ModeId::new("diff-mode")) keymap layer (K.1.b shape).
DiffModeBridge
Host-side bridge between [DiffSubsystem] session lifecycle and the per-buffer ActiveModes set. See module docs for the why.
DiffModeChange
One queued toggle. Buffered until the dispatch tail drains the bridge and applies them through the mode registry.
DiffModeGuard
DX.3-C7 (2026-06-24): deregisters the buffer’s HunkFoldSource when diff-mode deactivates (the diff session closed, or the buffer’s mode set changed). Drop fires from the mode guard store when deactivate_minor runs — the same Drop-based lifecycle multibuffer’s MultibufferModeGuard uses for its excerpt/file-boundary fold sources. Empty when the fold service / diff subsystem weren’t registered (some test harnesses) — Drop is then a no-op.

Enums§

DiffModeAction
Direction of a pending DiffModeChange. Activate adds diff-mode to the buffer’s ActiveModes.minors(); Deactivate removes it.
DiffModeKind
CR.3 (2026-06-24): which diff minor mode a DiffModeChange toggles. Base = diff-mode (every session participant); Conflict = diff-conflict-mode (only sessions whose sign map carries a Conflict region). The host’s apply_pending_diff_mode_changes maps the kind to the concrete ModeId.

Constants§

DIFF_ELEMENT
Modeline element id for the diff add/change summary. Owned by the diff subsystem (feedback_mode_owns_its_surface).

Functions§

diff_content
Format a diff sign-map into the +N ~M modeline content (ML.3). The formatter the diff subsystem owns — moved here from the retired DiffMode::status_line_items. Empty content (no adds/changes) ⇒ hidden this frame (the caller’s apply clears the slot).
register_diff_modeline_element
Register the diff modeline descriptor (ML.3). Scope::Global: the active document’s session sign-map is a single shared value, so the summary renders on the active pane’s modeline (the renderer gates Global elements to the active pane, modeline.md §7) — there is no distinct per-side count to show. Content is computed host-side on the actor (where the sign map already lives, sync_diff_modeline_element) rather than counted on the render thread (paramount #1). Left zone, after core.path.
register_diff_modes
Register the diff modes against registry. Called from crate::install::install (the Phase-B install list) alongside the other feature-crate register_*_modes helpers. Registers both diff-mode (the 2-way base surface) and the diff-conflict-mode shell (DX.8).
sign_map_has_conflicts
DX.8 (2026-06-24): the diff-conflict-mode activation predicate.