From 9cb7a5614c7b642aefd43a1915ff0c506ed49020 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Fri, 22 May 2026 04:57:25 -0500 Subject: [PATCH] feat(rg): add max depth aliases --- crates/bashkit/src/builtins/rg/mod.rs | 69 +++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/crates/bashkit/src/builtins/rg/mod.rs b/crates/bashkit/src/builtins/rg/mod.rs index 002539293..865530e33 100644 --- a/crates/bashkit/src/builtins/rg/mod.rs +++ b/crates/bashkit/src/builtins/rg/mod.rs @@ -262,10 +262,12 @@ impl RgOptions { })?); } else if let Some(val) = long_value(&mut p, "--max-filesize")? { opts.max_filesize = Some(parse_max_filesize(&val)?); + } else if let Some(val) = p.flag_value("-d", "rg").map_err(Error::Execution)? { + opts.max_depth = Some(parse_max_depth(val, "-d")?); } else if let Some(val) = long_value(&mut p, "--max-depth")? { - opts.max_depth = Some(val.parse().map_err(|_| { - Error::Execution(format!("rg: invalid --max-depth value: {val}")) - })?); + opts.max_depth = Some(parse_max_depth(&val, "--max-depth")?); + } else if let Some(val) = long_value(&mut p, "--maxdepth")? { + opts.max_depth = Some(parse_max_depth(&val, "--maxdepth")?); } else if let Some(val) = p.flag_value("-A", "rg").map_err(Error::Execution)? { opts.after_context = parse_context_value(val, "-A")?; } else if let Some(val) = p.flag_value("-B", "rg").map_err(Error::Execution)? { @@ -596,6 +598,23 @@ impl RgOptions { })?); break; } + 'd' => { + let rest: String = chars[j + 1..].iter().collect(); + let depth = if !rest.is_empty() { + rest + } else { + match p.positional() { + Some(v) => v.to_string(), + None => { + return Err(Error::Execution( + "rg: -d requires an argument".to_string(), + )); + } + } + }; + opts.max_depth = Some(parse_max_depth(&depth, "-d")?); + break; + } 'M' => { let rest: String = chars[j + 1..].iter().collect(); let num_str = if !rest.is_empty() { @@ -1231,6 +1250,12 @@ fn parse_context_value(value: &str, flag: &str) -> Result { .map_err(|_| Error::Execution(format!("rg: invalid {} value: {}", flag, value))) } +fn parse_max_depth(value: &str, flag: &str) -> Result { + value + .parse() + .map_err(|_| Error::Execution(format!("rg: invalid {flag} value: {value}"))) +} + fn parse_max_filesize(value: &str) -> Result { let (digits, multiplier) = match value.as_bytes().last().copied() { Some(b'K') => (&value[..value.len() - 1], 1024_u64), @@ -2251,6 +2276,10 @@ fn append_rg_stats(output: &mut String, stats: &RgSearchStats, bytes_printed: us impl Builtin for Rg { async fn execute(&self, ctx: Context<'_>) -> Result { let help_text = "Usage: rg [OPTIONS] PATTERN [PATH...]\nRecursively search for a pattern.\n\n -i, --ignore-case\tcase insensitive\n -S, --smart-case\tcase insensitive if pattern is lowercase\n -s, --case-sensitive\tcase sensitive\n -n, --line-number\tshow line numbers\n -N, --no-line-number\tsuppress line numbers\n --column\tshow column numbers\n -b, --byte-offset\tshow byte offsets\n --vimgrep\tshow file:line:column:match lines\n --json\tshow JSON Lines events\n --stats\tshow search statistics\n --null\tterminate path fields with NUL\n -c, --count\tcount matching lines\n --count-matches\tcount individual matches\n --include-zero\tinclude zero counts\n -l, --files-with-matches\tfiles with matches\n --files-without-match\tfiles without matches\n --files\tprint files that would be searched\n -v, --invert-match\tinvert match\n -w, --word-regexp\tword boundary\n -x, --line-regexp\tmatch whole lines\n -F, --fixed-strings\tfixed strings (literal)\n -a, --text\tsearch binary files as text\n --binary\tsearch binary files and print binary-match summaries\n --crlf\ttreat CRLF as line terminators for $ anchors\n --no-crlf\tdisable CRLF line terminator mode\n -U, --multiline\tenable matching across line boundaries\n --no-multiline\tdisable multiline matching\n --multiline-dotall\tmake . match line terminators in multiline mode\n --no-multiline-dotall\tdisable multiline dotall mode\n -o, --only-matching\tshow only matching text\n -q, --quiet\tsuppress output; exit status only\n -e, --regexp PATTERN\tuse PATTERN for matching\n -f, --file PATTERNFILE\tread patterns from file\n -E, --encoding ENCODING\tdecode searched files using ENCODING\n -r, --replace REPLACEMENT\treplace matches in output\n --passthru\tprint matching and non-matching lines\n --trim\ttrim whitespace from output lines\n -m, --max-count NUM\tmax count per file\n -M, --max-columns NUM\tomit lines longer than NUM columns\n --max-columns-preview\tshow prefixes of long lines\n --max-depth NUM\tlimit recursive directory depth\n -A, --after-context NUM\tshow trailing context\n -B, --before-context NUM\tshow leading context\n -C, --context NUM\tshow leading and trailing context\n --context-separator SEP\tset context group separator\n --field-match-separator SEP\tset match field separator\n --field-context-separator SEP\tset context field separator\n --heading\tgroup matches by file\n --no-heading\tdisable heading output\n --sort SORTBY\tsort paths (path only)\n --sortr SORTBY\tsort paths in reverse (path only)\n --sort-files\tsort --files output\n --path-separator SEP\tset displayed path separator\n -g, --glob GLOB\tinclude/exclude paths by glob (!GLOB excludes)\n -t, --type TYPE\tinclude files matching TYPE\n -T, --type-not TYPE\texclude files matching TYPE\n --type-add TYPE:GLOB\tadd a file type glob\n --type-clear TYPE\tclear a file type definition\n --type-list\tshow file type definitions\n --ignore-file FILE\tuse additional ignore file\n --no-ignore\tdo not use ignore files\n --no-ignore-dot\tdo not use .ignore files\n --no-ignore-vcs\tdo not use .gitignore files\n --no-require-git\tuse .gitignore outside git repositories\n --require-git\trequire a git repository for .gitignore files\n -u, --unrestricted\treduce filtering (repeatable)\n --messages\tshow file read diagnostics\n --no-messages\tsuppress file read diagnostics\n --hidden\tsearch hidden files and directories\n --no-hidden\tdo not search hidden files and directories\n -H, --with-filename\tshow filename\n -I, --no-filename\tsuppress filename\n --line-buffered\tforce line buffering (no-op)\n --block-buffered\tforce block buffering (no-op)\n --no-config\tdo not read config files (no-op)\n --mmap\tsearch using memory maps when possible (no-op)\n --no-mmap\tdisable memory maps (no-op)\n -P, --pcre2\tuse PCRE2 regex engine for supported patterns (no-op)\n --no-pcre2\tdisable PCRE2 regex engine (no-op)\n --engine ENGINE\tselect regex engine: default, auto, pcre2 (no-op)\n --auto-hybrid-regex\tuse PCRE2 when needed (no-op)\n --no-auto-hybrid-regex\tdisable auto hybrid regex (no-op)\n --color MODE\tcolor output (no-op)\n -h, --help\tdisplay this help and exit\n -V, --version\toutput version information and exit\n"; + let help_text = help_text.replace( + " --max-depth NUM\tlimit recursive directory depth\n", + " -d, --max-depth NUM\tlimit recursive directory depth\n --maxdepth NUM\talias for --max-depth\n", + ); let help_text = help_text.replace( " --max-columns-preview\tshow prefixes of long lines\n", " --max-columns-preview\tshow prefixes of long lines\n --max-filesize NUM\tignore files larger than NUM bytes, K, M, or G\n", @@ -3589,6 +3618,30 @@ mod tests { cwd: "/", output: RgDiffOutput::UnorderedLines, }, + RgDiffCase { + name: "maxdepth alias", + args: &["--maxdepth=1", "needle", "proj"], + stdin: None, + files: DIFF_BASIC_FILES, + cwd: "/", + output: RgDiffOutput::UnorderedLines, + }, + RgDiffCase { + name: "short max depth attached", + args: &["-d1", "needle", "proj"], + stdin: None, + files: DIFF_BASIC_FILES, + cwd: "/", + output: RgDiffOutput::UnorderedLines, + }, + RgDiffCase { + name: "short max depth zero", + args: &["-d", "0", "needle", "proj"], + stdin: None, + files: DIFF_BASIC_FILES, + cwd: "/", + output: RgDiffOutput::UnorderedLines, + }, RgDiffCase { name: "files list max depth", args: &["--files", "--max-depth", "1", "proj"], @@ -3597,6 +3650,14 @@ mod tests { cwd: "/", output: RgDiffOutput::UnorderedLines, }, + RgDiffCase { + name: "files list short max depth", + args: &["--files", "-d1", "proj"], + stdin: None, + files: DIFF_BASIC_FILES, + cwd: "/", + output: RgDiffOutput::UnorderedLines, + }, RgDiffCase { name: "replace", args: &["--replace", "X", "needle", "proj/a.txt"], @@ -4735,6 +4796,8 @@ mod tests { assert!(long_help.stdout.contains("--multiline-dotall")); assert!(long_help.stdout.contains("--no-ignore-files")); assert!(long_help.stdout.contains("--ignore-file-case-insensitive")); + assert!(long_help.stdout.contains("-d, --max-depth")); + assert!(long_help.stdout.contains("--maxdepth")); let short_help = run_rg(&["-h"], None, &[]).await; assert_eq!(short_help.exit_code, 0);