args: refactor to permit adding other engines

This is in preparation for adding a new --engine flag which is intended
to eventually supplant --auto-hybrid-regex.

While there are no immediate plans to add more regex engines to ripgrep,
this is intended to make it easier to maintain a patch to ripgrep with
an additional regex engine. See #1488 for more details.
This commit is contained in:
pierrenn 2020-02-28 00:47:34 +09:00 committed by Andrew Gallant
parent 1856cda77b
commit aab3d80374
No known key found for this signature in database
GPG Key ID: B2E3A4923F8B0D44

View File

@ -576,27 +576,64 @@ impl ArgMatches {
/// ///
/// If there was a problem building the matcher (e.g., a syntax error), /// If there was a problem building the matcher (e.g., a syntax error),
/// then this returns an error. /// then this returns an error.
#[cfg(feature = "pcre2")]
fn matcher(&self, patterns: &[String]) -> Result<PatternMatcher> { fn matcher(&self, patterns: &[String]) -> Result<PatternMatcher> {
if self.is_present("pcre2") { if self.is_present("pcre2") {
self.matcher_engine("pcre2", patterns)
} else if self.is_present("auto-hybrid-regex") {
self.matcher_engine("auto", patterns)
} else {
self.matcher_engine("default", patterns)
}
}
/// 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.
fn matcher_engine(
&self,
engine: &str,
patterns: &[String],
) -> Result<PatternMatcher> {
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)?; let matcher = self.matcher_pcre2(patterns)?;
Ok(PatternMatcher::PCRE2(matcher)) Ok(PatternMatcher::PCRE2(matcher))
} else if self.is_present("auto-hybrid-regex") { }
#[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) { let rust_err = match self.matcher_rust(patterns) {
Ok(matcher) => return Ok(PatternMatcher::RustRegex(matcher)), Ok(matcher) => {
return Ok(PatternMatcher::RustRegex(matcher));
}
Err(err) => err, Err(err) => err,
}; };
log::debug!( log::debug!(
"error building Rust regex in hybrid mode:\n{}", "error building Rust regex in hybrid mode:\n{}",
rust_err, rust_err,
); );
let pcre_err = match self.matcher_pcre2(patterns) {
Ok(matcher) => return Ok(PatternMatcher::PCRE2(matcher)), let pcre_err = match self.matcher_engine("pcre2", patterns) {
Ok(matcher) => return Ok(matcher),
Err(err) => err, Err(err) => err,
}; };
Err(From::from(format!( Err(From::from(format!(
"regex could not be compiled with either the default regex \ "regex could not be compiled with either the default \
engine or with PCRE2.\n\n\ regex engine or with PCRE2.\n\n\
default regex engine error:\n{}\n{}\n{}\n\n\ default regex engine error:\n{}\n{}\n{}\n\n\
PCRE2 regex engine error:\n{}", PCRE2 regex engine error:\n{}",
"~".repeat(79), "~".repeat(79),
@ -604,32 +641,14 @@ impl ArgMatches {
"~".repeat(79), "~".repeat(79),
pcre_err, pcre_err,
))) )))
} else {
let matcher = match self.matcher_rust(patterns) {
Ok(matcher) => matcher,
Err(err) => {
return Err(From::from(suggest_pcre2(err.to_string())));
} }
}; _ => Err(From::from(format!(
Ok(PatternMatcher::RustRegex(matcher)) "unrecognized regex engine '{}'",
engine
))),
} }
} }
/// Return the matcher that should be used for searching.
///
/// 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<PatternMatcher> {
if self.is_present("pcre2") {
return Err(From::from(
"PCRE2 is not available in this build of ripgrep",
));
}
let matcher = self.matcher_rust(patterns)?;
Ok(PatternMatcher::RustRegex(matcher))
}
/// Build a matcher using Rust's regex engine. /// Build a matcher using Rust's regex engine.
/// ///
/// If there was a problem building the matcher (such as a regex syntax /// If there was a problem building the matcher (such as a regex syntax
@ -1676,24 +1695,43 @@ 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 /// 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 /// believed to correspond to a syntax error that PCRE2 could handle, then
/// add a message to suggest the use of -P/--pcre2. /// add a message to suggest the use of -P/--pcre2.
fn suggest_pcre2(msg: &str) -> Option<String> {
#[cfg(feature = "pcre2")] #[cfg(feature = "pcre2")]
fn suggest_pcre2(msg: String) -> String { fn suggest(msg: &str) -> Option<String> {
if !msg.contains("backreferences") && !msg.contains("look-around") { if !msg.contains("backreferences") && !msg.contains("look-around") {
msg None
} else { } else {
format!( Some(format!(
"{} "{}
Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences
and look-around.", and look-around.",
msg msg
) ))
} }
} }
#[cfg(not(feature = "pcre2"))]
fn suggest(_: &str) -> Option<String> {
None
}
suggest(msg)
}
fn suggest_multiline(msg: String) -> String { fn suggest_multiline(msg: String) -> String {
if msg.contains("the literal") && msg.contains("not allowed") { if msg.contains("the literal") && msg.contains("not allowed") {
format!( format!(