diff options
author | David Peter <sharkdp@users.noreply.github.com> | 2022-11-01 20:34:08 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-01 20:34:08 +0100 |
commit | 13a47c3a2cc3742f0e3ebea4ea9212a1c6b6aa90 (patch) | |
tree | f53ba300c2ff432676d67f70becb9189a5b80f6b | |
parent | c6f9595a02750c4015df1781d313c80630608cfb (diff) | |
parent | 5771e74b95abc03f53c8df9ea94241fc8a096896 (diff) |
Merge pull request #1067 from tmccombs/clap-derive
Clap derive
-rw-r--r-- | .github/workflows/CICD.yml | 19 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Cargo.lock | 65 | ||||
-rw-r--r-- | Cargo.toml | 11 | ||||
-rw-r--r-- | Makefile | 37 | ||||
-rw-r--r-- | build.rs | 23 | ||||
-rw-r--r-- | clippy.toml | 2 | ||||
-rw-r--r-- | src/app.rs | 779 | ||||
-rw-r--r-- | src/cli.rs | 845 | ||||
-rw-r--r-- | src/exec/job.rs | 3 | ||||
-rw-r--r-- | src/exec/mod.rs | 45 | ||||
-rw-r--r-- | src/filter/size.rs | 10 | ||||
-rw-r--r-- | src/main.rs | 360 | ||||
-rw-r--r-- | src/walk.rs | 1 | ||||
-rw-r--r-- | tests/tests.rs | 14 |
15 files changed, 1081 insertions, 1134 deletions
diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 718fc19..5d5c363 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1,7 +1,7 @@ name: CICD env: - MIN_SUPPORTED_RUST_VERSION: "1.57.0" + MIN_SUPPORTED_RUST_VERSION: "1.60.0" CICD_INTERMEDIATES_DIR: "_cicd-intermediates" on: @@ -181,6 +181,11 @@ jobs: command: test args: --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} + - name: Generate completions + id: completions + shell: bash + run: make completions + - name: Create tarball id: package shell: bash @@ -193,7 +198,6 @@ jobs: PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package" ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/" mkdir -p "${ARCHIVE_DIR}" - mkdir -p "${ARCHIVE_DIR}/autocomplete" # Binary cp "${{ steps.strip.outputs.BIN_PATH }}" "$ARCHIVE_DIR" @@ -205,10 +209,7 @@ jobs: cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR" # Autocompletion files - cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.bash' "$ARCHIVE_DIR/autocomplete/" - cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.fish' "$ARCHIVE_DIR/autocomplete/" - cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'_${{ env.PROJECT_NAME }}.ps1' "$ARCHIVE_DIR/autocomplete/" - cp 'contrib/completion/_fd' "$ARCHIVE_DIR/autocomplete/" + cp -r autocomplete "${ARCHIVE_DIR}" # base compressed package pushd "${PKG_STAGING}/" >/dev/null @@ -256,9 +257,9 @@ jobs: gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1" # Autocompletion files - install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.bash' "${DPKG_DIR}/usr/share/bash-completion/completions/${{ env.PROJECT_NAME }}" - install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.fish' "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ env.PROJECT_NAME }}.fish" - install -Dm644 'contrib/completion/_fd' "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ env.PROJECT_NAME }}" + install -Dm644 'autocomplete/fd.bash' "${DPKG_DIR}/usr/share/bash-completion/completions/${{ env.PROJECT_NAME }}" + install -Dm644 'autocomplete/fd.fish' "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ env.PROJECT_NAME }}.fish" + install -Dm644 'autocomplete/_fd' "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ env.PROJECT_NAME }}" # README and LICENSE install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md" @@ -1,2 +1,3 @@ target/ +/autocomplete/ **/*.rs.bk @@ -113,35 +113,47 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.22" +version = "4.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" dependencies = [ "atty", "bitflags", + "clap_derive", "clap_lex", - "indexmap", "once_cell", "strsim", "termcolor", - "terminal_size 0.2.1", - "textwrap", + "terminal_size", ] [[package]] name = "clap_complete" -version = "3.2.5" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8" +checksum = "dfe581a2035db4174cdbdc91265e1aba50f381577f0510d0ad36c7bc59cc84a3" dependencies = [ "clap", ] [[package]] +name = "clap_derive" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] @@ -384,10 +396,10 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "heck" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" @@ -447,16 +459,6 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] name = "io-lifetimes" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -808,16 +810,6 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "terminal_size" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1" @@ -849,15 +841,6 @@ dependencies = [ ] [[package]] -name = "textwrap" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" -dependencies = [ - "terminal_size 0.1.17", -] - -[[package]] name = "thiserror" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -30,8 +30,6 @@ name = "fd" path = "src/main.rs" [build-dependencies] -clap = { version = "3.1", features = ["cargo"] } -clap_complete = "3.1" version_check = "0.9" [dependencies] @@ -52,10 +50,11 @@ normpath = "0.3.2" chrono = "0.4" once_cell = "1.15.0" crossbeam-channel = "0.5.6" +clap_complete = {version = "4.0", optional = true} [dependencies.clap] -version = "3.1" -features = ["suggestions", "color", "wrap_help", "cargo", "unstable-grouped"] +version = "4.0.12" +features = ["suggestions", "color", "wrap_help", "cargo", "unstable-grouped", "derive"] [target.'cfg(unix)'.dependencies] users = "0.11.0" @@ -85,4 +84,6 @@ codegen-units = 1 [features] use-jemalloc = ["jemallocator"] -default = ["use-jemalloc"] +completions = ["clap_complete"] +base = ["use-jemalloc"] +default = ["use-jemalloc", "completions"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6ca17ba --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +PROFILE=release +EXE=target/$(PROFILE)/fd +prefix=/usr/local +bindir=$(prefix)/bin +datadir=$(prefix)/share +exe_name=fd + +$(EXE): Cargo.toml src/**/*.rs + cargo build --profile $(PROFILE) + +.PHONY: completions +completions: autocomplete/fd.bash autocomplete/fd.fish autocomplete/fd.ps1 autocomplete/_fd + +comp_dir=@mkdir -p autocomplete + +autocomplete/fd.bash: $(EXE) + $(comp_dir) + $(EXE) --gen-completions bash > $@ + +autocomplete/fd.fish: $(EXE) + $(comp_dir) + $(EXE) --gen-completions fish > $@ + +autocomplete/fd.ps1: $(EXE) + $(comp_dir) + $(EXE) --gen-completions powershell > $@ + +autocomplete/_fd: contrib/completion/_fd + $(comp_dir) + cp $< $@ + +install: $(EXE) completions + install -Dm755 $(EXE) $(DESTDIR)$(bindir)/fd + install -Dm644 autocomplete/fd.bash $(DESTDIR)/$(datadir)/bash-completion/completions/$(exe_name) + install -Dm644 autocomplete/fd.fish $(DESTDIR)/$(datadir)/fish/vendor_completions.d/$(exe_name).fish + install -Dm644 autocomplete/_fd $(DESTDIR)/$(datadir)/zsh/site-functions/_$(exe_name) + install -Dm644 doc/fd.1 $(DESTDIR)/$(datadir)/man/man1/$(exe_name).1 @@ -1,13 +1,5 @@ -use std::fs; - -use clap_complete::{generate_to, Shell}; -use Shell::*; -//use clap_complete::shells::Shel{Bash, Fish, PowerShell, Elvish}; - -include!("src/app.rs"); - fn main() { - let min_version = "1.57"; + let min_version = "1.60"; match version_check::is_min_version(min_version) { Some(true) => {} @@ -17,17 +9,4 @@ fn main() { std::process::exit(1); } } - - let var = std::env::var_os("SHELL_COMPLETIONS_DIR").or_else(|| std::env::var_os("OUT_DIR")); - let outdir = match var { - None => return, - Some(outdir) => outdir, - }; - fs::create_dir_all(&outdir).unwrap(); - - let mut app = build_app(); - // NOTE: zsh completions are hand written in contrib/completion/_fd - for shell in [Bash, PowerShell, Fish, Elvish] { - generate_to(shell, &mut app, "fd", &outdir).unwrap(); - } } diff --git a/clippy.toml b/clippy.toml index 23b32c1..16caf02 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.57.0" +msrv = "1.60.0" diff --git a/src/app.rs b/src/app.rs deleted file mode 100644 index fa9416a..0000000 --- a/src/app.rs +++ /dev/null @@ -1,779 +0,0 @@ -use clap::{crate_version, AppSettings, Arg, ColorChoice, Command}; - -pub fn build_app() -> Command<'static> { - let clap_color_choice = if std::env::var_os("NO_COLOR").is_none() { - ColorChoice::Auto - } else { - ColorChoice::Never - }; - - let mut app = Command::new("fd") - .version(crate_version!()) - .color(clap_color_choice) - .setting(AppSettings::DeriveDisplayOrder) - .dont_collapse_args_in_usage(true) - .after_help( - "Note: `fd -h` prints a short and concise overview while `fd --help` gives all \ - details.", - ) - .after_long_help( - "Bugs can be reported on GitHub: https://github.com/sharkdp/fd/issues" - ) - .arg( - Arg::new("hidden") - .long("hidden") - .short('H') - .overrides_with("hidden") - .help("Search hidden files and directories") - .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). \ - The flag can be overridden with --no-hidden.", - ), - ) - .arg( - Arg::new("no-hidden") - .long("no-hidden") - .overrides_with("hidden") - .hide(true) - .long_help( - "Overrides --hidden.", - ), - ) - .arg( - Arg::new("no-ignore") - .long("no-ignore") - .short('I') - .overrides_with("no-ignore") - .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. \ - The flag can be overridden with --ignore.", - ), - ) - .arg( - Arg::new("ignore") - .long("ignore") - .overrides_with("no-ignore") - .hide(true) - .long_help( - "Overrides --no-ignore.", - ), - ) - .arg( - Arg::new("no-ignore-vcs") - .long("no-ignore-vcs") - .overrides_with("no-ignore-vcs") - .hide_short_help(true) - .help("Do not respect .gitignore files") - .long_help( - "Show search results from files and directories that would otherwise be \ - ignored by '.gitignore' files. The flag can be overridden with --ignore-vcs.", - ), - ) - .arg( - Arg::new("ignore-vcs") - .long("ignore-vcs") - .overrides_with("no-ignore-vcs") - .hide(true) - .long_help( - "Overrides --no-ignore-vcs.", - ), - ) - .arg( - Arg::new("no-ignore-parent") - .long("no-ignore-parent") - .overrides_with("no-ignore-parent") - .hide_short_help(true) - .help("Do not respect .(git|fd)ignore files in parent directories") - .long_help( - "Show search results from files and directories that would otherwise be \ - ignored by '.gitignore', '.ignore', or '.fdignore' files in parent directories.", - ), - ) - .arg( - Arg::new("no-global-ignore-file") - .long("no-global-ignore-file") - .hide(true) - .help("Do not respect the global ignore file") - .long_help("Do not respect the global ignore file."), - ) - .arg( - Arg::new("rg-alias-hidden-ignore") - .short('u') - .long("unrestricted") - .overrides_with_all(&["ignore", "no-hidden"]) - .multiple_occurrences(true) // Allowed for historical reasons - .hide_short_help(true) - .help("Unrestricted search, alias for '--no-ignore --hidden'") - .long_help( - "Perform an unrestricted search, including ignored and hidden files. This is \ - an alias for '--no-ignore --hidden'." - ), - ) - .arg( - Arg::new("case-sensitive") - .long("case-sensitive") - .short('s') - .overrides_with_all(&["ignore-case", "case-sensitive"]) - .help("Case-sensitive search (default: smart case)") - .long_help( - "Perform a case-sensitive search. By default, fd uses case-insensitive \ - searches, unless the pattern contains an uppercase character (smart \ - case).", - ), - ) - .arg( - Arg::new("ignore-case") - .long("ignore-case") - .short('i') - .overrides_with_all(&["case-sensitive", "ignore-case"]) - .help("Case-insensitive search (default: smart case)") - .long_help( - "Perform a case-insensitive search. By default, fd uses case-insensitive \ - searches, unless the pattern contains an uppercase character (smart \ - case).", - ), - ) - .arg( - Arg::new("glob") - .long("glob") - .short('g') - .conflicts_with("fixed-strings") - .overrides_with("glob") - .help("Glob-based search (default: regular expression)") - .long_help("Perform a glob-based search instead of a regular expression search."), - ) - .arg( - Arg::new("regex") - .long("regex") - .overrides_with_all(&["glob", "regex"]) - .hide_short_help(true) - .help("Regular-expression based search (default)") - .long_help( - "Perform a regular-expression based search (default). This can be used to \ - override --glob.", - ), - ) - .arg( - Arg::new("fixed-strings") - .long("fixed-strings") - .short('F') - .alias("literal") - .overrides_with("fixed-strings") - .hide_short_help(true) - .help("Treat pattern as literal string instead of regex") - .long_help( - "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'.", - ), - ) - .arg( - Arg::new("absolute-path") - .long("absolute-path") - .short('a') - .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. \ - The flag can be overridden with --relative-path.", - ), - ) - .arg( - Arg::new("relative-path") - .long("relative-path") - .overrides_with("absolute-path") - .hide(true) - .long_help( - "Overrides --absolute-path.", - ), - ) - .arg( - Arg::new("list-details") - .long("list-details") - .short('l') - .conflicts_with("absolute-path") - .help("Use a long listing format with file metadata") - .long_help( - "Use a detailed listing format like 'ls -l'. This is basically an alias \ - for '--exec-batch ls -l' with some additional 'ls' options. This can be \ - used to see more metadata, to show symlink targets and to achieve a \ - deterministic sort order.", - ), - ) - .arg( - Arg::new("follow") - .long("follow") - .short('L') - .alias("dereference") - .overrides_with("follow") - .help("Follow symbolic links") - .long_help( - "By default, fd does not descend into symlinked directories. Using this \ - flag, symbolic links are also traversed. \ - Flag can be overriden with --no-follow.", - ), - ) - .arg( - Arg::new("no-follow") - .long("no-follow") - .overrides_with("follow") - .hide(true) - .long_help( - "Overrides --follow.", - ), - ) - .arg( - Arg::new("full-path") - .long("full-path") - .short('p') - .overrides_with("full-path") - .help("Search full abs. path (default: filename only)") - .long_help( - "By default, the search pattern is only matched against the filename (or \ - directory name). Using this flag, the pattern is matched against the full \ - (absolute) path. Example:\n \ - fd --glob -p '**/.git/config'", - ), - ) - .arg( - Arg::new("null_separator") - .long("print0") - .short('0') - .overrides_with("print0") - .conflicts_with("list-details") - .hide_short_help(true) - .help("Separate results by the null character") - .long_help( - "Separate search results by the null character (instead of newlines). \ - Useful for piping results to 'xargs'.", - ), - ) - .arg( - Arg::new("max-depth") - .long("max-depth") - .short('d') - .takes_value(true) - .value_name("depth") - .help("Set maximum search depth (default: none)") - .long_help( - "Limit the directory traversal to a given depth. By default, there is no \ - limit on the search depth.", - ), - ) - // support --maxdepth as well, for compatibility with rg - .arg( - Arg::new("rg-depth") - .long("maxdepth") - .hide(true) - .takes_value(true) - .help("Set maximum search depth (default: none)") - ) - .arg( - Arg::new("min-depth") - .long("min-depth") - .takes_value(true) - .value_name("depth") - .hide_short_help(true) - .help("Only show results starting at given depth") - .long_help( - "Only show search results starting at the given depth. \ - See also: '--max-depth' and '--exact-depth'", - ), - ) - .arg( - Arg::new("exact-depth") - .long("exact-depth") - .takes_value(true) - .value_name("depth") - .hide_short_help(true) - .conflicts_with_all(&["max-depth", "min-depth"]) - .help("Only show results at exact given depth") - .long_help( - "Only show search results at the exact given depth. This is an alias for \ - '--min-depth <depth> --max-depth <depth>'.", - ), - ) - .arg( - Arg::new("prune") - .long("prune") - .conflicts_with_all(&["size", "exact-depth"]) - .hide_short_help(true) - .help("Do not traverse into matching directories") - .long_help("Do not traverse into directories that match the search criteria. If \ - you want to exclude specific directories, use the '--exclude=…' option.") - ) - .arg( - Arg::new("file-type") - .long("type") - .short('t') - .multiple_occurrences(true) - .number_of_values(1) - .takes_value(true) - .value_name("filetype") - .possible_values(&[ - "f", - "file", - "d", - "directory", - "l", - "symlink", - "x", - "executable", - "e", - "empty", - "s", - "socket", - "p", - "pipe", - ]) - .hide_possible_values(true) - .help( - "Filter by type: file (f), directory (d), symlink (l),\nexecutable (x), \ - empty (e), socket (s), pipe (p)", - ) - .long_help( - "Filter the search by type:\n \ - 'f' or 'file': regular files\n \ - 'd' or 'directory': directories\n \ - 'l' or 'symlink': symbolic links\n \ - 's' or 'socket': socket\n \ - 'p' or 'pipe': named pipe (FIFO)\n\n \ - 'x' or 'executable': executables\n \ - 'e' or 'empty': empty files or directories\n\n\ - This option can be specified more than once to include multiple file types. \ - Searching for '--type file --type symlink' will show both regular files as \ - well as symlinks. Note that the 'executable' and 'empty' filters work differently: \ - '--type executable' implies '--type file' by default. And '--type empty' searches \ - for empty files and directories, unless either '--type file' or '--type directory' \ - is specified in addition.\n\n\ - Examples:\n \ - - Only search for files:\n \ - fd --type file …\n \ - fd -tf …\n \ - - Find both files and symlinks\n \ - fd --type file --type symlink …\n \ - fd -tf -tl …\n \ - - Find executable files:\n \ - fd --type executable\n \ - fd -tx\n \ - - Find empty files:\n \ - fd --type empty --type file\n \ - fd -te -tf\n \ - - Find empty directories:\n \ - fd --type empty --type directory\n \ - fd -te -td" - ), - ) - .arg( - Arg::new("extension") - .long("extension") - .short('e') - .multiple_occurrences(true) - .number_of_values(1) - .takes_value(true) - .value_name("ext") - .help("Filter by file extension") - .long_help( - "(Additionally) filter search results by their file extension. Multiple \ - allowable file extensions can be specified.\n\ - If you want to search for files without extension, \ - you can use the regex '^[^.]+$' as a normal search pattern.", - ), - ) - .arg( - Arg::new("exec") - .long("exec") - .short('x') - .min_values(1) - .multiple_occurrences(true) - .allow_hyphen_values(true) - .value_terminator(";") - .value_name("cmd") - .conflicts_with("list-details") - .help("Execute a command for each search result") - .long_help( - "Execute a command for each search result in parallel (use --threads=1 for sequential command execution). \ - All positional arguments following --exec are considered to be arguments to the command - not to fd. \ - It is therefore recommended to place the '-x'/'--exec' option last.\n\ - The following placeholders are substituted before the command is executed:\n \ - '{}': path (of the current search result)\n \ - '{/}': basename\n \ - '{//}': parent directory\n \ - '{.}': path without file extension\n \ - '{/.}': basename without file extension\n\n\ - If no placeholder is present, an implicit \"{}\" at the end is assumed.\n\n\ - Examples:\n\n \ - - find all *.zip files and unzip them:\n\n \ - fd -e zip -x unzip\n\n \ - - find *.h and *.cpp files and run \"clang-format -i ..\" for each of them:\n\n \ - fd -e h -e cpp -x clang-format -i\n\n \ - - Convert all *.jpg files to *.png files:\n\n \ - fd -e jpg -x convert {} {.}.png\ - ", - ), - ) - .arg( - Arg::new("exec-batch") - .long("exec-batch") - .short('X') - .min_values(1) - .multiple_occurrences(true) - .allow_hyphen_values(true) - .value_terminator(";") - .value_name("cmd") - .conflicts_with_all(&["exec", "list-details"]) - .help("Execute a command with all search results at once") - .long_help( - "Execute the given command once, with all search results as arguments.\n\ - One of the following placeholders is substituted before the command is executed:\n \ - '{}': path (of all search results)\n \ - '{/}': basename\n \ - '{//}': parent directory\n \ - '{.}': path without file extension\n \ - '{/.}': basename without file extension\n\n\ - If no placeholder is present, an implicit \"{}\" at the end is assumed.\n\n\ - Examples:\n\n \ - - Find all test_*.py files and open them in your favorite editor:\n\n \ - fd -g 'test_*.py' -X vim\n\n \ - - Find all *.rs files and count the lines with \"wc -l ...\":\n\n \ - fd -e rs -X wc -l\ - " - ), - ) - .arg( - Arg::new("batch-size") - .long("batch-size") - .takes_value(true) - .value_name("size") - .hide_short_help(true) - .requires("exec-batch") - .help("Max number of arguments to run as a batch with -X") - .long_help( - "Maximum number of arguments to pass to the command given with -X. \ - If the number of results is greater than the given size, \ - the command given with -X is run again with remaining arguments. \ - A batch size of zero means there is no limit (default), but note \ - that batching might still happen due to OS restrictions on the \ - maximum length of command lines.", - ), - ) - .arg( - Arg::new("exclude") - .long("exclude") - .short('E') - .takes_value(true) - .value_name("pattern") - .number_of_values(1) - .multiple_occurrences(true) - .help("Exclude entries that match the given glob pattern") - .long_help( - "Exclude files/directories that match the given glob pattern. This \ - overrides any other ignore logic. Multiple exclude patterns can be \ - specified.\n\n\ - Examples:\n \ - --exclude '*.pyc'\n \ - --exclude node_modules", - ), - ) - .arg( - Arg::new("ignore-file") - .long("ignore-file") - .takes_value(true) - .value_name("path") - .number_of_values(1) - .multiple_occurrences(true) - .hide_short_help(true) - .help("Add custom ignore-file in '.gitignore' format") - .long_help( - "Add a custom ignore-file in '.gitignore' format. These files have a low \ - precedence.", - ), - ) - .arg( - Arg::new("color") - .long("color") - .short('c') - .takes_value(true) - .value_name("when") - .possible_values(&["never", "auto", "always"]) - .hide_possible_values(true) - .help("When to use colors: never, *auto*, always") - .long_help( - "Declare when to use color for the pattern match output:\n \ - 'auto': show colors if the output goes to an interactive console (default)\n \ - 'never': do not use colorized output\n \ - 'always': always use colorized output", - ), - ) - .arg( - Arg::new("threads") - .long("threads") - .short('j') - .takes_value(true) - .value_name("num") - .hide_short_help(true) - .help("Set number of threads") - .long_help( - "Set number of threads to use for searching & executing (default: number \ - of available CPU cores)", - ), - ) - .arg( - Arg::new("size") - .long("size") - .short('S') - .takes_value(true) - .number_of_values(1) - .allow_hyphen_values(true) - .multiple_occurrences(true) - .help("Limit results based on the size of files") - .long_help( - "Limit results based on the size of files using the format <+-><NUM><UNIT>.\n \ - '+': file size must be greater than or equal to this\n \ - '-': file size must be less than or equal to this\n\ - If neither '+' nor '-' is specified, file size must be exactly equal |