summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Peter <sharkdp@users.noreply.github.com>2022-11-01 20:34:08 +0100
committerGitHub <noreply@github.com>2022-11-01 20:34:08 +0100
commit13a47c3a2cc3742f0e3ebea4ea9212a1c6b6aa90 (patch)
treef53ba300c2ff432676d67f70becb9189a5b80f6b
parentc6f9595a02750c4015df1781d313c80630608cfb (diff)
parent5771e74b95abc03f53c8df9ea94241fc8a096896 (diff)
Merge pull request #1067 from tmccombs/clap-derive
Clap derive
-rw-r--r--.github/workflows/CICD.yml19
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock65
-rw-r--r--Cargo.toml11
-rw-r--r--Makefile37
-rw-r--r--build.rs23
-rw-r--r--clippy.toml2
-rw-r--r--src/app.rs779
-rw-r--r--src/cli.rs845
-rw-r--r--src/exec/job.rs3
-rw-r--r--src/exec/mod.rs45
-rw-r--r--src/filter/size.rs10
-rw-r--r--src/main.rs360
-rw-r--r--src/walk.rs1
-rw-r--r--tests/tests.rs14
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"
diff --git a/.gitignore b/.gitignore
index 324c57f..fddac43 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
target/
+/autocomplete/
**/*.rs.bk
diff --git a/Cargo.lock b/Cargo.lock
index 0b21f8e..80c9b24 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index fad68d5..a75a005 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
diff --git a/build.rs b/build.rs
index 771f932..224f2d3 100644
--- a/build.rs
+++ b/build.rs
@@ -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