diff --git a/CHANGELOG.md b/CHANGELOG.md index 08e4e60..55889ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Features +- Add opposing command-line options, see #595 (@Asha20) + ## Bugfixes - Set default path separator to `/` in MSYS, see #537 and #730 (@aswild) diff --git a/Cargo.lock b/Cargo.lock index dede4b7..5b5359c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,7 @@ dependencies = [ "regex", "regex-syntax", "tempdir", + "test-case", "users", "version_check", ] @@ -352,6 +353,24 @@ dependencies = [ "libc", ] +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.4.6" @@ -455,6 +474,17 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "tempdir" version = "0.3.7" @@ -475,6 +505,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "test-case" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b114ece25254e97bf48dd4bfc2a12bad0647adacfe4cae1247a9ca6ad302cec" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro2", + "quote", + "syn", + "version_check", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -500,6 +543,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + [[package]] name = "users" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index a4d479d..4349488 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ jemallocator = "0.3.0" diff = "0.1" tempdir = "0.3" filetime = "0.2.14" +test-case = "1.2.0" [profile.release] lto = true diff --git a/src/app.rs b/src/app.rs index 2d6ec94..7ca6a51 100644 --- a/src/app.rs +++ b/src/app.rs @@ -25,7 +25,17 @@ pub fn build_app() -> App<'static, 'static> { .long_help( "Include hidden directories and files in the search results (default: \ hidden files and directories are skipped). Files and directories are \ - considered to be hidden if their name starts with a `.` sign (dot).", + considered to be hidden if their name starts with a `.` sign (dot). \ + Flag can be overridden with --no-hidden.", + ), + ) + .arg( + Arg::with_name("no-hidden") + .long("no-hidden") + .overrides_with("no-hidden") + .hidden(true) + .long_help( + "Overrides --hidden.", ), ) .arg( @@ -36,7 +46,17 @@ pub fn build_app() -> App<'static, 'static> { .help("Do not respect .(git|fd)ignore files") .long_help( "Show search results from files and directories that would otherwise be \ - ignored by '.gitignore', '.ignore', '.fdignore', or the global ignore file.", + ignored by '.gitignore', '.ignore', '.fdignore', or the global ignore file. \ + Flag can be overridden with --ignore.", + ), + ) + .arg( + Arg::with_name("ignore") + .long("ignore") + .overrides_with("ignore") + .hidden(true) + .long_help( + "Overrides --no-ignore.", ), ) .arg( @@ -46,7 +66,16 @@ pub fn build_app() -> App<'static, 'static> { .hidden_short_help(true) .long_help( "Show search results from files and directories that would otherwise be \ - ignored by '.gitignore' files.", + ignored by '.gitignore' files. Flag can be overridden with --ignore-vcs.", + ), + ) + .arg( + Arg::with_name("ignore-vcs") + .long("ignore-vcs") + .overrides_with("ignore-vcs") + .hidden(true) + .long_help( + "Overrides --no-ignore-vcs.", ), ) .arg( @@ -129,7 +158,17 @@ pub fn build_app() -> App<'static, 'static> { .overrides_with("absolute-path") .help("Show absolute instead of relative paths") .long_help( - "Shows the full path starting from the root as opposed to relative paths.", + "Shows the full path starting from the root as opposed to relative paths. \ + Flag can be overridden with --relative-path.", + ), + ) + .arg( + Arg::with_name("relative-path") + .long("relative-path") + .overrides_with("relative-path") + .hidden(true) + .long_help( + "Overrides --absolute-path.", ), ) .arg( @@ -154,7 +193,17 @@ pub fn build_app() -> App<'static, 'static> { .help("Follow symbolic links") .long_help( "By default, fd does not descend into symlinked directories. Using this \ - flag, symbolic links are also traversed.", + flag, symbolic links are also traversed. \ + Flag can be overriden with --no-follow.", + ), + ) + .arg( + Arg::with_name("no-follow") + .long("no-follow") + .overrides_with("no-follow") + .hidden(true) + .long_help( + "Overrides --follow.", ), ) .arg( diff --git a/src/main.rs b/src/main.rs index 8654688..9572a25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,8 +20,8 @@ use anyhow::{anyhow, Context, Result}; use atty::Stream; use globset::GlobBuilder; use lscolors::LsColors; -use regex::bytes::{RegexBuilder, RegexSetBuilder}; use normpath::PathExt; +use regex::bytes::{RegexBuilder, RegexSetBuilder}; use crate::error::print_error; use crate::exec::CommandTemplate; @@ -116,7 +116,7 @@ fn run() -> Result { return Err(anyhow!("No valid search paths given.")); } - if matches.is_present("absolute-path") { + if matches.is_present("absolute-path") && !matches.is_present("relative-path") { search_paths = search_paths .iter() .map(|path_buffer| { @@ -331,17 +331,21 @@ fn run() -> Result { let config = Options { case_sensitive, search_full_path: matches.is_present("full-path"), - ignore_hidden: !(matches.is_present("hidden") - || matches.occurrences_of("rg-alias-hidden-ignore") >= 2), - read_fdignore: !(matches.is_present("no-ignore") - || matches.is_present("rg-alias-hidden-ignore")), - read_vcsignore: !(matches.is_present("no-ignore") - || matches.is_present("rg-alias-hidden-ignore") - || matches.is_present("no-ignore-vcs")), - read_global_ignore: !(matches.is_present("no-ignore") - || matches.is_present("rg-alias-hidden-ignore") - || matches.is_present("no-global-ignore-file")), - follow_links: matches.is_present("follow"), + ignore_hidden: matches.is_present("no-hidden") + || !(matches.is_present("hidden") + || matches.occurrences_of("rg-alias-hidden-ignore") >= 2), + read_fdignore: matches.is_present("ignore") + || !(matches.is_present("no-ignore") || matches.is_present("rg-alias-hidden-ignore")), + read_vcsignore: matches.is_present("ignore") || matches.is_present("ignore-vcs") || { + !(matches.is_present("no-ignore") + || matches.is_present("rg-alias-hidden-ignore") + || matches.is_present("no-ignore-vcs")) + }, + read_global_ignore: matches.is_present("ignore") + || !(matches.is_present("no-ignore") + || matches.is_present("rg-alias-hidden-ignore") + || matches.is_present("no-global-ignore-file")), + follow_links: matches.is_present("follow") && !matches.is_present("no-follow"), one_file_system: matches.is_present("one-file-system"), null_separator: matches.is_present("null_separator"), max_depth: matches diff --git a/tests/tests.rs b/tests/tests.rs index 0b6e6b5..03bbea5 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -4,6 +4,7 @@ use std::fs; use std::io::Write; use std::path::Path; use std::time::{Duration, SystemTime}; +use test_case::test_case; use normpath::PathExt; use regex::escape; @@ -1788,6 +1789,30 @@ fn test_number_parsing_errors() { te.assert_failure(&["--max-results=a"]); } +#[test_case("--hidden", &["--no-hidden"] ; "hidden")] +#[test_case("--no-ignore", &["--ignore"] ; "no-ignore")] +#[test_case("--no-ignore-vcs", &["--ignore-vcs"] ; "no-ignore-vcs")] +#[test_case("--follow", &["--no-follow"] ; "follow")] +#[test_case("--absolute-path", &["--relative-path"] ; "absolute-path")] +#[test_case("-u", &["--ignore"] ; "u")] +#[test_case("-uu", &["--ignore", "--no-hidden"] ; "uu")] +fn test_opposing(flag: &str, opposing_flags: &[&str]) { + let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES); + + let mut flags = vec![flag]; + flags.extend_from_slice(opposing_flags); + let out_no_flags = te.assert_success_and_get_output(".", &[]); + let out_opposing_flags = te.assert_success_and_get_output(".", &flags); + + assert_eq!( + out_no_flags, + out_opposing_flags, + "{} should override {}", + opposing_flags.join(" "), + flag + ); +} + /// Print error if search pattern starts with a dot and --hidden is not set /// (Unix only, hidden files on Windows work differently) #[test]