# How Zed's Multi-Buffer System Handles Multiple Files: Architecture and Implementation

> Explore Zed's multi buffer system architecture and implementation. Learn how Zed unifies multiple file buffers into a single view for seamless editing, coordinate mapping, and diff rendering.

- Repository: [Zed Industries/zed](https://github.com/zed-industries/zed)
- Tags: architecture
- Published: 2026-03-01

---

**Zed's multi-buffer system aggregates independent file buffers into a single logical view using excerpts, enabling unified editing, coordinate mapping, and diff rendering across multiple files as one continuous document.**

Zed's multi-buffer system powers features like project-wide search results and refactoring previews in the zed-industries/zed repository. By representing multiple open files as a continuous sequence of excerpts rather than separate tabs, the system allows developers to edit, review diffs, and navigate across file boundaries within a unified interface.

## Core Architecture: Buffers, Excerpts, and Snapshots

At the foundation of Zed's multi-buffer system are three core concepts implemented primarily in [`crates/multi_buffer/src/multi_buffer.rs`](https://github.com/zed-industries/zed/blob/main/crates/multi_buffer/src/multi_buffer.rs): the raw **Buffer** entities, the **Excerpts** that represent slices of those buffers, and the immutable **Snapshots** that provide thread-safe views.

### The Buffer Entity

Individual files are backed by the `Buffer` type defined in [`crates/language/src/buffer.rs`](https://github.com/zed-industries/zed/blob/main/crates/language/src/buffer.rs). Each `Buffer` stores raw text, supports editing operations, language services, and diff tracking. The multi-buffer does not duplicate text content; instead, it holds references to these independent buffer instances.

### Excerpts as Virtual File Slices

An **excerpt** is a contiguous slice of a `Buffer` presented in the multi-buffer view. The `Excerpt` struct (and its public counterpart `MultiBufferExcerpt`) tracks:

- A **path key** identifying the source file
- A **range** within the underlying buffer to display
- Context lines before and after the range

The system stores excerpts in a sum-tree structure, allowing efficient lookup and cursor navigation across the virtual document.

### Immutable Snapshots for Concurrent Access

All read-only operations—such as row/column mapping, rendering, and diff hunk discovery—operate on a `MultiBufferSnapshot`. This immutable, point-in-time view prevents race conditions during concurrent editing. When underlying buffers change, the multi-buffer updates its snapshot lazily via `sync` or `sync_mut` methods.

## Creating and Populating a MultiBuffer

Developers construct a multi-buffer using `MultiBuffer::new`, specifying capabilities like `Capability::ReadWrite`. For simple cases, `MultiBuffer::singleton` creates a view containing exactly one buffer and one excerpt.

### Singleton Mode for Single Files

The singleton convenience method wraps a single buffer for standard file editing:

```rust
use gpui::Context;
use multi_buffer::{MultiBuffer, Capability};
use language::Buffer;

let mut cx = Context::default();
let buffer = Buffer::new_local(Arc::new("main.rs".into()), Capability::ReadWrite, &mut cx);
let mb = MultiBuffer::singleton(buffer, &mut cx);

```

### Aggregating Multiple Files with set_excerpts_for_path

To display multiple files, use `set_excerpts_for_path`, which creates `Excerpt` instances and inserts them into the internal sum-tree:

```rust
use multi_buffer::{MultiBuffer, PathKey, Capability};
use language::{Buffer, Point};
use gpui::Context;

fn open_two_files(cx: &mut Context<MultiBuffer>) -> MultiBuffer {
    let buf1 = Buffer::new_local(Arc::new("path/a.rs".into()), Capability::ReadWrite, cx);
    let buf2 = Buffer::new_local(Arc::new("path/b.rs".into()), Capability::ReadWrite, cx);
    
    let mut mb = MultiBuffer::new(Capability::ReadWrite);
    
    // Show entire files with zero context lines
    mb.set_excerpts_for_path(
        PathKey::sorted(0),
        buf1,
        [Point::zero()..buf1.read(cx).max_point()],
        0,
        cx,
    );
    
    mb.set_excerpts_for_path(
        PathKey::sorted(1),
        buf2,
        [Point::zero()..buf2.read(cx).max_point()],
        0,
        cx,
    );
    
    mb
}

```

## Coordinate Mapping Between MultiBuffer and Source Files

The multi-buffer provides bidirectional coordinate translation between its virtual document space and the underlying buffer coordinates. The `MultiBufferSnapshot` exposes methods like `row_to_buffer` to map positions:

```rust
let snapshot = mb.snapshot(&cx);
let mb_row = MultiBufferRow(42);
let (buffer_id, buffer_row) = snapshot.row_to_buffer(mb_row);

```

Internally, this uses `MultiBufferCursor` traversing the excerpt sum-tree to locate the correct excerpt, then delegates to `buffer.row_to_point` on the source `Buffer`.

## Cross-File Editing and Diff Integration

When users edit text in a multi-buffer view, the system must route changes back to the appropriate source files, handling cases where an edit spans multiple files.

### Routing Edits to Source Buffers

The `MultiBuffer::edit` method calls `convert_edits_to_buffer_edits` (approximately lines 1670-1790 in [`crates/multi_buffer/src/multi_buffer.rs`](https://github.com/zed-industries/zed/blob/main/crates/multi_buffer/src/multi_buffer.rs)) to translate multi-buffer ranges into per-buffer edits:

1. **Locate excerpts**: Walks the snapshot cursor to find start and end excerpts for the edit range
2. **Split operations**: Divides edits that cross excerpt boundaries into separate per-buffer operations
3. **Emit notifications**: Fires `Event::ExcerptsEdited` so UI views can redraw affected regions

```rust
// Delete text spanning file boundaries
let edit_range = MultiBufferOffset(1_000)..MultiBufferOffset(1_020);
mb.edit(vec![(edit_range, "")], None, &mut cx);

```

### Unified Diff Rendering

When a `BufferDiff` is attached via `MultiBuffer::set_diff_for_buffer`, the `DiffState` subscription listens for `BufferDiffEvent`s. The system recomputes `diff_transforms` to merge diffs across excerpts, exposing unified diff hunks via `MultiBuffer::diff_hunks` for renderers in [`crates/buffer_diff/src/buffer_diff.rs`](https://github.com/zed-industries/zed/blob/main/crates/buffer_diff/src/buffer_diff.rs).

## Change Synchronization and Subscriptions

The multi-buffer maintains consistency with underlying buffers through lazy synchronization. When buffers change, the system updates snapshots and notifies listeners via a `Topic<MultiBufferOffset>`:

```rust
let subscription = mb.subscribe(); // Returns Topic<MultiBufferOffset>

cx.notify_on(subscription, move |_, cx| {
    // Re-render UI or recalculate line numbers
    update_sidebar(cx);
});

```

The `sync` and `sync_mut` methods reconcile changes, while `subscriptions.publish(offset)` emits notifications during edits or diff updates.

## Summary

- **Zed's multi-buffer system** treats multiple files as one continuous document by aggregating `Buffer` entities into excerpts managed by `MultiBuffer` in [`crates/multi_buffer/src/multi_buffer.rs`](https://github.com/zed-industries/zed/blob/main/crates/multi_buffer/src/multi_buffer.rs)
- **Excerpts** are virtual slices defined by path keys and ranges, stored in a sum-tree for efficient navigation
- **Immutable snapshots** (`MultiBufferSnapshot`) provide thread-safe read access, while `sync` methods handle lazy updates
- **Cross-file editing** uses `convert_edits_to_buffer_edits` to route virtual document changes back to specific source buffers
- **Diff integration** merges per-buffer diffs into a unified view using `DiffState` and `DiffTransform` logic

## Frequently Asked Questions

### What is a MultiBuffer excerpt in Zed?

An excerpt is a contiguous slice of an underlying `Buffer` displayed within a multi-buffer view. Defined by the `Excerpt` struct in [`crates/multi_buffer/src/multi_buffer.rs`](https://github.com/zed-industries/zed/blob/main/crates/multi_buffer/src/multi_buffer.rs), each excerpt tracks a path key identifying the source file and a specific byte or point range. Excerpts enable the system to show arbitrary portions of multiple files as one continuous scrollable document.

### How does Zed handle edits that span multiple files in a multi-buffer?

When `MultiBuffer::edit` receives a range crossing excerpt boundaries, it invokes `convert_edits_to_buffer_edits` (approximately lines 1670-1790 in [`multi_buffer.rs`](https://github.com/zed-industries/zed/blob/main/multi_buffer.rs)). This method walks the snapshot cursor to locate affected excerpts, splits the edit into per-buffer operations, and applies them atomically to the underlying `Buffer` instances. The system then emits `Event::ExcerptsEdited` to notify UI components of the changes.

### What is the purpose of MultiBufferSnapshot?

`MultiBufferSnapshot` provides an immutable, point-in-time view of all excerpts, diff states, and layout information. All read-only operations—including coordinate mapping via `row_to_buffer`, rendering, and diff hunk discovery—operate on this snapshot to prevent race conditions during concurrent modifications. The snapshot updates lazily when `sync` or `sync_mut` detects changes in underlying buffers.

### How does Zed integrate diffs into multi-buffer views?

The system attaches `BufferDiff` instances to individual buffers via `MultiBuffer::set_diff_for_buffer`. A `DiffState` subscription listens for `BufferDiffEvent`s and recomputes `diff_transforms` to merge diff information across all excerpts. This allows `MultiBuffer::diff_hunks` to present a unified diff view spanning multiple files, implemented in coordination with [`crates/buffer_diff/src/buffer_diff.rs`](https://github.com/zed-industries/zed/blob/main/crates/buffer_diff/src/buffer_diff.rs).