# How Zed Implements Search and Replace with Regex Support

> Discover how Zed implements efficient regex search and replace across editors and terminals. Learn about its async task execution and background scanning for large files.

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

---

**Zed's search and replace functionality uses the `Searchable` trait to provide regex-enabled replacements across editors, terminals, and file trees, compiling patterns with the `regex` crate and executing replacements through foreground async tasks with background scanning for large files.**

Zed's architecture treats search and replace as a core workspace capability, not merely an editor feature. Every searchable view—from the text buffer to the terminal—implements the `Searchable` trait, enabling consistent **Zed search and replace regex** functionality across the entire application.

## The Searchable Trait: Core Architecture

The `Searchable` trait, defined in [`crates/workspace/src/searchable.rs`](https://github.com/zed-industries/zed/blob/main/crates/workspace/src/searchable.rs) (lines 133-166), establishes the contract for any view that supports find-and-replace operations. This abstraction allows Zed to offer regex search and replace uniformly whether you are editing code, scanning terminal output, or browsing the project file tree.

### Key Methods: replace and replace_all

The trait exposes two primary methods for executing replacements:

- **`replace`** – Replaces a single match identified by a `SearchResult`. The method signature accepts a `SearchQuery` parameter that includes the `regex` flag, determining whether the replacement uses literal string matching or regex-based substitution.
- **`replace_all`** – Iterates through every match in the current view, applying the replacement string to each occurrence. When `query.regex` is true, Zed compiles the pattern using the `regex` crate and processes capture groups through `regex::Captures::expand`.

## Building the Search Query with Regex Support

### The SearchQuery Struct

User intent is captured in the `SearchQuery` struct located in [`crates/workspace/src/search.rs`](https://github.com/zed-industries/zed/blob/main/crates/workspace/src/search.rs) (lines 45-70). This structure records all parameters entered through the search UI:

```rust
pub struct SearchQuery {
    pub text: String,
    pub case_sensitive: bool,
    pub regex: bool,               // Toggles regex mode
    pub whole_word: bool,
    // …
}

```

When a user toggles the "Regex" checkbox in the search bar, the UI sets `regex: true`, signaling that the `text` field should be treated as a regular expression pattern rather than a literal string.

### Regex Compilation with RegexSearch

Zed wraps the standard `regex` crate in a helper struct called `RegexSearch`, implemented in [`crates/terminal/src/terminal_view.rs`](https://github.com/zed-industries/zed/blob/main/crates/terminal/src/terminal_view.rs) (lines 1095-1110). This wrapper provides two critical functions:

1. **Early validation** – `RegexSearch::new(pattern: &str)` compiles the regex immediately, returning an error if the pattern is invalid. This allows Zed to surface "Invalid regular expression" messages in the UI before any search execution begins.
2. **Match iteration** – `RegexSearch::iter(&self, text: &str)` returns an iterator yielding every match, preserving capture group information necessary for replacement strings containing back-references like `$1` or `$2`.

## Executing Regex Replace Operations in Zed

### The Five-Step Replacement Flow

When a user executes a regex replace operation, Zed follows this precise sequence:

1. **Parse the query** – `SearchQuery::from_string` reads the UI fields, setting `regex = true` when the regex toggle is active.
2. **Compile the regex** – `RegexSearch::new` is invoked; compilation errors abort the operation and emit a notification via `cx.spawn(|cx| async move { cx.emit(error) })`.
3. **Find matches** – The searchable view calls `self.search(query, token, ...)`, which internally selects between plain-text search (`String::find`) or `RegexSearch::iter` based on `query.regex`.
4. **Apply replacements** – For each match, Zed constructs the replacement string. If back-references are present, Zed delegates to `regex::Captures::expand` to resolve `$1`, `$2`, etc. The final text is inserted using `String::replace_range` on the underlying buffer.
5. **Update UI** – After all edits complete, the view emits `cx.notify()` to trigger a redraw, refreshing the "Replace All" preview panel.

### Handling Capture Groups and Back-References

Zed supports standard regex capture group syntax in replacement strings. When `replace_all` processes a match, it passes the replacement template to the regex crate's expansion function:

```rust
// Example: Replacing "Foo123" with "Bar123" using pattern r"Foo(\d+)" -> "Bar$1"
let captures = regex.captures(matched_text).unwrap();
let mut replacement = String::new();
captures.expand("Bar$1", &mut replacement); // replacement now contains "Bar123"

```

This functionality is available across all `Searchable` implementations, ensuring consistent behavior whether replacing code in the editor or text in the terminal.

## Threading Model: Foreground and Background Tasks

Because Zed runs on a single foreground thread (GPUI), all replace logic executes within a **foreground async task** created via `cx.spawn`. However, scanning large files or directories is delegated to a **background task** (`cx.background_spawn`) that streams matches back to the UI thread.

This architecture prevents UI freezes when processing massive files while maintaining atomic replace operations from the user's perspective. The background worker yields `SearchResult` items to the foreground, which then applies edits sequentially to preserve buffer consistency.

## Integration Points: Editor, Terminal, and Vim Mode

### Editor Buffer Replacements

The primary implementation of `Searchable` for text editing resides in [`crates/editor/src/buffer.rs`](https://github.com/zed-industries/zed/blob/main/crates/editor/src/buffer.rs). This module handles the `replace_all` logic for buffer contents, managing undo history and transactional edits. When regex mode is active, it utilizes the `RegexSearch` iterator to locate matches before applying `String::replace_range` operations.

### Terminal Search Integration

Terminal views implement search through [`crates/terminal/src/terminal_view.rs`](https://github.com/zed-industries/zed/blob/main/crates/terminal/src/terminal_view.rs) (lines 1095-1110). The `RegexSearch` helper was originally designed for terminal use cases, enabling users to search scrollback buffers with regex patterns. The same infrastructure supports replacing text in terminal buffers when the terminal emulator permits editing.

### Vim Mode Compatibility

For users operating in Vim mode, Zed provides integration at [`crates/vim/src/replace.rs`](https://github.com/zed-industries/zed/blob/main/crates/vim/src/replace.rs). This module maps Vim replace commands (such as `:s` substitution) to the core `Searchable` API. When a Vim user executes a regex substitution, the command is translated into a `SearchQuery` with `regex: true` and forwarded to the active view's `replace_all` method.

## Summary

- Zed implements **search and replace with regex support** through the `Searchable` trait, providing a unified API across editors, terminals, and file trees.
- The `SearchQuery` struct captures user input, including a `regex` boolean flag that toggles pattern matching between literal text and regular expressions.
- Regex compilation and iteration are handled by the `RegexSearch` helper using the standard `regex` crate, with early validation to surface syntax errors in the UI.
- Replace operations execute on the foreground GPUI thread via `cx.spawn`, while large-file scanning runs on background threads to maintain UI responsiveness.
- Capture groups and back-references (e.g., `$1`, `$2`) are supported in replacement strings through `regex::Captures::expand`.

## Frequently Asked Questions

### How do I enable regex mode in Zed's search and replace?

Click the regex icon (usually labeled `.*`) in the search bar, or use the keyboard shortcut to toggle regex mode. When enabled, the `regex` field in the underlying `SearchQuery` struct is set to `true`, causing Zed to compile your search pattern using the `regex` crate rather than performing a literal string match.

### Does Zed support capture groups in regex replacements?

Yes. Zed fully supports regex capture groups in replacement strings. When you use parentheses `()` in your search pattern to create groups, you can reference them in the replacement field using `$1`, `$2`, etc. Internally, Zed calls `regex::Captures::expand` to resolve these back-references before applying the replacement via `String::replace_range`.

### What regex engine does Zed use for search and replace?

Zed uses the standard **`regex` crate** (the Rust regex library) for all regular expression operations. This engine is wrapped in a helper struct called `RegexSearch` (found in [`crates/terminal/src/terminal_view.rs`](https://github.com/zed-industries/zed/blob/main/crates/terminal/src/terminal_view.rs)), which provides methods for compiling patterns and iterating over matches while preserving capture group information for replacements.

### Can I use regex search and replace in Zed's integrated terminal?

Yes. The terminal view in Zed implements the `Searchable` trait, enabling regex search functionality within terminal scrollback buffers. The same `RegexSearch` infrastructure used in the editor is utilized here, allowing you to search terminal output with patterns like `error.*\d+` and perform replacements when the terminal emulator supports buffer editing.