mirror of
https://github.com/rtk-ai/rtk.git
synced 2026-09-19 07:33:17 +08:00
0adfae6cf6
The grep argument-parsing rework routed many flag shapes through the grouping reparse, which silently dropped any line it could not parse and returned a success exit. Common invocations produced wrong or empty output -- and grep-only syntax like `--include` failed outright -- which pushed agents back to unfiltered `rtk proxy grep`. Every valid grep invocation now produces correct output: - Context (`-A`/`-B`/`-C`) is grouped and compressed instead of dropped; the header counts matches only and context lines are shown. - Output the reparse cannot represent (`-N`, `-I`, `--color`, `-p`, `--heading`, binary-file notices, ...) is passed through verbatim instead of reported as a false "0 matches". - Format/shape flags (`-c`, `-l`, `-o`, `-Z`, `--column`, `-b`, `--vimgrep`, `--null-data`) pass through cleanly with no NUL leak. - grep-only syntax (`--include`, `--exclude`, ...) works: ripgrep is the fast path and rtk falls back to system grep when ripgrep rejects a flag. - rtk grep never emits more than plain grep: if grouping is not smaller than `file:line:content`, the raw form is shown. Resolves #2543.
112 lines
3.0 KiB
Rust
112 lines
3.0 KiB
Rust
#![cfg(unix)]
|
|
//! Regressions from the #2333 grep overhaul: -A/-B/-C must keep context lines,
|
|
//! -c/-o must not leak ripgrep's NUL separator.
|
|
|
|
use std::process::Command;
|
|
|
|
fn rtk() -> Command {
|
|
Command::new(env!("CARGO_BIN_EXE_rtk"))
|
|
}
|
|
|
|
fn rg_available() -> bool {
|
|
Command::new("rg")
|
|
.arg("--version")
|
|
.output()
|
|
.map(|o| o.status.success())
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
#[test]
|
|
fn after_context_lines_are_shown() {
|
|
if !rg_available() {
|
|
return;
|
|
}
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let f = dir.path().join("ctx.txt");
|
|
std::fs::write(&f, "before1\nbefore2\nMATCHME\nafter1\nafter2\nafter3\n").unwrap();
|
|
let out = rtk()
|
|
.args(["grep", "-A2", "MATCHME", f.to_str().unwrap()])
|
|
.output()
|
|
.unwrap();
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
assert!(
|
|
stdout.contains("after1"),
|
|
"missing after-context line 1:\n{stdout}"
|
|
);
|
|
assert!(
|
|
stdout.contains("after2"),
|
|
"missing after-context line 2:\n{stdout}"
|
|
);
|
|
assert!(!stdout.contains("after3"), "leaked beyond -A2:\n{stdout}");
|
|
}
|
|
|
|
#[test]
|
|
fn before_context_lines_are_shown() {
|
|
if !rg_available() {
|
|
return;
|
|
}
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let f = dir.path().join("ctx.txt");
|
|
std::fs::write(&f, "before1\nbefore2\nMATCHME\nafter1\n").unwrap();
|
|
let out = rtk()
|
|
.args(["grep", "-B1", "MATCHME", f.to_str().unwrap()])
|
|
.output()
|
|
.unwrap();
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
assert!(
|
|
stdout.contains("before2"),
|
|
"missing before-context line:\n{stdout}"
|
|
);
|
|
assert!(!stdout.contains("before1"), "leaked beyond -B1:\n{stdout}");
|
|
}
|
|
|
|
#[test]
|
|
fn count_output_has_no_nul_separator() {
|
|
if !rg_available() {
|
|
return;
|
|
}
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let f = dir.path().join("nul.txt");
|
|
std::fs::write(&f, "TODO one\nnope\nTODO two\n").unwrap();
|
|
let out = rtk()
|
|
.args(["grep", "-c", "TODO", f.to_str().unwrap()])
|
|
.output()
|
|
.unwrap();
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
assert!(
|
|
!stdout.contains('\u{0}'),
|
|
"NUL leaked into -c output: {stdout:?}"
|
|
);
|
|
assert!(
|
|
stdout.contains('2'),
|
|
"count missing from -c output: {stdout:?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn only_matching_output_has_no_nul_separator() {
|
|
if !rg_available() {
|
|
return;
|
|
}
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let f = dir.path().join("nul.txt");
|
|
std::fs::write(&f, "TODO one\nnope\nTODO two\n").unwrap();
|
|
let out = rtk()
|
|
.args(["grep", "-o", "TODO", f.to_str().unwrap()])
|
|
.output()
|
|
.unwrap();
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
assert!(
|
|
!stdout.contains('\u{0}'),
|
|
"NUL leaked into -o output: {stdout:?}"
|
|
);
|
|
assert!(
|
|
stdout.contains("TODO"),
|
|
"match missing from -o output: {stdout:?}"
|
|
);
|
|
assert!(
|
|
!stdout.contains("1:TODO"),
|
|
"line-number prefix leaked into -o output: {stdout:?}"
|
|
);
|
|
}
|