Open-Source Wikis

/

ripgrep

/

Features

/

CLI flags

BurntSushi/ripgrep

CLI flags

ripgrep's CLI is unusually large (over 100 flags) and unusually well-documented (rg --help long-form is ~5000 lines). Both come out of a single registry of Flag implementations.

The pipeline

graph LR
    Argv["std::env::args_os()"] --> Config["read RIPGREP_CONFIG_PATH"]
    Config --> Lex["lexopt::Parser"]
    Lex --> Dispatch["match flag name → Flag::update"]
    Dispatch --> Low["LowArgs"]
    Low --> Hi["HiArgs::from_low_args"]
    Hi --> Run["main.rs::run"]

Every box maps to one file:

  • argv read in crates/core/main.rs::main
  • config-file parse in crates/core/flags/config.rs
  • lexing and dispatch in crates/core/flags/parse.rs
  • flag table in crates/core/flags/defs.rs
  • low-level args struct in crates/core/flags/lowargs.rs
  • high-level args struct in crates/core/flags/hiargs.rs

The Flag trait

Defined in crates/core/flags/mod.rs. Every flag is a unit struct that implements:

trait Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
    fn is_switch(&self) -> bool;
    fn name_short(&self) -> Option<u8> { None }
    fn name_long(&self) -> &'static str;
    fn aliases(&self) -> &'static [&'static str] { &[] }
    fn name_negated(&self) -> Option<&'static str> { None }
    fn doc_variable(&self) -> Option<&'static str> { None }
    fn doc_category(&self) -> Category;
    fn doc_short(&self) -> &'static str;
    fn doc_long(&self) -> &'static str;
    fn doc_choices(&self) -> &'static [&'static str] { &[] }
    fn completion_type(&self) -> CompletionType { CompletionType::Other }
    fn update(&self, value: FlagValue, args: &mut LowArgs) -> anyhow::Result<()>;
}

The interesting ones:

  • name_long: every flag has a long name (>=2 bytes).
  • name_short: optional one-byte short name.
  • name_negated: if set, this flag also accepts a "no-" form (--no-line-number for --line-number, etc.).
  • doc_short and doc_long: short text for rg --help, long text in mdoc/mdoc-style for the man page.
  • doc_category: which --help section the flag lands in.
  • update: receives the parsed value and updates LowArgs. The convention is to do only parsing/validation here, not heavy work (e.g., --hostname-bin doesn't run the binary; that happens later).

Category

enum Category {
    Input,         // patterns, haystacks, stdin
    Search,        // -i, --multiline, -w, ...
    Filter,        // .gitignore, file types, -g, --hidden, ...
    Output,        // line numbers, columns, colors, hyperlinks
    OutputModes,   // -c/--count, -l/--files-with-matches, --json, --files
    Logging,       // --debug, --trace, --quiet, --stats
    OtherBehaviors, // --type-list, --pcre2-version, --generate
}

rg --help and the man page group flags by category.

Negation flags

Almost every "switch" flag has a negation. The negation does not require its own struct:

fn name_negated(&self) -> Option<&'static str> { Some("no-line-number") }

The parser routes --no-line-number to the same Flag::update with FlagValue::Switch(false).

LowArgs vs. HiArgs

This is the crucial layering:

  • LowArgs (crates/core/flags/lowargs.rs): flat struct with one field per flag. Filled in directly by Flag::update. Knows nothing about config files, environment, or cross-flag interactions.
  • HiArgs (crates/core/flags/hiargs.rs): the resolved struct. Built from LowArgs via HiArgs::from_low_args. Resolves things like:
    • "case sensitivity" = combination of -i, -S, --case-sensitive.
    • "threads to use" = -j, fall back to num CPUs, force to 1 if --sort is set.
    • "stdout writer" = picks line-buffered or block-buffered termcolor stream based on whether stdout is a tty.
    • "matcher" = constructs a RegexMatcher or PCRE2 matcher with all the right options.

HiArgs is the API that crates/core/main.rs and crates/core/search.rs use. LowArgs is internal.

Config file

RIPGREP_CONFIG_PATH=/path/to/config makes ripgrep parse that file before argv. Each line of the file is treated as one argv element:

# my-config
--smart-case
--hidden
--no-messages
--colors=path:fg:cyan

Implementation in crates/core/flags/config.rs. Lines starting with # are comments. Equivalent to passing those flags on the command line; argv overrides config-file values for non-cumulative flags.

--no-config skips the config file. RIPGREP_CONFIG_PATH= (empty) also skips.

--generate modes

The same Flag registry powers four generators:

Mode Flag Output
Man page --generate man roff source for man rg
Bash completion --generate complete-bash complete -F _rg rg script
Zsh completion --generate complete-zsh _rg zsh function
Fish completion --generate complete-fish complete --command rg ...
PowerShell completion --generate complete-powershell Register-ArgumentCompleter block

The man page generator is crates/core/flags/doc/man.rs. It walks the flag registry, sorts by category, and emits mdoc-style sections. The completion generators are in crates/core/flags/complete/{bash,zsh,fish,powershell}.rs.

The 15.0.0 release added improvements for --hyperlink-format completions in bash/fish (FEATURE #3096) and zsh (FEATURE #3102), and made fish completions take config files into account (FEATURE #2708).

Parsing details

crates/core/flags/parse.rs uses lexopt to tokenise argv. Tokenisation is straightforward, but the dispatch step is interesting:

  • Long flags hash-lookup by their name_long() plus aliases().
  • Short flags hash-lookup by their name_short().
  • Negations are recognised when a long flag starts with --no- (and the matching positive flag has a name_negated).

The ParseResult<T> enum encodes the three outcomes:

  • ParseResult::Ok(args) — normal parse.
  • ParseResult::Err(err) — parse failed.
  • ParseResult::Special(SpecialMode::HelpShort) etc. — short-circuit for --help, --version, --pcre2-version. Skips the rest of initialisation so --help always works even if the working directory is broken.

Reference: file & function pointers

What Where
Flag trait + Category enum crates/core/flags/mod.rs
All flag definitions (~235K) crates/core/flags/defs.rs
Parser crates/core/flags/parse.rs
Config-file reader crates/core/flags/config.rs
LowArgs crates/core/flags/lowargs.rs
HiArgs crates/core/flags/hiargs.rs
Man page generator crates/core/flags/doc/man.rs
--help short / long generators crates/core/flags/doc/help.rs
Version generator crates/core/flags/doc/version.rs
Bash / zsh / fish / PowerShell completion generators crates/core/flags/complete/

Adding a new flag

  1. Add a struct to defs.rs: struct MyFlag; and impl Flag for MyFlag { ... }.
  2. Register it in the master FLAGS slice at the bottom of the file.
  3. Add a field to LowArgs for the parsed value.
  4. If the flag affects search behaviour, plumb it into HiArgs::from_low_args.
  5. Wire it into wherever the corresponding subsystem reads the value (matcher, searcher, walker, printer).
  6. Add an integration test in tests/feature.rs covering both the flag and (if applicable) its negation.
  7. Add a CHANGELOG.md bullet.

For style conventions see patterns and conventions.

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

CLI flags – ripgrep wiki | Factory