1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-04-24 17:12:16 +02:00

Add -s/--case-sensitive flag.

This flag overrides both --smart-case and --ignore-case.

Closes #124.
This commit is contained in:
Andrew Gallant 2016-09-28 16:30:57 -04:00
parent 316ffd87b3
commit 925d0db9f0
5 changed files with 96 additions and 37 deletions

View File

@ -70,6 +70,7 @@ Show this usage message.
.TP .TP
.B \-i, \-\-ignore\-case .B \-i, \-\-ignore\-case
Case insensitive search. Case insensitive search.
Overridden by \-\-case\-sensitive.
.RS .RS
.RE .RE
.TP .TP
@ -209,6 +210,12 @@ Follow symlinks.
.RS .RS
.RE .RE
.TP .TP
.B \-\-maxdepth \f[I]NUM\f[]
Descend at most NUM directories below the command line arguments.
A value of zero searches only the starting\-points themselves.
.RS
.RE
.TP
.B \-\-mmap .B \-\-mmap
Search using memory maps when possible. Search using memory maps when possible.
This is enabled by default when ripgrep thinks it will be faster. This is enabled by default when ripgrep thinks it will be faster.
@ -252,9 +259,16 @@ Alias for \-\-color=always \-\-heading \-n.
.RS .RS
.RE .RE
.TP .TP
.B \-s, \-\-case\-sensitive
Search case sensitively.
This overrides \-\-ignore\-case and \-\-smart\-case.
.RS
.RE
.TP
.B \-S, \-\-smart\-case .B \-S, \-\-smart\-case
Search case insensitively if the pattern is all lowercase. Search case insensitively if the pattern is all lowercase.
Search case sensitively otherwise. Search case sensitively otherwise.
This is overridden by either \-\-case\-sensitive or \-\-ignore\-case.
.RS .RS
.RE .RE
.TP .TP

View File

@ -49,7 +49,7 @@ the raw speed of grep.
: Show this usage message. : Show this usage message.
-i, --ignore-case -i, --ignore-case
: Case insensitive search. : Case insensitive search. Overridden by --case-sensitive.
-n, --line-number -n, --line-number
: Show line numbers (1-based). This is enabled by default at a tty. : Show line numbers (1-based). This is enabled by default at a tty.
@ -168,9 +168,13 @@ the raw speed of grep.
-p, --pretty -p, --pretty
: Alias for --color=always --heading -n. : Alias for --color=always --heading -n.
-s, --case-sensitive
: Search case sensitively. This overrides --ignore-case and --smart-case.
-S, --smart-case -S, --smart-case
: Search case insensitively if the pattern is all lowercase. : Search case insensitively if the pattern is all lowercase.
Search case sensitively otherwise. Search case sensitively otherwise. This is overridden by either
--case-sensitive or --ignore-case.
-j, --threads *ARG* -j, --threads *ARG*
: The number of threads to use. Defaults to the number of logical CPUs : The number of threads to use. Defaults to the number of logical CPUs

View File

@ -62,6 +62,7 @@ Common options:
Precede a glob with a '!' to exclude it. Precede a glob with a '!' to exclude it.
-h, --help Show this usage message. -h, --help Show this usage message.
-i, --ignore-case Case insensitive search. -i, --ignore-case Case insensitive search.
Overridden by --case-sensitive.
-n, --line-number Show line numbers (1-based). This is enabled -n, --line-number Show line numbers (1-based). This is enabled
by default at a tty. by default at a tty.
-N, --no-line-number Suppress line numbers. -N, --no-line-number Suppress line numbers.
@ -168,9 +169,13 @@ Less common options:
-p, --pretty -p, --pretty
Alias for --color=always --heading -n. Alias for --color=always --heading -n.
-s, --case-sensitive
Search case sensitively. This overrides --ignore-case and --smart-case.
-S, --smart-case -S, --smart-case
Search case insensitively if the pattern is all lowercase. Search case insensitively if the pattern is all lowercase.
Search case sensitively otherwise. Search case sensitively otherwise. This is overridden by
either --case-sensitive or --ignore-case.
-j, --threads ARG -j, --threads ARG
The number of threads to use. Defaults to the number of logical CPUs The number of threads to use. Defaults to the number of logical CPUs
@ -210,6 +215,7 @@ pub struct RawArgs {
arg_path: Vec<String>, arg_path: Vec<String>,
flag_after_context: usize, flag_after_context: usize,
flag_before_context: usize, flag_before_context: usize,
flag_case_sensitive: bool,
flag_color: String, flag_color: String,
flag_column: bool, flag_column: bool,
flag_context: usize, flag_context: usize,
@ -257,7 +263,6 @@ pub struct RawArgs {
/// Args are transformed/normalized from RawArgs. /// Args are transformed/normalized from RawArgs.
#[derive(Debug)] #[derive(Debug)]
pub struct Args { pub struct Args {
pattern: String,
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
after_context: usize, after_context: usize,
before_context: usize, before_context: usize,
@ -287,7 +292,6 @@ pub struct Args {
replace: Option<Vec<u8>>, replace: Option<Vec<u8>>,
text: bool, text: bool,
threads: usize, threads: usize,
type_defs: Vec<FileTypeDef>,
type_list: bool, type_list: bool,
types: Types, types: Types,
with_filename: bool, with_filename: bool,
@ -296,7 +300,6 @@ pub struct Args {
impl RawArgs { impl RawArgs {
/// Convert arguments parsed into a configuration used by ripgrep. /// Convert arguments parsed into a configuration used by ripgrep.
fn to_args(&self) -> Result<Args> { fn to_args(&self) -> Result<Args> {
let pattern = self.pattern();
let paths = let paths =
if self.arg_path.is_empty() { if self.arg_path.is_empty() {
if atty::on_stdin() if atty::on_stdin()
@ -362,7 +365,6 @@ impl RawArgs {
} else { } else {
self.flag_color == "always" self.flag_color == "always"
}; };
let eol = b'\n';
let mut with_filename = self.flag_with_filename; let mut with_filename = self.flag_with_filename;
if !with_filename { if !with_filename {
@ -370,22 +372,10 @@ impl RawArgs {
} }
with_filename = with_filename && !self.flag_no_filename; with_filename = with_filename && !self.flag_no_filename;
let mut btypes = TypesBuilder::new();
btypes.add_defaults();
try!(self.add_types(&mut btypes));
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()
);
let no_ignore = self.flag_no_ignore || self.flag_unrestricted >= 1; let no_ignore = self.flag_no_ignore || self.flag_unrestricted >= 1;
let hidden = self.flag_hidden || self.flag_unrestricted >= 2; let hidden = self.flag_hidden || self.flag_unrestricted >= 2;
let text = self.flag_text || self.flag_unrestricted >= 3; let text = self.flag_text || self.flag_unrestricted >= 3;
let mut args = Args { let mut args = Args {
pattern: pattern,
paths: paths, paths: paths,
after_context: after_context, after_context: after_context,
before_context: before_context, before_context: before_context,
@ -394,11 +384,11 @@ impl RawArgs {
context_separator: unescape(&self.flag_context_separator), context_separator: unescape(&self.flag_context_separator),
count: self.flag_count, count: self.flag_count,
files_with_matches: self.flag_files_with_matches, files_with_matches: self.flag_files_with_matches,
eol: eol, eol: self.eol(),
files: self.flag_files, files: self.flag_files,
follow: self.flag_follow, follow: self.flag_follow,
glob_overrides: glob_overrides, glob_overrides: glob_overrides,
grep: grep, grep: try!(self.grep()),
heading: !self.flag_no_heading && self.flag_heading, heading: !self.flag_no_heading && self.flag_heading,
hidden: hidden, hidden: hidden,
ignore_case: self.flag_ignore_case, ignore_case: self.flag_ignore_case,
@ -419,9 +409,8 @@ impl RawArgs {
replace: self.flag_replace.clone().map(|s| s.into_bytes()), replace: self.flag_replace.clone().map(|s| s.into_bytes()),
text: text, text: text,
threads: threads, threads: threads,
type_defs: btypes.definitions(),
type_list: self.flag_type_list, type_list: self.flag_type_list,
types: types, types: try!(self.types()),
with_filename: with_filename, with_filename: with_filename,
}; };
// If stdout is a tty, then apply some special default options. // If stdout is a tty, then apply some special default options.
@ -440,20 +429,22 @@ impl RawArgs {
Ok(args) Ok(args)
} }
fn add_types(&self, types: &mut TypesBuilder) -> Result<()> { fn types(&self) -> Result<Types> {
let mut btypes = TypesBuilder::new();
btypes.add_defaults();
for ty in &self.flag_type_clear { for ty in &self.flag_type_clear {
types.clear(ty); btypes.clear(ty);
} }
for def in &self.flag_type_add { for def in &self.flag_type_add {
try!(types.add_def(def)); try!(btypes.add_def(def));
} }
for ty in &self.flag_type { for ty in &self.flag_type {
types.select(ty); btypes.select(ty);
} }
for ty in &self.flag_type_not { for ty in &self.flag_type_not {
types.negate(ty); btypes.negate(ty);
} }
Ok(()) btypes.build().map_err(From::from)
} }
fn pattern(&self) -> String { fn pattern(&self) -> String {
@ -483,6 +474,27 @@ impl RawArgs {
s s
} }
} }
fn eol(&self) -> u8 {
// We might want to make this configurable.
b'\n'
}
fn grep(&self) -> Result<Grep> {
let smart =
self.flag_smart_case
&& !self.flag_ignore_case
&& !self.flag_case_sensitive;
let casei =
self.flag_ignore_case
&& !self.flag_case_sensitive;
GrepBuilder::new(&self.pattern())
.case_smart(smart)
.case_insensitive(casei)
.line_terminator(self.eol())
.build()
.map_err(From::from)
}
} }
impl Args { impl Args {
@ -677,7 +689,7 @@ impl Args {
/// Returns a list of type definitions currently loaded. /// Returns a list of type definitions currently loaded.
pub fn type_defs(&self) -> &[FileTypeDef] { pub fn type_defs(&self) -> &[FileTypeDef] {
&self.type_defs self.types.definitions()
} }
/// Returns true if ripgrep should print the type definitions currently /// Returns true if ripgrep should print the type definitions currently

View File

@ -159,6 +159,7 @@ impl FileTypeDef {
/// Types is a file type matcher. /// Types is a file type matcher.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Types { pub struct Types {
defs: Vec<FileTypeDef>,
selected: Option<glob::SetYesNo>, selected: Option<glob::SetYesNo>,
negated: Option<glob::SetYesNo>, negated: Option<glob::SetYesNo>,
has_selected: bool, has_selected: bool,
@ -176,8 +177,10 @@ impl Types {
selected: Option<glob::SetYesNo>, selected: Option<glob::SetYesNo>,
negated: Option<glob::SetYesNo>, negated: Option<glob::SetYesNo>,
has_selected: bool, has_selected: bool,
defs: Vec<FileTypeDef>,
) -> Types { ) -> Types {
Types { Types {
defs: defs,
selected: selected, selected: selected,
negated: negated, negated: negated,
has_selected: has_selected, has_selected: has_selected,
@ -193,7 +196,7 @@ impl Types {
/// Creates a new file type matcher that never matches. /// Creates a new file type matcher that never matches.
pub fn empty() -> Types { pub fn empty() -> Types {
Types::new(None, None, false) Types::new(None, None, false, vec![])
} }
/// Returns a match for the given path against this file type matcher. /// Returns a match for the given path against this file type matcher.
@ -233,6 +236,11 @@ impl Types {
Match::None Match::None
} }
} }
/// Return the set of current file type definitions.
pub fn definitions(&self) -> &[FileTypeDef] {
&self.defs
}
} }
/// TypesBuilder builds a type matcher from a set of file type definitions and /// TypesBuilder builds a type matcher from a set of file type definitions and
@ -298,7 +306,11 @@ impl TypesBuilder {
Some(try!(bset.build_yesno())) Some(try!(bset.build_yesno()))
}; };
Ok(Types::new( Ok(Types::new(
selected_globs, negated_globs, !self.selected.is_empty())) selected_globs,
negated_globs,
!self.selected.is_empty(),
self.definitions(),
))
} }
/// Return the set of current file type definitions. /// Return the set of current file type definitions.

View File

@ -754,7 +754,8 @@ clean!(regression_105_part2, "test", ".", |wd: WorkDir, mut cmd: Command| {
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/20 // See: https://github.com/BurntSushi/ripgrep/issues/20
sherlock!(feature_20, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { sherlock!(feature_20_no_filename, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--no-filename"); cmd.arg("--no-filename");
let lines: String = wd.stdout(&mut cmd); let lines: String = wd.stdout(&mut cmd);
@ -766,7 +767,7 @@ be, to a very large extent, the result of luck. Sherlock Holmes
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/68 // See: https://github.com/BurntSushi/ripgrep/issues/68
clean!(feature_68, "test", ".", |wd: WorkDir, mut cmd: Command| { clean!(feature_68_no_ignore_vcs, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create(".gitignore", "foo"); wd.create(".gitignore", "foo");
wd.create(".ignore", "bar"); wd.create(".ignore", "bar");
wd.create("foo", "test"); wd.create("foo", "test");
@ -778,7 +779,8 @@ clean!(feature_68, "test", ".", |wd: WorkDir, mut cmd: Command| {
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/70 // See: https://github.com/BurntSushi/ripgrep/issues/70
sherlock!(feature_70, "sherlock", ".", |wd: WorkDir, mut cmd: Command| { sherlock!(feature_70_smart_case, "sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--smart-case"); cmd.arg("--smart-case");
let lines: String = wd.stdout(&mut cmd); let lines: String = wd.stdout(&mut cmd);
@ -832,7 +834,7 @@ sherlock\x00can extract a clew from a wisp of straw or a flake of cigar ash;
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/109 // See: https://github.com/BurntSushi/ripgrep/issues/109
clean!(max_depth, "far", ".", |wd: WorkDir, mut cmd: Command| { clean!(feature_109_max_depth, "far", ".", |wd: WorkDir, mut cmd: Command| {
wd.create_dir("one"); wd.create_dir("one");
wd.create("one/pass", "far"); wd.create("one/pass", "far");
wd.create_dir("one/too"); wd.create_dir("one/too");
@ -842,10 +844,25 @@ clean!(max_depth, "far", ".", |wd: WorkDir, mut cmd: Command| {
let lines: String = wd.stdout(&mut cmd); let lines: String = wd.stdout(&mut cmd);
let expected = path("one/pass:far\n"); let expected = path("one/pass:far\n");
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/124
clean!(feature_109_case_sensitive_part1, "test", ".",
|wd: WorkDir, mut cmd: Command| {
wd.create("foo", "tEsT");
cmd.arg("--smart-case").arg("--case-sensitive");
wd.assert_err(&mut cmd);
});
// See: https://github.com/BurntSushi/ripgrep/issues/124
clean!(feature_109_case_sensitive_part2, "test", ".",
|wd: WorkDir, mut cmd: Command| {
wd.create("foo", "tEsT");
cmd.arg("--ignore-case").arg("--case-sensitive");
wd.assert_err(&mut cmd);
});
#[test] #[test]
fn binary_nosearch() { fn binary_nosearch() {
let wd = WorkDir::new("binary_nosearch"); let wd = WorkDir::new("binary_nosearch");