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
endEach 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 --> globsetSource: 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
endThe 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:
- Builds a per-thread clone of the search worker (
SearchWorker). - Reads the file into a buffer or memory map.
- Runs the matcher and emits matches into a per-thread
termcolor::Buffer. - Hands the buffer to a single
BufferWriterso 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:
- Command-line
--glob/--igloboverrides (crates/ignore/src/overrides.rs) - File-type filters from
-t/-T(crates/ignore/src/types.rs) - The user's global gitignore (
core.excludesFile, thegit configvalue) .gitignore/.ignore/.rgignorefiles in each directory (crates/ignore/src/gitignore.rs)- 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 viaencoding_rsbefore 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
- Packages tour — what each crate does in detail
- Features: gitignore handling
- Features: file typing
- Features: PCRE2 support
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.