mirror of
https://github.com/sharkdp/fd.git
synced 2024-09-28 21:11:30 +02:00
Show error if pattern matches leading dot but --hidden is not given, closes #615
This commit is contained in:
parent
17bd256ae6
commit
cadaef3f07
13
src/main.rs
13
src/main.rs
@ -30,7 +30,7 @@ use crate::filetypes::FileTypes;
|
|||||||
use crate::filter::OwnerFilter;
|
use crate::filter::OwnerFilter;
|
||||||
use crate::filter::{SizeFilter, TimeFilter};
|
use crate::filter::{SizeFilter, TimeFilter};
|
||||||
use crate::options::Options;
|
use crate::options::Options;
|
||||||
use crate::regex_helper::pattern_has_uppercase_char;
|
use crate::regex_helper::{pattern_has_uppercase_char, pattern_matches_strings_with_leading_dot};
|
||||||
|
|
||||||
// We use jemalloc for performance reasons, see https://github.com/sharkdp/fd/pull/481
|
// We use jemalloc for performance reasons, see https://github.com/sharkdp/fd/pull/481
|
||||||
// FIXME: re-enable jemalloc on macOS, see comment in Cargo.toml file for more infos
|
// FIXME: re-enable jemalloc on macOS, see comment in Cargo.toml file for more infos
|
||||||
@ -431,6 +431,17 @@ fn run() -> Result<ExitCode> {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if cfg!(unix)
|
||||||
|
&& config.ignore_hidden
|
||||||
|
&& pattern_matches_strings_with_leading_dot(&pattern_regex)
|
||||||
|
{
|
||||||
|
return Err(anyhow!(
|
||||||
|
"The pattern seems to only match files with a leading dot, but hidden files are \
|
||||||
|
filtered by default. Consider adding -H/--hidden to search hidden files as well \
|
||||||
|
or adjust your search pattern."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let re = RegexBuilder::new(&pattern_regex)
|
let re = RegexBuilder::new(&pattern_regex)
|
||||||
.case_insensitive(!config.case_sensitive)
|
.case_insensitive(!config.case_sensitive)
|
||||||
.dot_matches_new_line(true)
|
.dot_matches_new_line(true)
|
||||||
|
@ -34,6 +34,45 @@ fn hir_has_uppercase_char(hir: &Hir) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine if a regex pattern only matches strings starting with a literal dot (hidden files)
|
||||||
|
pub fn pattern_matches_strings_with_leading_dot(pattern: &str) -> bool {
|
||||||
|
let mut parser = ParserBuilder::new().allow_invalid_utf8(true).build();
|
||||||
|
|
||||||
|
parser
|
||||||
|
.parse(pattern)
|
||||||
|
.map(|hir| hir_matches_strings_with_leading_dot(&hir))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See above.
|
||||||
|
fn hir_matches_strings_with_leading_dot(hir: &Hir) -> bool {
|
||||||
|
use regex_syntax::hir::*;
|
||||||
|
|
||||||
|
// Note: this only really detects the simplest case where a regex starts with
|
||||||
|
// "^\\.", i.e. a start text anchor and a literal dot character. There are a lot
|
||||||
|
// of other patterns that ONLY match hidden files, e.g. ^(\\.foo|\\.bar) which are
|
||||||
|
// not (yet) detected by this algorithm.
|
||||||
|
match *hir.kind() {
|
||||||
|
HirKind::Concat(ref hirs) => {
|
||||||
|
let mut hirs = hirs.iter();
|
||||||
|
if let Some(hir) = hirs.next() {
|
||||||
|
if *hir.kind() != HirKind::Anchor(Anchor::StartText) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(hir) = hirs.next() {
|
||||||
|
*hir.kind() == HirKind::Literal(Literal::Unicode('.'))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pattern_has_uppercase_char_simple() {
|
fn pattern_has_uppercase_char_simple() {
|
||||||
assert!(pattern_has_uppercase_char("A"));
|
assert!(pattern_has_uppercase_char("A"));
|
||||||
@ -50,3 +89,12 @@ fn pattern_has_uppercase_char_advanced() {
|
|||||||
assert!(!pattern_has_uppercase_char(r"\Acargo"));
|
assert!(!pattern_has_uppercase_char(r"\Acargo"));
|
||||||
assert!(!pattern_has_uppercase_char(r"carg\x6F"));
|
assert!(!pattern_has_uppercase_char(r"carg\x6F"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matches_strings_with_leading_dot_simple() {
|
||||||
|
assert!(pattern_matches_strings_with_leading_dot("^\\.gitignore"));
|
||||||
|
|
||||||
|
assert!(!pattern_matches_strings_with_leading_dot("^.gitignore"));
|
||||||
|
assert!(!pattern_matches_strings_with_leading_dot("\\.gitignore"));
|
||||||
|
assert!(!pattern_matches_strings_with_leading_dot("^gitignore"));
|
||||||
|
}
|
||||||
|
@ -1699,3 +1699,18 @@ fn test_number_parsing_errors() {
|
|||||||
|
|
||||||
te.assert_failure(&["--max-results=a"]);
|
te.assert_failure(&["--max-results=a"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print error if search pattern starts with a dot and --hidden is not set
|
||||||
|
/// (Unix only, hidden files on Windows work differently)
|
||||||
|
#[test]
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn test_error_if_hidden_not_set_and_pattern_starts_with_dot() {
|
||||||
|
let te = TestEnv::new(&[], &[".gitignore", ".whatever", "non-hidden"]);
|
||||||
|
|
||||||
|
te.assert_failure(&["^\\.gitignore"]);
|
||||||
|
te.assert_failure(&["--glob", ".gitignore"]);
|
||||||
|
|
||||||
|
te.assert_output(&["--hidden", "^\\.gitignore"], ".gitignore");
|
||||||
|
te.assert_output(&["--hidden", "--glob", ".gitignore"], ".gitignore");
|
||||||
|
te.assert_output(&[".gitignore"], "");
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user