`-U --count-matches` counts one cross-line match once per covered line and disagrees with `--stats`
Los mantenedores suelen responder en 1 día
Ya se ha fusionado un pull request relacionado.
- #192 de @shengyfu — fusionado
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 45/100
Línea de trabajo
Start in tgrep-cli/src/matching.rs at FileMatches::find, where match_totals keeps the original count, and at match_count, which sums per-line spans. The client count in tgrep-cli/src/search.rs (around lines 1224 and 2119) and the stats path in serve.rs (around line 2170) also need checking. Run the existing stats_multiline_counts_original_matches_before_rendering test in tests/ripgrep_compat.rs. Done when hello\nworld over hello\nworld\n prints 1 with --count-matches on scan, local-index and server paths, with and without --stats, and the single-line and independent-match controls are unchanged.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
With -U/--multiline, --count-matches counts the per-line pieces of a match instead of the original regex match. A single match spanning two lines is reported as 2, while ripgrep reports 1.
For a file containing hello\nworld\n, the pattern hello\nworld matches exactly once. tgrep's --stats correctly reports 1 matches (2 matched lines), but the same command prints 2 for --count-matches.
This happens with --no-index, with the on-disk index, and through tgrep serve.
Related to #185: the adjacent-match example from that issue now correctly counts two matches after #187. This report concerns one match spanning multiple lines.
Reproduce
mkdir repro && cd repro
printf 'hello\nworld\n' > a.txt
tgrep --no-index -U --count-matches -- 'hello\nworld' a.txt
rg -U --count-matches -- 'hello\nworld' a.txt
tgrep --no-index -U --count-matches --stats -- 'hello\nworld' a.txt
# Controls: one single-line match, then two independent matches
tgrep --no-index -U --count-matches -- 'hello' a.txt
tgrep --no-index -U --count-matches -- 'hello|world' a.txt
| Query | Actual: tgrep | Expected: rg 15.1.0 |
|---|---|---|
-U --count-matches 'hello\nworld' |
2 |
1 |
-U --count-matches --stats 'hello\nworld' |
stdout: 2; stats: 1 matches (2 matched lines) |
stdout: 1 |
-U --count-matches 'hello' |
1 |
1 |
| `-U --count-matches 'hello | world'` | 2 |
All commands above exit 0.
The indexed query gives the same incorrect count:
tgrep index .
tgrep -U --count-matches -- 'hello\nworld' .
For the server path, start tgrep serve --no-watch . in another terminal and repeat the directory query. Adding --stats confirms (via server) and still reports one match while stdout counts two.
Environment
- tgrep 1.1.0, built from b614b8c51e76607abffaf73239c42365f0e3c1ff (current
main, including #187) - Release build:
cargo build --release --locked -p tgrep-cli - rustc 1.95.0 (59807616e 2026-04-14)
- Windows 11 Pro (build 28000); the counting code involved is platform-independent
- ripgrep 15.1.0, checked with its default engine and
--pcre2 - The incorrect tgrep count was confirmed across scan, local-index, and server paths with both
--engine defaultand--engine pcre2
Cause
In multiline mode, FileMatches::find collects the original spans, then passes them to group_spans_by_line. It already saves the original match count in match_totals:
group_spans_by_line clips each original span onto every line it covers. The single (0, 11) span for hello\nworld becomes one span on the hello line and one on the world line.
However, FileMatches::match_count sums the spans in those per-line hits. The scan and local-index output path uses that method for --count-matches:
The server client independently reconstructs the count by summing spans from each returned line, producing the same result:
Server stats instead use the original match_totals, which explains the disagreement:
Possible fix
Use the original regex match count, after applying the search limit and before splitting spans for presentation, for non-inverted multiline --count-matches. The local path already retains that count in FileMatches::match_totals. Carry the original per-file count through server responses independently of --stats, rather than reconstructing it from rendered lines. Preserve the existing behavior for inverted matching.
The existing stats_multiline_counts_original_matches_before_rendering test includes --count-matches, but it asserts the stats totals and output parity with/without --stats, not the expected stdout count.
A regression could assert that hello\nworld over hello\nworld\n prints 1 with --count-matches, with and without --stats, across scan, local-index, and server paths. The single-line, independent-match, and adjacent-match controls should retain their existing counts.
- Lenguaje dominante
- Rust
- Estrellas
- 3.5k
- Forks
- 140
- Merge medio
- 14 h 29 min
- PR fusionados (30 d)
- 24
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/tgrep
-
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
Todos los issues de microsoft/tgrep
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
nervosnetwork/fiber#1723 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 69/100
Los mantenedores suelen responder en 1 día
-
needs triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 71/100
pbakaus/impeccable#1314 ·
Los mantenedores suelen responder en 1 día
-
marmite skill: `curl ... | sh` is the first install method, bypassing review and integrity checksAbierto
Dificultad 1/5 1-3 horas Aptitud para principiantes 78/100
rochacbruno/marmite#565 ·