diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f0177d1..b6ece534 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Unreleased changes. Release notes have not yet been written. Performance improvements: +* [PERF #2111](https://github.com/BurntSushi/ripgrep/issues/2111): + Don't resolve helper binaries on Windows when `-z/--search-zip` isn't used. * [PERF #2865](https://github.com/BurntSushi/ripgrep/pull/2865): Avoid using path canonicalization on Windows when emitting hyperlinks. @@ -46,6 +48,8 @@ Bug fixes: Statically compile PCRE2 into macOS release artifacts on `aarch64`. * [BUG #3173](https://github.com/BurntSushi/ripgrep/issues/3173): Fix ancestor ignore filter bug when searching whitelisted hidden files. +* [BUG #3180](https://github.com/BurntSushi/ripgrep/issues/3180): + Fix a panicking bug when using `-U/--multiline` and `-r/--replace`. Feature enhancements: diff --git a/crates/printer/src/util.rs b/crates/printer/src/util.rs index 4fd96cc2..491a0b7e 100644 --- a/crates/printer/src/util.rs +++ b/crates/printer/src/util.rs @@ -59,7 +59,8 @@ impl Replacer { // See the giant comment in 'find_iter_at_in_context' below for why we // do this dance. let is_multi_line = searcher.multi_line_with_matcher(&matcher); - // Get the line_terminator that was removed (if any) so we can add it back + // Get the line_terminator that was removed (if any) so we can add it + // back. let line_terminator = if is_multi_line { if haystack[range.end..].len() >= MAX_LOOK_AHEAD { haystack = &haystack[..range.end + MAX_LOOK_AHEAD]; @@ -513,7 +514,8 @@ where // Otherwise, it's possible for the regex (via look-around) to observe // the line terminator and not match because of it. let mut m = Match::new(0, range.end); - // No need to rember the line terminator as we aren't doing a replace here + // No need to rember the line terminator as we aren't doing a replace + // here. trim_line_terminator(searcher, bytes, &mut m); bytes = &bytes[..m.end()]; } @@ -575,9 +577,13 @@ where last_match = m.end(); append(caps, dst) })?; - let end = std::cmp::min(bytes.len(), range.end); + let end = if last_match > range.end { + bytes.len() + } else { + std::cmp::min(bytes.len(), range.end) + }; dst.extend(&bytes[last_match..end]); - // Add back any line terminator + // Add back any line terminator. dst.extend(line_terminator); Ok(()) } diff --git a/tests/regression.rs b/tests/regression.rs index f3ba63b0..93e4ba67 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -1654,3 +1654,16 @@ rgtest!(r3173_hidden_whitelist_only_dot, |dir: Dir, _: TestCommand| { eqnice!(cmd().args(&["--files", "."]).stdout(), "./.foo.txt\n"); eqnice!(cmd().args(&["--files", "./"]).stdout(), "./.foo.txt\n"); }); + +// See: https://github.com/BurntSushi/ripgrep/issues/3180 +rgtest!(r3180_look_around_panic, |dir: Dir, mut cmd: TestCommand| { + dir.create("haystack", " b b b b b b b b\nc\n"); + + let got = cmd + .arg(r#"(^|[^a-z])((([a-z]+)?)\s)?b(\s([a-z]+)?)($|[^a-z])"#) + .arg("haystack") + .arg("-U") + .arg("-rx") + .stdout(); + eqnice!("xbxbx\n", got); +});