Open-Source Wikis

/

ripgrep

/

ripgrep

/

Architecture

BurntSushi/ripgrep

Architecture

ripgrep is organised as a Cargo workspace. The rg binary lives in crates/core and depends on a stack of nine library crates. Most of ripgrep's behaviour is in the libraries; the binary is glue.

High-level component diagram

graph TD
    User["User invocation: rg PATTERN PATH"] --> CLI[crates/core flags<br/>parse + lowargs + hiargs]
    CLI --> Walker["ignore crate<br/>WalkBuilder"]
    CLI --> Matcher["matcher trait"]
    Matcher --> Regex["grep-regex<br/>Rust regex crate"]
    Matcher --> Pcre2["grep-pcre2<br/>PCRE2 (optional)"]
    Walker --> Decompress["grep-cli<br/>decompress"]
    Walker --> Searcher["grep-searcher<br/>line-oriented search"]
    Decompress --> Searcher
    Matcher --> Searcher
    Searcher --> Printer["grep-printer<br/>Standard / JSON / Summary"]
    Printer --> Stdout[stdout]

    subgraph workspace[Cargo workspace]
        Walker
        Matcher
        Regex
        Pcre2
        Searcher
        Printer
        Decompress
    end

Each box is a crate. The arrows show direct dependencies for a search invocation. globset is used internally by ignore for glob and gitignore matching. The grep crate (not shown) is a thin facade that re-exports the public APIs of the other crates.

Crate dependency graph

graph LR
    rg[ripgrep binary] --> ignore
    rg --> grep
    grep --> grep_cli[grep-cli]
    grep --> grep_matcher[grep-matcher]
    grep --> grep_pcre2[grep-pcre2]
    grep --> grep_printer[grep-printer]
    grep --> grep_regex[grep-regex]
    grep --> grep_searcher[grep-searcher]
    grep_searcher --> grep_matcher
    grep_regex --> grep_matcher
    grep_pcre2 --> grep_matcher
    grep_printer --> grep_matcher
    grep_printer --> grep_searcher
    ignore --> globset

Source: Cargo.toml files for each crate.

Search lifecycle

sequenceDiagram
    autonumber
    participant Main as crates/core/main.rs
    participant Hi as HiArgs
    participant Walk as ignore::WalkBuilder
    participant Search as grep_searcher::Searcher
    participant Match as grep_matcher::Matcher
    participant Print as grep_printer::Standard

    Main->>Hi: flags::parse() + HiArgs::from_low_args
    Main->>Walk: hi.walk_builder()?.build_parallel()
    loop for each path yielded by Walk
        Walk->>Main: DirEntry
        Main->>Search: searcher.search(&haystack)
        Search->>Match: matcher.find_iter / find
        Match-->>Search: Match ranges
        Search->>Print: sink_match / sink_context
        Print-->>Main: write to stdout via BufferWriter
    end

The two top-level entry points in crates/core/main.rs are search (single-threaded) and search_parallel (multi-threaded). They differ only in how they iterate the walker; the per-haystack work is identical.

Threading model

ripgrep parallelises directory traversal. WalkBuilder::build_parallel creates a pool of worker threads (default = number of CPU cores, configurable via -j/--threads). Each worker calls a closure that:

  1. Builds a per-thread clone of the search worker (SearchWorker).
  2. Reads the file into a buffer or memory map.
  3. Runs the matcher and emits matches into a per-thread termcolor::Buffer.
  4. Hands the buffer to a single BufferWriter so multiple workers can't tear each other's output.

crates/core/main.rs (functions search_parallel and files_parallel) orchestrates this. The WalkParallel type lives in crates/ignore/src/walk.rs and uses crossbeam channels under the hood.

When --sort is requested, parallelism is disabled because output ordering must follow the sort key. This is enforced inside HiArgs::threads (crates/core/flags/hiargs.rs).

How filtering decisions are made

crates/ignore/src/walk.rs (the largest single file in the project at ~85K) walks the directory tree. For every directory entry it asks a stack of matchers, in priority order:

  1. Command-line --glob/--iglob overrides (crates/ignore/src/overrides.rs)
  2. File-type filters from -t/-T (crates/ignore/src/types.rs)
  3. The user's global gitignore (core.excludesFile, the git config value)
  4. .gitignore / .ignore / .rgignore files in each directory (crates/ignore/src/gitignore.rs)
  5. Hidden-file rules (skipped unless --hidden)

Whichever matcher first emits an explicit "ignore" or "whitelist" decision wins. A directory matched as ignored is pruned, which is what makes ripgrep fast on repos with deeply nested ignored trees like node_modules.

Encoding and binary detection

By default ripgrep:

  • Treats input as bytes that may contain UTF-8 (the regex engine handles Unicode classes natively).
  • Uses heuristic binary detection: if a NUL byte appears in a file, the rest of the file is skipped (BinaryDetection::quit). Toggle with --binary / --text.
  • For files declared --encoding, transcodes via encoding_rs before searching.

This logic lives in crates/searcher/src/searcher/mod.rs and is configured by SearcherBuilder.

Configuration

There is no run-time config server. ripgrep reads its config from RIPGREP_CONFIG_PATH if set; the file contains one CLI flag per line and is consumed during parsing in crates/core/flags/config.rs. See Configuration for the full grammar.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – ripgrep wiki | Factory