Improve smart case to only consider literal uppercase chars (#103)

This commit is contained in:
Matthias Reitinger 2017-10-13 21:46:00 +02:00 committed by David Peter
parent 8fc3a83d92
commit b441528067
5 changed files with 40 additions and 6 deletions

1
Cargo.lock generated
View File

@ -9,6 +9,7 @@ dependencies = [
"ignore 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -25,6 +25,7 @@ ansi_term = "0.9"
clap = "2.26.0"
atty = "0.2"
regex = "0.2"
regex-syntax = "0.4"
ignore = "0.2"
num_cpus = "1.6.2"

View File

@ -4,6 +4,7 @@ use std::io::Write;
use lscolors::LsColors;
use walk::FileType;
use regex_syntax::{Expr, ExprBuilder};
/// Root directory
#[cfg(unix)]
@ -78,3 +79,28 @@ pub fn error(message: &str) -> ! {
writeln!(&mut ::std::io::stderr(), "{}", message).expect("Failed writing to stderr");
process::exit(1);
}
/// Determine if a regex pattern contains a literal uppercase character.
pub fn pattern_has_uppercase_char(pattern: &str) -> bool {
ExprBuilder::new()
.parse(pattern)
.map(|expr| expr_has_uppercase_char(&expr))
.unwrap_or(false)
}
/// Determine if a regex expression contains a literal uppercase character.
fn expr_has_uppercase_char(expr: &Expr) -> bool {
match *expr {
Expr::Literal { ref chars, .. } => chars.iter().any(|c| c.is_uppercase()),
Expr::Class(ref ranges) => {
ranges.iter().any(|r| {
r.start.is_uppercase() || r.end.is_uppercase()
})
}
Expr::Group { ref e, .. } => expr_has_uppercase_char(e),
Expr::Repeat { ref e, .. } => expr_has_uppercase_char(e),
Expr::Concat(ref es) => es.iter().any(expr_has_uppercase_char),
Expr::Alternate(ref es) => es.iter().any(expr_has_uppercase_char),
_ => false,
}
}

View File

@ -3,6 +3,7 @@ extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate regex;
extern crate regex_syntax;
extern crate ignore;
extern crate num_cpus;
@ -22,7 +23,7 @@ use std::time;
use atty::Stream;
use regex::RegexBuilder;
use internal::{error, FdOptions, PathDisplay, ROOT_DIR};
use internal::{error, pattern_has_uppercase_char, FdOptions, PathDisplay, ROOT_DIR};
use lscolors::LsColors;
use walk::FileType;
@ -65,11 +66,8 @@ fn main() {
// The search will be case-sensitive if the command line flag is set or
// if the pattern has an uppercase character (smart case).
let case_sensitive = if !matches.is_present("ignore-case") {
matches.is_present("case-sensitive") || pattern.chars().any(char::is_uppercase)
} else {
false
};
let case_sensitive = !matches.is_present("ignore-case") &&
(matches.is_present("case-sensitive") || pattern_has_uppercase_char(pattern));
let colored_output = match matches.value_of("color") {
Some("always") => true,

View File

@ -115,6 +115,14 @@ fn test_smart_case() {
te.assert_output(&["C.Foo"], "one/two/C.Foo2");
te.assert_output(&["Foo"], "one/two/C.Foo2");
// Only literal uppercase chars should trigger case sensitivity.
te.assert_output(
&["\\Ac"],
"one/two/c.foo
one/two/C.Foo2",
);
te.assert_output(&["\\AC"], "one/two/C.Foo2");
}
/// Case sensitivity (--case-sensitive)