diff --git a/crates/core/args.rs b/crates/core/args.rs index d99e777d..17f08b9f 100644 --- a/crates/core/args.rs +++ b/crates/core/args.rs @@ -576,58 +576,77 @@ impl ArgMatches { /// /// If there was a problem building the matcher (e.g., a syntax error), /// then this returns an error. - #[cfg(feature = "pcre2")] fn matcher(&self, patterns: &[String]) -> Result { if self.is_present("pcre2") { - let matcher = self.matcher_pcre2(patterns)?; - Ok(PatternMatcher::PCRE2(matcher)) + self.matcher_engine("pcre2", patterns) } else if self.is_present("auto-hybrid-regex") { - let rust_err = match self.matcher_rust(patterns) { - Ok(matcher) => return Ok(PatternMatcher::RustRegex(matcher)), - Err(err) => err, - }; - log::debug!( - "error building Rust regex in hybrid mode:\n{}", - rust_err, - ); - let pcre_err = match self.matcher_pcre2(patterns) { - Ok(matcher) => return Ok(PatternMatcher::PCRE2(matcher)), - Err(err) => err, - }; - Err(From::from(format!( - "regex could not be compiled with either the default regex \ - engine or with PCRE2.\n\n\ - default regex engine error:\n{}\n{}\n{}\n\n\ - PCRE2 regex engine error:\n{}", - "~".repeat(79), - rust_err, - "~".repeat(79), - pcre_err, - ))) + self.matcher_engine("auto", patterns) } else { - let matcher = match self.matcher_rust(patterns) { - Ok(matcher) => matcher, - Err(err) => { - return Err(From::from(suggest_pcre2(err.to_string()))); - } - }; - Ok(PatternMatcher::RustRegex(matcher)) + self.matcher_engine("default", patterns) } } - /// Return the matcher that should be used for searching. + /// Return the matcher that should be used for searching using engine + /// as the engine for the patterns. /// /// If there was a problem building the matcher (e.g., a syntax error), /// then this returns an error. - #[cfg(not(feature = "pcre2"))] - fn matcher(&self, patterns: &[String]) -> Result { - if self.is_present("pcre2") { - return Err(From::from( + fn matcher_engine( + &self, + engine: &str, + patterns: &[String], + ) -> Result { + match engine { + "default" => { + let matcher = match self.matcher_rust(patterns) { + Ok(matcher) => matcher, + Err(err) => { + return Err(From::from(suggest(err.to_string()))); + } + }; + Ok(PatternMatcher::RustRegex(matcher)) + } + #[cfg(feature = "pcre2")] + "pcre2" => { + let matcher = self.matcher_pcre2(patterns)?; + Ok(PatternMatcher::PCRE2(matcher)) + } + #[cfg(not(feature = "pcre2"))] + "pcre2" => Err(From::from( "PCRE2 is not available in this build of ripgrep", - )); + )), + "auto" => { + let rust_err = match self.matcher_rust(patterns) { + Ok(matcher) => { + return Ok(PatternMatcher::RustRegex(matcher)); + } + Err(err) => err, + }; + log::debug!( + "error building Rust regex in hybrid mode:\n{}", + rust_err, + ); + + let pcre_err = match self.matcher_engine("pcre2", patterns) { + Ok(matcher) => return Ok(matcher), + Err(err) => err, + }; + Err(From::from(format!( + "regex could not be compiled with either the default \ + regex engine or with PCRE2.\n\n\ + default regex engine error:\n{}\n{}\n{}\n\n\ + PCRE2 regex engine error:\n{}", + "~".repeat(79), + rust_err, + "~".repeat(79), + pcre_err, + ))) + } + _ => Err(From::from(format!( + "unrecognized regex engine '{}'", + engine + ))), } - let matcher = self.matcher_rust(patterns)?; - Ok(PatternMatcher::RustRegex(matcher)) } /// Build a matcher using Rust's regex engine. @@ -1676,22 +1695,41 @@ impl ArgMatches { } } +/// Inspect an error resulting from building a Rust regex matcher, and if it's +/// believed to correspond to a syntax error that another engine could handle, +/// then add a message to suggest the use of the engine flag. +fn suggest(msg: String) -> String { + if let Some(pcre_msg) = suggest_pcre2(&msg) { + return pcre_msg; + } + msg +} + /// Inspect an error resulting from building a Rust regex matcher, and if it's /// believed to correspond to a syntax error that PCRE2 could handle, then /// add a message to suggest the use of -P/--pcre2. -#[cfg(feature = "pcre2")] -fn suggest_pcre2(msg: String) -> String { - if !msg.contains("backreferences") && !msg.contains("look-around") { - msg - } else { - format!( - "{} +fn suggest_pcre2(msg: &str) -> Option { + #[cfg(feature = "pcre2")] + fn suggest(msg: &str) -> Option { + if !msg.contains("backreferences") && !msg.contains("look-around") { + None + } else { + Some(format!( + "{} Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences and look-around.", - msg - ) + msg + )) + } } + + #[cfg(not(feature = "pcre2"))] + fn suggest(_: &str) -> Option { + None + } + + suggest(msg) } fn suggest_multiline(msg: String) -> String {