Repository navigation
Description
Summary
A regex that matches across a newline is found successfully, but --replace does not replace it. Adding --only-matching produces empty stdout instead of the expanded replacement. The server path also reports exit 1 for that empty-output case, while scan and local-index paths exit 0.
This persists in v1.2.0 after #192 fixed #188's cross-line --count-matches totals. This report concerns replacement output.
Reproduce
mkdir repro-multiline-replace && cd repro-multiline-replace
mkdir data
printf 'hello\nworld\n' > data/a.txt
tgrep --no-index -I -U -r X -- 'hello\nworld' data
rg -I -U -r X -- 'hello\nworld' data
tgrep --no-index -I -U -o -r '${2}/${1}' -- '(hello)\n(world)' data
rg -I -U -o -r '${2}/${1}' -- '(hello)\n(world)' data| Query | Actual: tgrep 1.2.0 | Expected: rg 15.1.0 |
|---|---|---|
Cross-line match, -r X |
hello\nworld\n, exit 0 |
X\n, exit 0 |
Cross-line captures, -o -r '${2}/${1}' |
Empty stdout; scan/local exit 0, server exit 1 | world/hello\n, exit 0 |
The content-search cases reproduce with --no-index, an on-disk index, and tgrep serve --no-watch.
To repeat the indexed cases, run tgrep index data, remove --no-index, and keep the directory path data. For the server cases, start tgrep serve --no-watch data in another terminal and repeat those directory searches. --stats was used in the Windows harness to verify the selected backend, including (via server).
Environment
- tgrep 1.2.0, official Windows x86_64 release.
- Source inspected at release commit
3ba6ee02537281b68ee1366bb40df9f8b7cc54f0. - Windows; test harness run with PowerShell 7.
- ripgrep 15.1.0 (af60c2de9d).
- Verified on 2026-10-12. The shell examples describe the same fixture bytes and arguments tested on Windows.
Cause
tgrep-cli/src/matching.rs:830 retrieves the text for one line and calls matcher.expansions(line, rep) for -o -r. The normal replacement branch likewise calls matcher.replace_all(line, rep) in tgrep-cli/src/matching.rs:875.
Re-running the original cross-line regex on each individual line cannot recover a match that requires both lines.
Possible fix
Expand replacements from the original match/capture ranges before splitting the result into display lines. Preserve one logical cross-line replacement, capture expansion, and match-based exit status independently of whether rendering emitted a line.
A regression could assert both outputs and exit codes above with and without -o, across all three backends.
Activity
Fix ripgrep compatibility issues #194–#201 (#202)