mirror of
https://github.com/sharkdp/fd
synced 2024-09-29 20:43:52 +00:00
Merge 5cdf9763e4
into 4e9672250b
This commit is contained in:
commit
1e571d0a53
37
src/cli.rs
37
src/cli.rs
|
@ -167,8 +167,8 @@ pub struct Opts {
|
|||
pub regex: bool,
|
||||
|
||||
/// Treat the pattern as a literal string instead of a regular expression. Note
|
||||
/// that this also performs substring comparison. If you want to match on an
|
||||
/// exact filename, consider using '--glob'.
|
||||
/// that the pattern would still match on a substring of the input. If you want
|
||||
/// to match on an exact filename, consider adding '--anchor=input' as well.
|
||||
#[arg(
|
||||
long,
|
||||
short = 'F',
|
||||
|
@ -245,6 +245,20 @@ pub struct Opts {
|
|||
)]
|
||||
pub full_path: bool,
|
||||
|
||||
/// By default, the search pattern for --regex and --fixed-strings can match any part of the input.
|
||||
/// (See the --full-path option for what constitutes input)
|
||||
///
|
||||
/// This flag allows anchoring the pattern.
|
||||
///
|
||||
/// Conflicts with the --glob flag: globs always match the entire input
|
||||
#[arg(
|
||||
long,
|
||||
help = "Where to anchor the pattern",
|
||||
conflicts_with("glob"),
|
||||
long_help
|
||||
)]
|
||||
pub anchor: Option<Anchor>,
|
||||
|
||||
/// Separate search results by the null character (instead of newlines).
|
||||
/// Useful for piping results to 'xargs'.
|
||||
#[arg(
|
||||
|
@ -697,6 +711,17 @@ impl Opts {
|
|||
self.rg_alias_hidden_ignore > 0
|
||||
}
|
||||
|
||||
pub fn anchor(&self) -> Option<Anchor> {
|
||||
if self.glob {
|
||||
// globset has no way to use an anchor.
|
||||
// Otherwise we'd guard like this:
|
||||
// && !self.no_anchor && self.anchor.is_none()
|
||||
Some(Anchor::Input)
|
||||
} else {
|
||||
self.anchor
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_depth(&self) -> Option<usize> {
|
||||
self.max_depth.or(self.exact_depth)
|
||||
}
|
||||
|
@ -752,6 +777,14 @@ fn default_num_threads() -> NonZeroUsize {
|
|||
.min(limit)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
|
||||
pub enum Anchor {
|
||||
InputStart,
|
||||
InputEnd,
|
||||
Input,
|
||||
Word,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
|
||||
pub enum FileType {
|
||||
#[value(alias = "f")]
|
||||
|
|
35
src/main.rs
35
src/main.rs
|
@ -163,15 +163,36 @@ fn ensure_search_pattern_is_not_a_path(opts: &Opts) -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_anchors(re: String, anchors: Option<cli::Anchor>) -> String {
|
||||
use cli::Anchor;
|
||||
match anchors {
|
||||
None => re,
|
||||
Some(Anchor::InputStart) => "^".to_owned() + &re,
|
||||
Some(Anchor::InputEnd) => re + "$",
|
||||
Some(Anchor::Input) => "^".to_owned() + &re + "$",
|
||||
// https://docs.rs/regex/latest/regex/#empty-matches
|
||||
Some(Anchor::Word) => r"\<".to_owned() + &re + r"\>",
|
||||
}
|
||||
}
|
||||
|
||||
fn build_pattern_regex(pattern: &str, opts: &Opts) -> Result<String> {
|
||||
Ok(if opts.glob && !pattern.is_empty() {
|
||||
let glob = GlobBuilder::new(pattern).literal_separator(true).build()?;
|
||||
glob.regex().to_owned()
|
||||
} else if opts.fixed_strings {
|
||||
// Treat pattern as literal string if '--fixed-strings' is used
|
||||
regex::escape(pattern)
|
||||
Ok(if opts.glob {
|
||||
if !pattern.is_empty() {
|
||||
let glob = GlobBuilder::new(pattern).literal_separator(true).build()?;
|
||||
glob.regex().to_owned()
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
} else {
|
||||
String::from(pattern)
|
||||
apply_anchors(
|
||||
if opts.fixed_strings {
|
||||
// Treat pattern as literal string if '--fixed-strings' is used
|
||||
regex::escape(pattern)
|
||||
} else {
|
||||
String::from(pattern)
|
||||
},
|
||||
opts.anchor(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -604,6 +604,33 @@ fn test_full_path() {
|
|||
);
|
||||
}
|
||||
|
||||
/// Anchoring (--anchor)
|
||||
#[test]
|
||||
fn test_anchors() {
|
||||
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);
|
||||
|
||||
te.assert_output(&["--anchor=input", "foo"], "");
|
||||
te.assert_output(&["--anchor=input", "b.foo"], "one/b.foo");
|
||||
te.assert_output(&["--anchor=input-start", "foo"], "");
|
||||
te.assert_output(&["--anchor=input-start", "b."], "one/b.foo");
|
||||
te.assert_output(
|
||||
&["--anchor=input-end", "oo"],
|
||||
"a.foo
|
||||
one/b.foo
|
||||
one/two/c.foo
|
||||
one/two/three/d.foo
|
||||
one/two/three/directory_foo/",
|
||||
);
|
||||
te.assert_output(&["--anchor=word", "oo"], "");
|
||||
te.assert_output(
|
||||
&["--anchor=word", "foo"],
|
||||
"a.foo
|
||||
one/b.foo
|
||||
one/two/c.foo
|
||||
one/two/three/d.foo",
|
||||
);
|
||||
}
|
||||
|
||||
/// Hidden files (--hidden)
|
||||
#[test]
|
||||
fn test_hidden() {
|
||||
|
|
Loading…
Reference in a new issue