Skip to content

Commit

Permalink
Add --smart-case.
Browse files Browse the repository at this point in the history
It does what it says on the tin.

Closes BurntSushi#70.
  • Loading branch information
amsharma91 committed Sep 20, 2016
1 parent 44769ad commit 83107ab
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 1 deletion.
6 changes: 6 additions & 0 deletions doc/rg.1
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ Alias for \-\-color=always \-\-heading \-n.
.RS
.RE
.TP
.B \-S, \-\-smart\-case
Search case insensitively if the pattern is all lowercase.
Search case sensitively otherwise.
.RS
.RE
.TP
.B \-j, \-\-threads \f[I]ARG\f[]
The number of threads to use.
Defaults to the number of logical CPUs (capped at 6).
Expand Down
4 changes: 4 additions & 0 deletions doc/rg.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ the raw speed of grep.
-p, --pretty
: Alias for --color=always --heading -n.

-S, --smart-case
: Search case insensitively if the pattern is all lowercase.
Search case sensitively otherwise.

-j, --threads *ARG*
: The number of threads to use. Defaults to the number of logical CPUs
(capped at 6). [default: 0]
Expand Down
36 changes: 35 additions & 1 deletion grep/src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub struct GrepBuilder {
#[derive(Clone, Debug)]
struct Options {
case_insensitive: bool,
case_smart: bool,
line_terminator: u8,
size_limit: usize,
dfa_size_limit: usize,
Expand All @@ -61,6 +62,7 @@ impl Default for Options {
fn default() -> Options {
Options {
case_insensitive: false,
case_smart: false,
line_terminator: b'\n',
size_limit: 10 * (1 << 20),
dfa_size_limit: 10 * (1 << 20),
Expand Down Expand Up @@ -98,6 +100,18 @@ impl GrepBuilder {
self
}

/// Whether to enable smart case search or not (disabled by default).
///
/// Smart case uses case insensitive search if the regex is contains all
/// lowercase literal characters. Otherwise, a case sensitive search is
/// used instead.
///
/// Enabling the case_insensitive flag overrides this.
pub fn case_smart(mut self, yes: bool) -> GrepBuilder {
self.opts.case_smart = yes;
self
}

/// Set the approximate size limit of the compiled regular expression.
///
/// This roughly corresponds to the number of bytes occupied by a
Expand Down Expand Up @@ -148,8 +162,11 @@ impl GrepBuilder {
/// Creates a new regex from the given expression with the current
/// configuration.
fn regex(&self, expr: &Expr) -> Result<Regex> {
let casei =
self.opts.case_insensitive
|| (self.opts.case_smart && !has_uppercase_literal(expr));
RegexBuilder::new(&expr.to_string())
.case_insensitive(self.opts.case_insensitive)
.case_insensitive(casei)
.multi_line(true)
.unicode(true)
.size_limit(self.opts.size_limit)
Expand Down Expand Up @@ -274,6 +291,23 @@ impl<'b, 's> Iterator for Iter<'b, 's> {
}
}

fn has_uppercase_literal(expr: &Expr) -> bool {
use syntax::Expr::*;
match *expr {
Literal { ref chars, casei } => {
casei || chars.iter().any(|c| c.is_uppercase())
}
LiteralBytes { ref bytes, casei } => {
casei || bytes.iter().any(|&b| b'A' <= b && b <= b'Z')
}
Group { ref e, .. } => has_uppercase_literal(e),
Repeat { ref e, .. } => has_uppercase_literal(e),
Concat(ref es) => es.iter().any(has_uppercase_literal),
Alternate(ref es) => es.iter().any(has_uppercase_literal),
_ => false,
}
}

#[cfg(test)]
mod tests {
#![allow(unused_imports)]
Expand Down
6 changes: 6 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ Less common options:
-p, --pretty
Alias for --color=always --heading -n.
-S, --smart-case
Search case insensitively if the pattern is all lowercase.
Search case sensitively otherwise.
-j, --threads ARG
The number of threads to use. Defaults to the number of logical CPUs
(capped at 6). [default: 0]
Expand Down Expand Up @@ -217,6 +221,7 @@ pub struct RawArgs {
flag_quiet: bool,
flag_regexp: Vec<String>,
flag_replace: Option<String>,
flag_smart_case: bool,
flag_text: bool,
flag_threads: usize,
flag_type: Vec<String>,
Expand Down Expand Up @@ -348,6 +353,7 @@ impl RawArgs {
let types = try!(btypes.build());
let grep = try!(
GrepBuilder::new(&pattern)
.case_smart(self.flag_smart_case)
.case_insensitive(self.flag_ignore_case)
.line_terminator(eol)
.build()
Expand Down
12 changes: 12 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,18 @@ clean!(feature_68, "test", ".", |wd: WorkDir, mut cmd: Command| {
assert_eq!(lines, "foo:test\n");
});

// See: https://github.com/BurntSushi/ripgrep/issues/70
sherlock!(feature_70, "sherlock", ".", |wd: WorkDir, mut cmd: Command| {
cmd.arg("--smart-case");

let lines: String = wd.stdout(&mut cmd);
let expected = "\
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
";
assert_eq!(lines, expected);
});

#[test]
fn binary_nosearch() {
let wd = WorkDir::new("binary_nosearch");
Expand Down

0 comments on commit 83107ab

Please sign in to comment.