Expand description
D.2.a / D.2.b / D.2.c — DiffSubsystem.
Registry + compute + routing layer for the diff subsystem.
Sessions are Arc-shared so consumers (the future inline
overlay D.3, side-by-side D.4, hunk-transfer ops D.5,
:describe-diff D.2.d) can hold a stable handle while the
registry continues to mutate around them.
§Slice landing order
- D.2.a (2026-05-28) — registry skeleton.
DiffSubsystemkeyingArc<DiffSession>by [BufferId] behind astd::sync::Mutex.register/lookup/drop_session/iter_sessions. Per-sessionArcSwap<HunkIndex>for RCU-published reads. - D.2.b (2026-05-28) — compute path.
DiffParticipantSourcetrait (initial implStaticSource). Monotonic revision allocator on the session; gated publish viaDiffSession::try_publish_if_newer. Sync recompute bodyDiffSession::recompute_blocking; tokio orchestrationDiffSubsystem::schedule_recomputespawns it onspawn_blockingand returns a join handle. - D.2.c (2026-05-29) — routing + debounce + bus
subscription.
BufferTextProvider(one host seam),DiffParticipantSource(mirror ofDiffParticipantSource),BufferSource/BufferSourcelive-rope impls,DiffDescriptor(sources + explicitwatch: Vec<BufferId>). Centralized inversewatchersindex + per-session lazyDebouncer.DiffSubsystem::bindtakes aDocumentBufferResolverand anArc<EventBus>and returns aDiffSubscriptionGuardthat aborts the drainer task and unsubscribes the bus onDrop. See../../../docs/dev/architecture/diff-system.md§3.4 for the full data + routing model and the per-session-actor / direct-call alternatives that were considered and rejected.
§Concurrency model
- Registry / descriptor / watchers / debouncer mutation goes
through
std::sync::Mutex. Mutation is buffer-open / buffer-close / lazy-debouncer-spawn frequency — never per-frame. - Per-session published
hunks: ArcSwap<HunkIndex>is read lock-free from any thread (the renderer,:describe-diff, etc.). - The session’s
Arcitself is cloned out of the registry under the registry lock, then released. Holders may keep theArcpast adrop_sessioncall — the registry forgets the entry, but in-flight readers see a coherent snapshot until they release their clone (RCU). Matches the standardBufferRegistry/cells_matrix_cellpattern in this crate. - Bus subscription is centralized (one subscription on
the subsystem; one drainer task). On each
DocumentChangedthe drainer resolves DocumentId → BufferId, looks up dependents in thewatchersinverse index, and pokes each session’sDebouncer. The per-session debouncer is lazy — no task at rest; a tokio task spawns on first poke, sleeps the debounce window, and self-terminates after the burst quiesces. Rationale + scaling discussion in §3.4.
Structs§
- Buffer
Source - D.8.b (2026-05-31): live-rope participant backed by a
buffer. Replaces the prior
BufferSource+BufferSourcetwo-struct split (both had identical shape — provider + buffer_id — and only differed in which trait they implemented). The trait collapse toDiffParticipantSourcemakes the split structurally redundant. - Debouncer
- Per-session debounce controller.
- Diff
Descriptor - The “what to diff against what” pair for a session.
- Diff
GetPlan - D.5.b (2026-05-30): describes the edit the diff-mode
do(diff-get) operator would apply when invoked at a given cursor row on the active side of a session. Produced byDiffSubsystem::compute_get_edit; consumed by dispatch which translates it into anapply_edit_blockingcall and re-positions the cursor. - Diff
Session - Per-document diff state. Wraps an
ArcSwap<HunkIndex>so consumers read the latest published hunks without holding the registry lock. - Diff
Session Description - One row of
:describe-diffoutput. Produced byDiffSubsystem::describe_sessionsand rendered into the help-buffer body byDiffSubsystem::build_describe_diff_content. - Diff
Subscription Guard - RAII guard returned by
DiffSubsystem::bind. Holds the busSubscriptionId+ the drainer taskJoinHandle. OnDrop, unsubscribes the bus subscription and aborts the drainer task. - Diff
Subsystem - Process-wide registry + routing layer for diff sessions.
- OnDisk
Source - D.3.a (2026-05-29): on-disk file participant source.
- Static
Source - In-memory participant source — an owned
Ropecloned on everySelf::snapshot.
Enums§
- Diff
GetOutcome - D.6.d (2026-05-31): outcome of resolving the diff-mode
dochord or:diffget [<bufnr>]ex-command. MirrorsDiffPutOutcome’s tri-state but for the get direction: - Diff
Outcome - D.6.e (2026-05-31): resolution signal fired through a
DiffSession’s completion channel when the user invokes:diff-accept/:diff-reject. Consumed by the futureopenDiffplugin flow (Claude Code, AI multi-file edits) and by any in-tree consumer (magit plugin, AI proposals) that wants to know how the user resolved a session. - Diff
PutOutcome - D.5.c (2026-05-30) / D.6.d (2026-05-31): outcome of
resolving the diff-mode
dpchord or:diffput [<bufnr>]ex-command. Distinguishes four cases the dispatch handler must surface differently: - Membership
Error - D.8.d (2026-05-31): errors the subsystem’s membership
API returns when mutating a session’s participant set.
Distinct from [
DiffEngineError] (which is the engine crate’s surface) so callers don’t have to importlattice_diffjust to pattern-match onadd_participantfailures. The engine’s cap on arity (N ≥ 4 rejected in v1) surfaces here asEngineRejected(DiffEngineError::Unsupported{...}).
Constants§
- DEFAULT_
DEBOUNCE_ WINDOW - Default debounce window. Matches Helix’s diff debounce
(50ms); short enough that the visual lag is imperceptible
during sustained typing, long enough that a burst of 5–10
keystrokes collapses to a single recompute. Overridable via
DiffSubsystem::with_debounce_windowfor tests and host configuration.
Traits§
- Buffer
Text Provider - One-trait seam between the diff subsystem and the host’s
buffer storage. Required for
BufferSourceto resolve a [BufferId] to its live rope at snapshot time. - Diff
Participant Source - One participant in a diff session — produces the rope to
diff for one slot in
Hunk::ranges. - Document
Buffer Resolver - Translates the protocol-layer [
DocumentId] carried inEvent::DocumentChanged/Event::DocumentClosedback to a host-layer [BufferId]. The host supplies an impl backed byBufferRegistry. Kept as a trait so the subsystem stays independent of buffer-registry layout (and so tests can inject a stub mapping).
Type Aliases§
- Diff
Subsystem Handle - Cheap-clone service handle for the diff subsystem. DX.3/C7 (BC.6):
registered in the
ServiceRegistryat boot sodiff-mode’son_activatecan reach the session for a buffer (ctx.service::<DiffSubsystemHandle>()) and register aHunkFoldSourcevia theFoldOverlayService— mirroring howMultibufferModereaches itsMultibufferRegistryHandle. Follows the Arc/TypeId convention: registerArc<DiffSubsystem>, look upArc<DiffSubsystem>.