Compare commits

..

40 Commits

Author SHA1 Message Date
David Peter 9f5ed8534e Add temporary version of logo 2022-01-29 16:53:13 +01:00
David Peter c48d02e0a9 Bump version to v8.3.2 2022-01-29 16:49:09 +01:00
David Peter b3399c239b Add clippy run in CI/CD 2022-01-29 13:01:46 +01:00
David Peter 4377954cf9 Add tavianator sponsoring link 2022-01-29 12:51:16 +01:00
Thayne McCombs 125cb81a5b Merge pull request #929 from tmccombs/clap-upgrade
Upgrade to clap 3.0
2022-01-08 00:24:43 -07:00
Thayne McCombs 2e9be3e3f5 Bump MSRV to 1.54
Because that is needed by clap 3.0
2022-01-07 23:56:03 -07:00
Thayne McCombs 43f276e073 Upgrade to clap 3.0
This wasn't backwards compatible so required some more substantial
changes.
2022-01-07 23:56:03 -07:00
David Peter ec38e23d58 Add CHANGELOG entry 2022-01-07 17:39:24 +01:00
Tavian Barnes 56060a5b6b Merge pull request #936 from gbarta/fix_fd_issue_931
Update normpath to 0.3.2 to fix issue #931
2022-01-07 10:14:08 -05:00
Tavian Barnes 03548a847f Merge pull request #937 from tmccombs/less-flaky-tests
Less flaky tests
2022-01-07 10:11:54 -05:00
Thayne McCombs 06cd391845 Change test_opposing to be less flaky.
Make it resilient to the different invocations returning results in
different orders.
2022-01-07 03:10:09 -07:00
Gabriel Barta ef4abfc1d2 Update normpath to 0.3.2 to fix issue #931 2022-01-07 10:53:03 +11:00
David Peter 72895675ef Bump version to v8.3.1 2022-01-06 14:20:02 +01:00
David Peter ccc6cc54b2 Merge pull request #916 from tavianator/revert-crossbeam
Revert "Switch from std::sync::mpsc to crossbeam-channel"
2022-01-06 13:59:16 +01:00
David Peter aa1c0250b7 Update CHANGELOG 2022-01-06 13:59:04 +01:00
Thayne McCombs d5f740c9d3 Add a couple of options to zsh completion
--no-ignore-parent and --strip-cwd-prefix
2022-01-06 13:52:49 +01:00
David Peter aa6933d9e3 Merge branch 'master' into revert-crossbeam 2022-01-06 13:49:58 +01:00
dependabot[bot] c800c79976 Bump anyhow from 1.0.51 to 1.0.52
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.51 to 1.0.52.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.51...1.0.52)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 21:45:55 +01:00
dependabot[bot] 075c1af7d8 Bump num_cpus from 1.13.0 to 1.13.1 (#927)
Bumps [num_cpus](https://github.com/seanmonstar/num_cpus) from 1.13.0 to 1.13.1.
- [Release notes](https://github.com/seanmonstar/num_cpus/releases)
- [Changelog](https://github.com/seanmonstar/num_cpus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/num_cpus/compare/v1.13.0...v1.13.1)

---
updated-dependencies:
- dependency-name: num_cpus
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-03 09:47:20 -07:00
dependabot[bot] fbebaa299d Bump version_check from 0.9.3 to 0.9.4 (#921)
Bumps [version_check](https://github.com/SergioBenitez/version_check) from 0.9.3 to 0.9.4.
- [Release notes](https://github.com/SergioBenitez/version_check/releases)
- [Commits](https://github.com/SergioBenitez/version_check/compare/v0.9.3...v0.9.4)

---
updated-dependencies:
- dependency-name: version_check
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-03 09:26:25 -07:00
dependabot[bot] f6bbeb35f5 Bump nix from 0.23.0 to 0.23.1 (#922)
Bumps [nix](https://github.com/nix-rust/nix) from 0.23.0 to 0.23.1.
- [Release notes](https://github.com/nix-rust/nix/releases)
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.23.0...v0.23.1)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-03 09:26:00 -07:00
dependabot[bot] 0dc49b07a0 Bump libc from 0.2.108 to 0.2.112 (#924)
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.108 to 0.2.112.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.108...0.2.112)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-03 09:25:33 -07:00
dependabot[bot] c2e5075316 Bump once_cell from 1.8.0 to 1.9.0 (#925)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.8.0...v1.9.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-03 09:25:06 -07:00
Tavian Barnes e7f192f1c6 Revert "Switch from std::sync::mpsc to crossbeam-channel"
@sharkdp [noticed][1] a quite severe performance degredation due to this
change.  Switch back to std::sync::mpsc until we can fix the performance
regression.

This reverts commit a4bb734482.

[1]: https://github.com/sharkdp/fd/pull/895#issuecomment-1002099369
2021-12-28 12:11:32 -05:00
Yujia Qiao e5145ffb98 fix: disable jemalloc on risc-v target
`jemallocator` does not support risc-v and fails the compilation.
This fixes build for risc-v.
2021-12-28 17:38:28 +01:00
Ryan Lue 30add71233 Update manpage to match --help output
Three command line options were added in late 2021
that never made their way into the manpage:

* f8ae334 introduced --no-ignore-parent;
* bf9e6fd introduced --strip-prefix
  (later renamed strip-cwd-prefix); and
* 17dd2a6 introduced --batch-size.

This commit updates the manpage content to reflect
the auto-generated output of `fd --help`.
2021-12-28 14:21:10 +01:00
Thayne McCombs dade9736ac Add entry to changelog for no-ignore-vcs fix
I forgot to add it when I made the PR.

See commit 8bdbbd8e0cbe8ba010ce5c7f863e23af44386741
2021-12-28 14:20:25 +01:00
eatradish 639ed34791 Allow use to disable jemalloc
jemalloc will require special flags on devices where page size is not 4K. With default options fd will not work on a Mac Mini M1 where hardware only allow 16K paging size. This commit will allow the user to turn off jemalloc (using --no-default-features) under special circumstances where jemalloc is not usable.
2021-12-28 14:15:45 +01:00
David Peter da7ea79034 Use correct description for Debian package, closes #915 2021-12-28 14:00:36 +01:00
Thayne McCombs b7e077320d Fix logic for --no-ignore-parent (#908)
Make sure that using `--no-ignore-vcs` or `--no-ignore` don't also
enable `--no-ignore-parent`. So that if `--no-ignore-vcs` is enabled, it
continues to respect .fdignore and .ignore in the parent directories.

Fixes: #907
Fixes: #901
2021-12-22 23:38:00 -07:00
Tavian Barnes 81669f4c10 Merge pull request #902 from tavianator/quit-senders
Quit senders more aggressively
2021-12-10 13:56:28 -05:00
Thayne McCombs 5ea7cb7a05 A couple of minor recommendations from the Code Quality action (#903)
use derive for a Default impl, and use a function directly instead of
needlessly wrapping it in a closure
2021-12-07 10:04:17 -07:00
Tavian Barnes 4baefb2f5a walk: Split wants_to_quit into two separate flags
quit_flag is now used to quit the sender threads for any reason, either
due to an interrupt or because the receiver is done.

interrupt_flag is used specifically for ^C interrupts, and causes the
receiver to stop between printing paths, to avoid unfinished escape
sequences when colors are being used.

Fixes #867.
2021-12-05 12:06:49 -05:00
Tavian Barnes 97a8825b00 output: Remove wants_to_quit handling from print_entry()
Since we only check it once per path now, we might as well just check it
before printing anything.
2021-12-05 12:06:49 -05:00
Tavian Barnes 7fe4bfaacb Merge pull request #895 from tavianator/receiver-buffer
walk: Encapsulate the buffering behavior in a struct
2021-12-05 11:56:22 -05:00
dependabot[bot] 1a6638ba23 Bump anyhow from 1.0.48 to 1.0.51 (#899)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.48 to 1.0.51.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.48...1.0.51)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-01 13:58:36 -07:00
dependabot[bot] 8772708aa7 Bump clap from 2.33.3 to 2.34.0 (#900)
Bumps [clap](https://github.com/clap-rs/clap) from 2.33.3 to 2.34.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/commits)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-01 13:58:05 -07:00
Tavian Barnes a4bb734482 Switch from std::sync::mpsc to crossbeam-channel
This lets us avoid https://github.com/rust-lang/rust/issues/39364, which
could potentially be seen now that we're using recv_timeout().
2021-12-01 10:24:58 -05:00
Alexander Sieg 5fbfdfadb8 fix: disable jemalloc on FreeBSD
As jemalloc is the default system allocator on FreeBSD and the
jemalloc-sys crate failes to compile on FreeBSD, this fixes the build on
FreeBSD.
2021-11-28 17:42:15 +01:00
Tavian Barnes 16ae03c3b4 walk: Encapsulate the buffering behavior in a struct
The new ReceiverBuffer struct allows us to factor out the receiver
implementation into a number of helper methods.  The new implementation
uses rx.{recv,recv_timeout} instead of a for loop, which enables us to
switch to streaming mode at the right time without waiting for more
results.

Fixes #868.
2021-11-27 12:56:22 -05:00
19 changed files with 712 additions and 351 deletions
+1
View File
@@ -0,0 +1 @@
github: [sharkdp, tavianator]
+13 -3
View File
@@ -1,7 +1,7 @@
name: CICD
env:
MIN_SUPPORTED_RUST_VERSION: "1.53.0"
MIN_SUPPORTED_RUST_VERSION: "1.54.0"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on:
@@ -46,11 +46,18 @@ jobs:
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Install rust toolchain (v${{ env.MIN_SUPPORTED_RUST_VERSION }})
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
default: true
components: clippy
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --locked --all-targets --all-features
profile: minimal # minimal component installation (ie, no documentation)
- name: Run tests
uses: actions-rs/cargo@v1
@@ -313,8 +320,11 @@ jobs:
Architecture: ${DPKG_ARCH}
Provides: ${{ env.PROJECT_NAME }}
Conflicts: ${DPKG_CONFLICTS}
Description: cat(1) clone with wings.
A cat(1) clone with syntax highlighting and Git integration.
Description: simple, fast and user-friendly alternative to find
fd is a program to find entries in your filesystem.
It is a simple, fast and user-friendly alternative to find.
While it does not aim to support all of finds powerful functionality, it provides
sensible (opinionated) defaults for a majority of use cases.
EOF
DPKG_PATH="${DPKG_STAGING}/${DPKG_NAME}"
+26
View File
@@ -2,15 +2,41 @@
## Performance improvements
## Features
## Bugfixes
## Changes
## Other
# v8.3.2
## Bugfixes
- Invalid absolute path on windows when searching from the drive root, see #931 and #936 (@gbarta)
# v8.3.1
## Bugfixes
- Stop implying `--no-ignore-parent` when `--no-vcs-ignore` is supplied, see #907, #901, #908 (@tmccombs)
- fd no longer waits for the whole traversal if the only matches arrive within max_buffer_time, see #868 and #895 (@tavianator)
- `--max-results=1` now immediately quits after the first result, see #867
- `fd -h` does not panic anymore when stdout is closed, see #897
## Changes
- Disable jemalloc on FreeBSD, see #896 (@xanderio)
- Updated man page, see #912 (@rlue)
- Updated zsh completions, see #932 (@tmccombs)
# v8.3.0
Generated
+84 -61
View File
@@ -11,15 +11,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@@ -31,9 +22,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.48"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
name = "atty"
@@ -94,18 +85,28 @@ dependencies = [
[[package]]
name = "clap"
version = "2.33.3"
version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "f6f34b09b9ee8c7c7b400fe2f8df39cafc9538b03d6ba7f4ae13e4cb90bfbb7d"
dependencies = [
"ansi_term 0.11.0",
"atty",
"bitflags",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"term_size",
"termcolor",
"terminal_size",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap_complete"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a394f7ec0715b42a4e52b294984c27c9a61f77c8d82f7774c5198350be143f19"
dependencies = [
"clap",
]
[[package]]
@@ -157,13 +158,14 @@ dependencies = [
[[package]]
name = "fd-find"
version = "8.3.0"
version = "8.3.2"
dependencies = [
"ansi_term 0.12.1",
"ansi_term",
"anyhow",
"atty",
"chrono",
"clap",
"clap_complete",
"ctrlc",
"diff",
"dirs-next",
@@ -240,6 +242,12 @@ dependencies = [
"regex",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@@ -273,6 +281,16 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "indexmap"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "jemalloc-sys"
version = "0.3.2"
@@ -302,9 +320,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.108"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "log"
@@ -321,7 +339,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd58d8727f3035fa6d5272f16b519741fd4875936b99d8a7cde21291b7d9174"
dependencies = [
"ansi_term 0.12.1",
"ansi_term",
]
[[package]]
@@ -332,18 +350,18 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
dependencies = [
"bitflags",
"cc",
@@ -354,9 +372,9 @@ dependencies = [
[[package]]
name = "normpath"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "640c20e9df4a2d4a5adad5b47e17d76dac3e824346b181931c3ec9f7a85687b1"
checksum = "04aaf5e9cb0fbf883cc0423159eacdf96a9878022084b35c462c428cab73bcaf"
dependencies = [
"winapi",
]
@@ -382,9 +400,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
@@ -392,24 +410,33 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
"proc-macro2",
]
@@ -507,15 +534,15 @@ dependencies = [
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.82"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
dependencies = [
"proc-macro2",
"quote",
@@ -533,10 +560,19 @@ dependencies = [
]
[[package]]
name = "term_size"
version = "0.3.2"
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
@@ -557,12 +593,11 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [
"term_size",
"unicode-width",
"terminal_size",
]
[[package]]
@@ -584,12 +619,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
@@ -606,17 +635,11 @@ dependencies = [
"log",
]
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
+15 -10
View File
@@ -16,7 +16,7 @@ license = "MIT/Apache-2.0"
name = "fd-find"
readme = "README.md"
repository = "https://github.com/sharkdp/fd"
version = "8.3.0"
version = "8.3.2"
edition= "2018"
[badges.appveyor]
@@ -30,14 +30,15 @@ name = "fd"
path = "src/main.rs"
[build-dependencies]
clap = "2.31.2"
clap = "3.0"
clap_complete = "3.0"
version_check = "0.9"
[dependencies]
ansi_term = "0.12"
atty = "0.2"
ignore = "0.4.3"
num_cpus = "1.8"
num_cpus = "1.13"
regex = "1.5.4"
regex-syntax = "0.6"
ctrlc = "3.2"
@@ -46,17 +47,17 @@ lscolors = "0.8"
globset = "0.4"
anyhow = "1.0"
dirs-next = "2.0"
normpath = "0.3"
normpath = "0.3.2"
chrono = "0.4"
once_cell = "1.8.0"
once_cell = "1.9.0"
[dependencies.clap]
version = "2.31.3"
features = ["suggestions", "color", "wrap_help"]
version = "3.0"
features = ["suggestions", "color", "wrap_help", "cargo"]
[target.'cfg(unix)'.dependencies]
users = "0.11.0"
nix = "0.23.0"
nix = "0.23.1"
[target.'cfg(all(unix, not(target_os = "redox")))'.dependencies]
libc = "0.2"
@@ -64,8 +65,8 @@ libc = "0.2"
# FIXME: Re-enable jemalloc on macOS
# jemalloc is currently disabled on macOS due to a bug in jemalloc in combination with macOS
# Catalina. See https://github.com/sharkdp/fd/issues/498 for details.
[target.'cfg(all(not(windows), not(target_os = "android"), not(target_os = "macos"), not(target_env = "musl")))'.dependencies]
jemallocator = "0.3.0"
[target.'cfg(all(not(windows), not(target_os = "android"), not(target_os = "macos"), not(target_os = "freebsd"), not(target_env = "musl"), not(target_arch = "riscv64")))'.dependencies]
jemallocator = {version = "0.3.0", optional = true}
[dev-dependencies]
diff = "0.1"
@@ -76,3 +77,7 @@ test-case = "1.2"
[profile.release]
lto = true
codegen-units = 1
[features]
use-jemalloc = ["jemallocator"]
default = ["use-jemalloc"]
+2 -2
View File
@@ -535,7 +535,7 @@ Make sure that `$HOME/.local/bin` is in your `$PATH`.
If you use an older version of Ubuntu, you can download the latest `.deb` package from the
[release page](https://github.com/sharkdp/fd/releases) and install it via:
``` bash
sudo dpkg -i fd_8.3.0_amd64.deb # adapt version number and architecture
sudo dpkg -i fd_8.3.2_amd64.deb # adapt version number and architecture
```
### On Debian
@@ -652,7 +652,7 @@ With Rust's package manager [cargo](https://github.com/rust-lang/cargo), you can
```
cargo install fd-find
```
Note that rust version *1.53.0* or later is required.
Note that rust version *1.54.0* or later is required.
`make` is also needed for the build.
+8 -5
View File
@@ -1,11 +1,13 @@
use std::fs;
use clap::Shell;
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.53";
let min_version = "1.54";
match version_check::is_min_version(min_version) {
Some(true) => {}
@@ -24,7 +26,8 @@ fn main() {
fs::create_dir_all(&outdir).unwrap();
let mut app = build_app();
app.gen_completions("fd", Shell::Bash, &outdir);
app.gen_completions("fd", Shell::Fish, &outdir);
app.gen_completions("fd", Shell::PowerShell, &outdir);
// 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();
}
}
+1 -1
View File
@@ -1 +1 @@
msrv = "1.53.0"
msrv = "1.54.0"
+4
View File
@@ -57,6 +57,7 @@ _fd() {
+ no-ignore-partial # some ignore files
"(no-ignore-full --no-ignore-vcs)--no-ignore-vcs[don't respect .gitignore files]"
"!(no-ignore-full --no-global-ignore-file)--no-global-ignore-file[don't respect the global ignore file]"
$no'(no-ignore-full --no-ignore-parent)--no-ignore-parent[]'
+ '(case)' # case-sensitivity
{-s,--case-sensitive}'[perform a case-sensitive search]'
@@ -154,6 +155,9 @@ _fd() {
$no'(--base-directory)--base-directory=[change the current working directory to the given path]:directory:_files -/'
$no'(*)*--search-path=[set search path (instead of positional <path> arguments)]:directory:_files -/'
+ strip-cwd-prefix
$no'(strip-cwd-prefix exec-cmds)--strip-cwd-prefix[Strip ./ prefix when output is redirected]'
+ args # positional arguments
'1: :_guard "^-*" pattern'
'(--search-path)*:directory:_files -/'
Vendored
+13 -1
View File
@@ -66,6 +66,10 @@ git setting, which defaults to
.IR $HOME/.config/git/ignore ).
The flag can be overridden with '--ignore-vcs'.
.TP
.B \-\-no\-ignore\-parent
Show search results from files and directories that would otherwise be ignored by gitignore files in
parent directories.
.TP
.B \-s, \-\-case\-sensitive
Perform a case-sensitive search. By default, fd uses case-insensitive searches, unless the
pattern contains an uppercase character (smart case).
@@ -126,6 +130,10 @@ can be used as an alias.
Enable the display of filesystem errors for situations such as insufficient
permissions or dead symlinks.
.TP
.B \-\-strip-cwd-prefix
By default, relative paths are prefixed with './' when the output goes to a non interactive terminal
(TTY). Use this flag to disable this behaviour.
.TP
.B \-\-one\-file\-system, \-\-mount, \-\-xdev
By default, fd will traverse the file system tree as far as other options dictate. With this flag, fd ensures that it does not descend into a different file system than the one it started in. Comparable to the -mount or -xdev filters of find(1).
.TP
@@ -393,7 +401,11 @@ Examples:
fd -e rs -X wc -l
.RE
.TP
.BI "\-\-batch-size " size
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.
.SH PATTERN SYNTAX
The regular expression syntax used by fd is documented here:
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

+161
View File
@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="66mm"
height="66mm"
viewBox="0 0 66 66.000001"
version="1.1"
id="svg5"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
sodipodi:docname="logo.svg"
inkscape:export-filename="/home/shark/Informatik/rust/fd/doc/logo.png"
inkscape:export-xdpi="192.42"
inkscape:export-ydpi="192.42"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#999999"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="2.1795515"
inkscape:cx="114.47309"
inkscape:cy="176.18304"
inkscape:window-width="1920"
inkscape:window-height="1175"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="false"
inkscape:guide-bbox="true"
inkscape:snap-global="false"
fit-margin-top="2"
fit-margin-left="2"
fit-margin-right="2"
fit-margin-bottom="2"
lock-margins="true">
<sodipodi:guide
position="26.228232,26.126763"
orientation="0,-1"
id="guide47826" />
<sodipodi:guide
position="25.799494,2.3628924"
orientation="0,-1"
id="guide47828" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-21.358009,-148.28012)">
<g
id="g66267"
transform="matrix(0.84959471,0,0,0.84959471,7.9920783,43.351816)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.7126px;line-height:0;font-family:'Fira Sans Condensed';-inkscape-font-specification:'Fira Sans Condensed, ';white-space:pre;inline-size:37.3715;fill:#e5e5e5;fill-opacity:1;stroke-width:0.742816"
x="50.647034"
y="173.19841"
id="text50653"
transform="matrix(1.0604862,0,0,1.0604862,-3.3101428,-10.150043)"><tspan
x="50.647034"
y="173.19841"
id="tspan66635"><tspan
style="font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro'"
id="tspan66633">fd</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.7126px;line-height:0;font-family:'Fira Sans Condensed';-inkscape-font-specification:'Fira Sans Condensed, ';white-space:pre;inline-size:37.3715;fill:#00ccff;fill-opacity:0.996078;stroke-width:0.742816"
x="50.647034"
y="173.19841"
id="text1244"
transform="matrix(1.0604862,0,0,1.0604862,-2.8008599,-9.6407599)"><tspan
x="50.647034"
y="173.19841"
id="tspan66639"><tspan
style="font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro'"
id="tspan66637">fd</tspan></tspan></text>
<g
id="g47824"
transform="translate(0.1724878,-0.35338542)">
<g
id="g42041">
<path
style="fill:none;stroke:#939dac;stroke-width:1.065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 28.209616,155.51329 v 15.68758 H 40.83001"
id="path39763"
sodipodi:nodetypes="ccc" />
<path
style="fill:#b7bec8;stroke:#939dac;stroke-width:1.065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 27.869464,161.83767 H 40.261291"
id="path39765" />
</g>
<g
id="g41945"
transform="translate(-1.0583333)">
<path
style="fill:#0088aa;fill-opacity:0.993797;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 42.164961,159.42939 -1.554274,-1.89462 -1.975227,-0.005 c -0.941841,0.014 -1.165466,0.27232 -1.14085,2.88812 z"
id="path40006"
sodipodi:nodetypes="ccccc" />
<rect
style="fill:#01ccff;fill-opacity:1;stroke:none;stroke-width:2.3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.80126;paint-order:stroke fill markers"
id="rect39949"
width="9.4925022"
height="6.2080379"
x="37.492516"
y="158.82776"
ry="0.90871465" />
</g>
<g
id="g41951"
transform="translate(-1.0583334,9.3665773)">
<path
style="fill:#373e48;fill-opacity:0.993797;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 42.164961,159.42939 -1.554274,-1.89462 -1.975227,-0.005 c -0.941841,0.014 -1.165466,0.27232 -1.14085,2.88812 z"
id="path41947"
sodipodi:nodetypes="ccccc" />
<rect
style="fill:#535d6c;fill-opacity:0.993797;stroke:none;stroke-width:2.3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.80126;paint-order:stroke fill markers"
id="rect41949"
width="9.4925022"
height="6.2080379"
x="37.492516"
y="158.82776"
ry="0.90871465" />
</g>
<g
id="g41957"
transform="translate(-14.306994,-6.8962642)">
<path
style="fill:#373e48;fill-opacity:0.993797;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 42.164961,159.42939 -1.554274,-1.89462 -1.975227,-0.005 c -0.941841,0.014 -1.165466,0.27232 -1.14085,2.88812 z"
id="path41953"
sodipodi:nodetypes="ccccc" />
<rect
style="fill:#535d6c;fill-opacity:0.993797;stroke:none;stroke-width:2.3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.80126;paint-order:stroke fill markers"
id="rect41955"
width="9.4925022"
height="6.2080379"
x="37.492516"
y="158.82776"
ry="0.90871465" />
</g>
</g>
<g
id="g65006"
transform="matrix(0.55302761,0,0,0.55302761,66.463548,117.45819)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

+130 -120
View File
@@ -1,25 +1,25 @@
use clap::{crate_version, App, AppSettings, Arg};
use clap::{crate_version, App, AppSettings, Arg, ColorChoice};
pub fn build_app() -> App<'static, 'static> {
let clap_color_setting = if std::env::var_os("NO_COLOR").is_none() {
AppSettings::ColoredHelp
pub fn build_app() -> App<'static> {
let clap_color_choice = if std::env::var_os("NO_COLOR").is_none() {
ColorChoice::Auto
} else {
AppSettings::ColorNever
ColorChoice::Never
};
let mut app = App::new("fd")
.version(crate_version!())
.usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
.setting(clap_color_setting)
.color(clap_color_choice)
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::DontCollapseArgsInUsage)
.after_help(
"Note: `fd -h` prints a short and concise overview while `fd --help` gives all \
details.",
)
.arg(
Arg::with_name("hidden")
Arg::new("hidden")
.long("hidden")
.short("H")
.short('H')
.overrides_with("hidden")
.help("Search hidden files and directories")
.long_help(
@@ -30,18 +30,18 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("no-hidden")
Arg::new("no-hidden")
.long("no-hidden")
.overrides_with("hidden")
.hidden(true)
.hide(true)
.long_help(
"Overrides --hidden.",
),
)
.arg(
Arg::with_name("no-ignore")
Arg::new("no-ignore")
.long("no-ignore")
.short("I")
.short('I')
.overrides_with("no-ignore")
.help("Do not respect .(git|fd)ignore files")
.long_help(
@@ -51,19 +51,19 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("ignore")
Arg::new("ignore")
.long("ignore")
.overrides_with("no-ignore")
.hidden(true)
.hide(true)
.long_help(
"Overrides --no-ignore.",
),
)
.arg(
Arg::with_name("no-ignore-vcs")
Arg::new("no-ignore-vcs")
.long("no-ignore-vcs")
.overrides_with("no-ignore-vcs")
.hidden_short_help(true)
.hide_short_help(true)
.help("Do not respect .gitignore files")
.long_help(
"Show search results from files and directories that would otherwise be \
@@ -71,19 +71,19 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("ignore-vcs")
Arg::new("ignore-vcs")
.long("ignore-vcs")
.overrides_with("no-ignore-vcs")
.hidden(true)
.hide(true)
.long_help(
"Overrides --no-ignore-vcs.",
),
)
.arg(
Arg::with_name("no-ignore-parent")
Arg::new("no-ignore-parent")
.long("no-ignore-parent")
.overrides_with("no-ignore-parent")
.hidden_short_help(true)
.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 \
@@ -91,19 +91,19 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("no-global-ignore-file")
Arg::new("no-global-ignore-file")
.long("no-global-ignore-file")
.hidden(true)
.hide(true)
.help("Do not respect the global ignore file")
.long_help("Do not respect the global ignore file."),
)
.arg(
Arg::with_name("rg-alias-hidden-ignore")
.short("u")
Arg::new("rg-alias-hidden-ignore")
.short('u')
.long("unrestricted")
.overrides_with_all(&["ignore", "no-hidden"])
.multiple(true)
.hidden_short_help(true)
.multiple_occurrences(true)
.hide_short_help(true)
.help("Alias for '--no-ignore', and '--hidden' when given twice")
.long_help(
"Alias for '--no-ignore'. Can be repeated. '-uu' is an alias for \
@@ -111,9 +111,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("case-sensitive")
Arg::new("case-sensitive")
.long("case-sensitive")
.short("s")
.short('s')
.overrides_with_all(&["ignore-case", "case-sensitive"])
.help("Case-sensitive search (default: smart case)")
.long_help(
@@ -123,9 +123,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("ignore-case")
Arg::new("ignore-case")
.long("ignore-case")
.short("i")
.short('i')
.overrides_with_all(&["case-sensitive", "ignore-case"])
.help("Case-insensitive search (default: smart case)")
.long_help(
@@ -135,19 +135,19 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("glob")
Arg::new("glob")
.long("glob")
.short("g")
.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::with_name("regex")
Arg::new("regex")
.long("regex")
.overrides_with_all(&["glob", "regex"])
.hidden_short_help(true)
.hide_short_help(true)
.help("Regular-expression based search (default)")
.long_help(
"Perform a regular-expression based search (default). This can be used to \
@@ -155,12 +155,12 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("fixed-strings")
Arg::new("fixed-strings")
.long("fixed-strings")
.short("F")
.short('F')
.alias("literal")
.overrides_with("fixed-strings")
.hidden_short_help(true)
.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 \
@@ -169,9 +169,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("absolute-path")
Arg::new("absolute-path")
.long("absolute-path")
.short("a")
.short('a')
.overrides_with("absolute-path")
.help("Show absolute instead of relative paths")
.long_help(
@@ -180,18 +180,18 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("relative-path")
Arg::new("relative-path")
.long("relative-path")
.overrides_with("absolute-path")
.hidden(true)
.hide(true)
.long_help(
"Overrides --absolute-path.",
),
)
.arg(
Arg::with_name("list-details")
Arg::new("list-details")
.long("list-details")
.short("l")
.short('l')
.conflicts_with("absolute-path")
.help("Use a long listing format with file metadata")
.long_help(
@@ -202,9 +202,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("follow")
Arg::new("follow")
.long("follow")
.short("L")
.short('L')
.alias("dereference")
.overrides_with("follow")
.help("Follow symbolic links")
@@ -215,18 +215,18 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("no-follow")
Arg::new("no-follow")
.long("no-follow")
.overrides_with("follow")
.hidden(true)
.hide(true)
.long_help(
"Overrides --follow.",
),
)
.arg(
Arg::with_name("full-path")
Arg::new("full-path")
.long("full-path")
.short("p")
.short('p')
.overrides_with("full-path")
.help("Search full abs. path (default: filename only)")
.long_help(
@@ -237,12 +237,12 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("null_separator")
Arg::new("null_separator")
.long("print0")
.short("0")
.short('0')
.overrides_with("print0")
.conflicts_with("list-details")
.hidden_short_help(true)
.hide_short_help(true)
.help("Separate results by the null character")
.long_help(
"Separate search results by the null character (instead of newlines). \
@@ -250,9 +250,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("max-depth")
Arg::new("max-depth")
.long("max-depth")
.short("d")
.short('d')
.takes_value(true)
.value_name("depth")
.help("Set maximum search depth (default: none)")
@@ -263,18 +263,18 @@ pub fn build_app() -> App<'static, 'static> {
)
// support --maxdepth as well, for compatibility with rg
.arg(
Arg::with_name("rg-depth")
Arg::new("rg-depth")
.long("maxdepth")
.hidden(true)
.hide(true)
.takes_value(true)
.help("Set maximum search depth (default: none)")
)
.arg(
Arg::with_name("min-depth")
Arg::new("min-depth")
.long("min-depth")
.takes_value(true)
.value_name("depth")
.hidden_short_help(true)
.hide_short_help(true)
.help("Only show results starting at given depth")
.long_help(
"Only show search results starting at the given depth. \
@@ -282,11 +282,11 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("exact-depth")
Arg::new("exact-depth")
.long("exact-depth")
.takes_value(true)
.value_name("depth")
.hidden_short_help(true)
.hide_short_help(true)
.conflicts_with_all(&["max-depth", "min-depth"])
.help("Only show results at exact given depth")
.long_help(
@@ -295,19 +295,19 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("prune")
Arg::new("prune")
.long("prune")
.conflicts_with_all(&["size", "exact-depth"])
.hidden_short_help(true)
.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::with_name("file-type")
Arg::new("file-type")
.long("type")
.short("t")
.multiple(true)
.short('t')
.multiple_occurrences(true)
.number_of_values(1)
.takes_value(true)
.value_name("filetype")
@@ -366,10 +366,10 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("extension")
Arg::new("extension")
.long("extension")
.short("e")
.multiple(true)
.short('e')
.multiple_occurrences(true)
.number_of_values(1)
.takes_value(true)
.value_name("ext")
@@ -382,9 +382,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("exec")
Arg::new("exec")
.long("exec")
.short("x")
.short('x')
.min_values(1)
.allow_hyphen_values(true)
.value_terminator(";")
@@ -413,9 +413,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("exec-batch")
Arg::new("exec-batch")
.long("exec-batch")
.short("X")
.short('X')
.min_values(1)
.allow_hyphen_values(true)
.value_terminator(";")
@@ -440,11 +440,11 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("batch-size")
Arg::new("batch-size")
.long("batch-size")
.takes_value(true)
.value_name("size")
.hidden_short_help(true)
.hide_short_help(true)
.requires("exec-batch")
.help("Max number of arguments to run as a batch with -X")
.long_help(
@@ -455,13 +455,13 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("exclude")
Arg::new("exclude")
.long("exclude")
.short("E")
.short('E')
.takes_value(true)
.value_name("pattern")
.number_of_values(1)
.multiple(true)
.multiple_occurrences(true)
.help("Exclude entries that match the given glob pattern")
.long_help(
"Exclude files/directories that match the given glob pattern. This \
@@ -473,13 +473,13 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("ignore-file")
Arg::new("ignore-file")
.long("ignore-file")
.takes_value(true)
.value_name("path")
.number_of_values(1)
.multiple(true)
.hidden_short_help(true)
.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 \
@@ -487,9 +487,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("color")
Arg::new("color")
.long("color")
.short("c")
.short('c')
.takes_value(true)
.value_name("when")
.possible_values(&["never", "auto", "always"])
@@ -503,12 +503,12 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("threads")
Arg::new("threads")
.long("threads")
.short("j")
.short('j')
.takes_value(true)
.value_name("num")
.hidden_short_help(true)
.hide_short_help(true)
.help("Set number of threads")
.long_help(
"Set number of threads to use for searching & executing (default: number \
@@ -516,13 +516,13 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("size")
Arg::new("size")
.long("size")
.short("S")
.short('S')
.takes_value(true)
.number_of_values(1)
.allow_hyphen_values(true)
.multiple(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 \
@@ -544,10 +544,10 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("max-buffer-time")
Arg::new("max-buffer-time")
.long("max-buffer-time")
.takes_value(true)
.hidden(true)
.hide(true)
.help("Milliseconds to buffer before streaming search results to console")
.long_help(
"Amount of time in milliseconds to buffer, before streaming the search \
@@ -555,7 +555,7 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("changed-within")
Arg::new("changed-within")
.long("changed-within")
.alias("change-newer-than")
.alias("newer")
@@ -575,7 +575,7 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("changed-before")
Arg::new("changed-before")
.long("changed-before")
.alias("change-older-than")
.alias("older")
@@ -594,7 +594,7 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("max-results")
Arg::new("max-results")
.long("max-results")
.takes_value(true)
.value_name("count")
@@ -604,14 +604,14 @@ pub fn build_app() -> App<'static, 'static> {
// same search with `--exec rm` attached and get a reliable removal of
// the files they saw in the previous search.
.conflicts_with_all(&["exec", "exec-batch", "list-details"])
.hidden_short_help(true)
.hide_short_help(true)
.help("Limit number of search results")
.long_help("Limit the number of search results to 'count' and quit immediately."),
)
.arg(
Arg::with_name("max-one-result")
.short("1")
.hidden_short_help(true)
Arg::new("max-one-result")
.short('1')
.hide_short_help(true)
.overrides_with("max-results")
.conflicts_with_all(&["exec", "exec-batch", "list-details"])
.help("Limit search to a single result")
@@ -619,11 +619,11 @@ pub fn build_app() -> App<'static, 'static> {
This is an alias for '--max-results=1'.")
)
.arg(
Arg::with_name("quiet")
Arg::new("quiet")
.long("quiet")
.short("q")
.short('q')
.alias("has-results")
.hidden_short_help(true)
.hide_short_help(true)
.conflicts_with_all(&["exec", "exec-batch", "list-details", "max-results"])
.help("Print nothing, exit code 0 if match found, 1 otherwise")
.long_help(
@@ -634,9 +634,9 @@ pub fn build_app() -> App<'static, 'static> {
)
)
.arg(
Arg::with_name("show-errors")
Arg::new("show-errors")
.long("show-errors")
.hidden_short_help(true)
.hide_short_help(true)
.overrides_with("show-errors")
.help("Show filesystem errors")
.long_help(
@@ -645,12 +645,13 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("base-directory")
Arg::new("base-directory")
.long("base-directory")
.takes_value(true)
.value_name("path")
.number_of_values(1)
.hidden_short_help(true)
.allow_invalid_utf8(true)
.hide_short_help(true)
.help("Change current working directory")
.long_help(
"Change the current working directory of fd to the provided path. This \
@@ -661,7 +662,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("pattern").help(
Arg::new("pattern")
.allow_invalid_utf8(true)
.help(
"the search pattern (a regular expression, unless '--glob' is used; optional)",
).long_help(
"the search pattern which is either a regular expression (default) or a glob \
@@ -670,11 +673,11 @@ pub fn build_app() -> App<'static, 'static> {
pass '--' first, or it will be considered as a flag (fd -- '-foo').")
)
.arg(
Arg::with_name("path-separator")
Arg::new("path-separator")
.takes_value(true)
.value_name("separator")
.long("path-separator")
.hidden_short_help(true)
.hide_short_help(true)
.help("Set path separator when printing file paths")
.long_help(
"Set the path separator to use when printing file paths. The default is \
@@ -682,8 +685,9 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("path")
.multiple(true)
Arg::new("path")
.multiple_occurrences(true)
.allow_invalid_utf8(true)
.help("the root directory for the filesystem search (optional)")
.long_help(
"The directory where the filesystem search is rooted (optional). If \
@@ -691,25 +695,26 @@ pub fn build_app() -> App<'static, 'static> {
),
)
.arg(
Arg::with_name("search-path")
Arg::new("search-path")
.long("search-path")
.takes_value(true)
.conflicts_with("path")
.multiple(true)
.hidden_short_help(true)
.multiple_occurrences(true)
.hide_short_help(true)
.number_of_values(1)
.allow_invalid_utf8(true)
.help("Provide paths to search as an alternative to the positional <path>")
.long_help(
"Provide paths to search as an alternative to the positional <path> \
argument. Changes the usage to `fd [FLAGS/OPTIONS] --search-path <path> \
argument. Changes the usage to `fd [OPTIONS] --search-path <path> \
--search-path <path2> [<pattern>]`",
),
)
.arg(
Arg::with_name("strip-cwd-prefix")
Arg::new("strip-cwd-prefix")
.long("strip-cwd-prefix")
.conflicts_with_all(&["path", "search-path"])
.hidden_short_help(true)
.hide_short_help(true)
.help("strip './' prefix from non-tty outputs")
.long_help(
"By default, relative paths are prefixed with './' when the output goes to a non \
@@ -719,9 +724,9 @@ pub fn build_app() -> App<'static, 'static> {
if cfg!(unix) {
app = app.arg(
Arg::with_name("owner")
Arg::new("owner")
.long("owner")
.short("o")
.short('o')
.takes_value(true)
.value_name("user:group")
.help("Filter by owning user and/or group")
@@ -742,10 +747,10 @@ pub fn build_app() -> App<'static, 'static> {
// Provide aliases `mount` and `xdev` for people coming from `find`.
if cfg!(any(unix, windows)) {
app = app.arg(
Arg::with_name("one-file-system")
Arg::new("one-file-system")
.long("one-file-system")
.aliases(&["mount", "xdev"])
.hidden_short_help(true)
.hide_short_help(true)
.help("Do not descend into a different file system")
.long_help(
"By default, fd will traverse the file system tree as far as other options \
@@ -758,3 +763,8 @@ pub fn build_app() -> App<'static, 'static> {
app
}
#[test]
fn verify_app() {
build_app().debug_assert()
}
+2 -15
View File
@@ -2,6 +2,7 @@ use crate::filesystem;
use crate::walk;
/// Whether or not to show
#[derive(Default)]
pub struct FileTypes {
pub files: bool,
pub directories: bool,
@@ -12,20 +13,6 @@ pub struct FileTypes {
pub empty_only: bool,
}
impl Default for FileTypes {
fn default() -> FileTypes {
FileTypes {
files: false,
directories: false,
symlinks: false,
sockets: false,
pipes: false,
executables_only: false,
empty_only: false,
}
}
}
impl FileTypes {
pub fn should_ignore(&self, entry: &walk::DirEntry) -> bool {
if let Some(ref entry_type) = entry.file_type() {
@@ -37,7 +24,7 @@ impl FileTypes {
|| (self.executables_only
&& !entry
.metadata()
.map(|m| filesystem::is_executable(m))
.map(filesystem::is_executable)
.unwrap_or(false))
|| (self.empty_only && !filesystem::is_empty(entry))
|| !(entry_type.is_file()
+5 -5
View File
@@ -38,7 +38,10 @@ use crate::regex_helper::{pattern_has_uppercase_char, pattern_matches_strings_wi
not(windows),
not(target_os = "android"),
not(target_os = "macos"),
not(target_env = "musl")
not(target_os = "freebsd"),
not(target_env = "musl"),
not(target_arch = "riscv64"),
feature = "use-jemalloc"
))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
@@ -259,10 +262,7 @@ fn construct_config(matches: clap::ArgMatches, pattern_regex: &str) -> Result<Co
read_vcsignore: !(matches.is_present("no-ignore")
|| matches.is_present("rg-alias-hidden-ignore")
|| matches.is_present("no-ignore-vcs")),
read_parent_ignore: !(matches.is_present("no-ignore")
|| matches.is_present("rg-alias-hidden-ignore")
|| matches.is_present("no-ignore-vcs")
|| matches.is_present("no-ignore-parent")),
read_parent_ignore: !matches.is_present("no-ignore-parent"),
read_global_ignore: !(matches.is_present("no-ignore")
|| matches.is_present("rg-alias-hidden-ignore")
|| matches.is_present("no-global-ignore-file")),
+2 -15
View File
@@ -1,7 +1,6 @@
use std::borrow::Cow;
use std::io::{self, Write};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use lscolors::{Indicator, LsColors, Style};
@@ -15,12 +14,7 @@ fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
}
// TODO: this function is performance critical and can probably be optimized
pub fn print_entry<W: Write>(
stdout: &mut W,
entry: &Path,
config: &Config,
wants_to_quit: &AtomicBool,
) {
pub fn print_entry<W: Write>(stdout: &mut W, entry: &Path, config: &Config) {
let path = if config.strip_cwd_prefix {
strip_current_dir(entry)
} else {
@@ -28,7 +22,7 @@ pub fn print_entry<W: Write>(
};
let r = if let Some(ref ls_colors) = config.ls_colors {
print_entry_colorized(stdout, path, config, ls_colors, wants_to_quit)
print_entry_colorized(stdout, path, config, ls_colors)
} else {
print_entry_uncolorized(stdout, path, config)
};
@@ -50,7 +44,6 @@ fn print_entry_colorized<W: Write>(
path: &Path,
config: &Config,
ls_colors: &LsColors,
wants_to_quit: &AtomicBool,
) -> io::Result<()> {
// Split the path between the parent and the last component
let mut offset = 0;
@@ -92,12 +85,6 @@ fn print_entry_colorized<W: Write>(
writeln!(stdout)?;
}
if wants_to_quit.load(Ordering::Relaxed) {
// Ignore any errors on flush, because we're about to exit anyway
let _ = stdout.flush();
ExitCode::KilledBySigint.exit();
}
Ok(())
}
+198 -96
View File
@@ -1,12 +1,13 @@
use std::ffi::OsStr;
use std::fs::{FileType, Metadata};
use std::io;
use std::mem;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time;
use std::time::{Duration, Instant};
use std::{borrow::Cow, io::Write};
use anyhow::{anyhow, Result};
@@ -23,6 +24,7 @@ use crate::filesystem;
use crate::output;
/// The receiver thread can either be buffering results or directly streaming to the console.
#[derive(PartialEq)]
enum ReceiverMode {
/// Receiver is still buffering in order to sort the results, if the search finishes fast
/// enough.
@@ -41,7 +43,7 @@ pub enum WorkerResult {
/// Maximum size of the output buffer before flushing results to the console
pub const MAX_BUFFER_LENGTH: usize = 1000;
/// Default duration until output buffering switches to streaming.
pub const DEFAULT_MAX_BUFFER_TIME: time::Duration = time::Duration::from_millis(100);
pub const DEFAULT_MAX_BUFFER_TIME: Duration = Duration::from_millis(100);
/// Recursively scan the given search path for files / pathnames matching the pattern.
///
@@ -70,7 +72,7 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Config>) -> R
walker
.hidden(config.ignore_hidden)
.ignore(config.read_fdignore)
.parents(config.read_parent_ignore)
.parents(config.read_parent_ignore && (config.read_fdignore || config.read_vcsignore))
.git_ignore(config.read_vcsignore)
.git_global(config.read_vcsignore)
.git_exclude(config.read_vcsignore)
@@ -132,11 +134,19 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Config>) -> R
let parallel_walker = walker.threads(config.threads).build_parallel();
let wants_to_quit = Arc::new(AtomicBool::new(false));
// Flag for cleanly shutting down the parallel walk
let quit_flag = Arc::new(AtomicBool::new(false));
// Flag specifically for quitting due to ^C
let interrupt_flag = Arc::new(AtomicBool::new(false));
if config.ls_colors.is_some() && config.command.is_none() {
let wq = Arc::clone(&wants_to_quit);
let quit_flag = Arc::clone(&quit_flag);
let interrupt_flag = Arc::clone(&interrupt_flag);
ctrlc::set_handler(move || {
if wq.fetch_or(true, Ordering::Relaxed) {
quit_flag.store(true, Ordering::Relaxed);
if interrupt_flag.fetch_or(true, Ordering::Relaxed) {
// Ctrl-C has been pressed twice, exit NOW
ExitCode::KilledBySigint.exit();
}
@@ -145,28 +155,198 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Config>) -> R
}
// Spawn the thread that receives all results through the channel.
let receiver_thread = spawn_receiver(&config, &wants_to_quit, rx);
let receiver_thread = spawn_receiver(&config, &quit_flag, &interrupt_flag, rx);
// Spawn the sender threads.
spawn_senders(&config, &wants_to_quit, pattern, parallel_walker, tx);
spawn_senders(&config, &quit_flag, pattern, parallel_walker, tx);
// Wait for the receiver thread to print out all results.
let exit_code = receiver_thread.join().unwrap();
if wants_to_quit.load(Ordering::Relaxed) {
if interrupt_flag.load(Ordering::Relaxed) {
Ok(ExitCode::KilledBySigint)
} else {
Ok(exit_code)
}
}
/// Wrapper for the receiver thread's buffering behavior.
struct ReceiverBuffer<W> {
/// The configuration.
config: Arc<Config>,
/// For shutting down the senders.
quit_flag: Arc<AtomicBool>,
/// The ^C notifier.
interrupt_flag: Arc<AtomicBool>,
/// Receiver for worker results.
rx: Receiver<WorkerResult>,
/// Standard output.
stdout: W,
/// The current buffer mode.
mode: ReceiverMode,
/// The deadline to switch to streaming mode.
deadline: Instant,
/// The buffer of quickly received paths.
buffer: Vec<PathBuf>,
/// Result count.
num_results: usize,
}
impl<W: Write> ReceiverBuffer<W> {
/// Create a new receiver buffer.
fn new(
config: Arc<Config>,
quit_flag: Arc<AtomicBool>,
interrupt_flag: Arc<AtomicBool>,
rx: Receiver<WorkerResult>,
stdout: W,
) -> Self {
let max_buffer_time = config.max_buffer_time.unwrap_or(DEFAULT_MAX_BUFFER_TIME);
let deadline = Instant::now() + max_buffer_time;
Self {
config,
quit_flag,
interrupt_flag,
rx,
stdout,
mode: ReceiverMode::Buffering,
deadline,
buffer: Vec::with_capacity(MAX_BUFFER_LENGTH),
num_results: 0,
}
}
/// Process results until finished.
fn process(&mut self) -> ExitCode {
loop {
if let Err(ec) = self.poll() {
self.quit_flag.store(true, Ordering::Relaxed);
return ec;
}
}
}
/// Receive the next worker result.
fn recv(&self) -> Result<WorkerResult, RecvTimeoutError> {
match self.mode {
ReceiverMode::Buffering => {
// Wait at most until we should switch to streaming
let now = Instant::now();
self.deadline
.checked_duration_since(now)
.ok_or(RecvTimeoutError::Timeout)
.and_then(|t| self.rx.recv_timeout(t))
}
ReceiverMode::Streaming => {
// Wait however long it takes for a result
Ok(self.rx.recv()?)
}
}
}
/// Wait for a result or state change.
fn poll(&mut self) -> Result<(), ExitCode> {
match self.recv() {
Ok(WorkerResult::Entry(path)) => {
if self.config.quiet {
return Err(ExitCode::HasResults(true));
}
match self.mode {
ReceiverMode::Buffering => {
self.buffer.push(path);
if self.buffer.len() > MAX_BUFFER_LENGTH {
self.stream()?;
}
}
ReceiverMode::Streaming => {
self.print(&path)?;
self.flush()?;
}
}
self.num_results += 1;
if let Some(max_results) = self.config.max_results {
if self.num_results >= max_results {
return self.stop();
}
}
}
Ok(WorkerResult::Error(err)) => {
if self.config.show_filesystem_errors {
print_error(err.to_string());
}
}
Err(RecvTimeoutError::Timeout) => {
self.stream()?;
}
Err(RecvTimeoutError::Disconnected) => {
return self.stop();
}
}
Ok(())
}
/// Output a path.
fn print(&mut self, path: &Path) -> Result<(), ExitCode> {
output::print_entry(&mut self.stdout, path, &self.config);
if self.interrupt_flag.load(Ordering::Relaxed) {
// Ignore any errors on flush, because we're about to exit anyway
let _ = self.flush();
return Err(ExitCode::KilledBySigint);
}
Ok(())
}
/// Switch ourselves into streaming mode.
fn stream(&mut self) -> Result<(), ExitCode> {
self.mode = ReceiverMode::Streaming;
let buffer = mem::take(&mut self.buffer);
for path in buffer {
self.print(&path)?;
}
self.flush()
}
/// Stop looping.
fn stop(&mut self) -> Result<(), ExitCode> {
if self.mode == ReceiverMode::Buffering {
self.buffer.sort();
self.stream()?;
}
if self.config.quiet {
Err(ExitCode::HasResults(self.num_results > 0))
} else {
Err(ExitCode::Success)
}
}
/// Flush stdout if necessary.
fn flush(&mut self) -> Result<(), ExitCode> {
if self.config.interactive_terminal && self.stdout.flush().is_err() {
// Probably a broken pipe. Exit gracefully.
return Err(ExitCode::GeneralError);
}
Ok(())
}
}
fn spawn_receiver(
config: &Arc<Config>,
wants_to_quit: &Arc<AtomicBool>,
quit_flag: &Arc<AtomicBool>,
interrupt_flag: &Arc<AtomicBool>,
rx: Receiver<WorkerResult>,
) -> thread::JoinHandle<ExitCode> {
let config = Arc::clone(config);
let wants_to_quit = Arc::clone(wants_to_quit);
let quit_flag = Arc::clone(quit_flag);
let interrupt_flag = Arc::clone(interrupt_flag);
let show_filesystem_errors = config.show_filesystem_errors;
let threads = config.threads;
@@ -218,90 +398,12 @@ fn spawn_receiver(
merge_exitcodes(exit_codes)
}
} else {
let start = time::Instant::now();
// Start in buffering mode
let mut mode = ReceiverMode::Buffering;
// Maximum time to wait before we start streaming to the console.
let max_buffer_time = config.max_buffer_time.unwrap_or(DEFAULT_MAX_BUFFER_TIME);
let stdout = io::stdout();
let stdout = stdout.lock();
let mut stdout = io::BufWriter::new(stdout);
let stdout = io::BufWriter::new(stdout);
let mut num_results = 0;
let is_interactive = config.interactive_terminal;
let mut buffer = Vec::with_capacity(MAX_BUFFER_LENGTH);
for worker_result in rx {
match worker_result {
WorkerResult::Entry(path) => {
if config.quiet {
return ExitCode::HasResults(true);
}
match mode {
ReceiverMode::Buffering => {
buffer.push(path);
// Have we reached the maximum buffer size or maximum buffering time?
if buffer.len() > MAX_BUFFER_LENGTH
|| start.elapsed() > max_buffer_time
{
// Flush the buffer
for path in &buffer {
output::print_entry(
&mut stdout,
path,
&config,
&wants_to_quit,
);
}
buffer.clear();
if is_interactive && stdout.flush().is_err() {
// Probably a broken pipe. Exit gracefully.
return ExitCode::GeneralError;
}
// Start streaming
mode = ReceiverMode::Streaming;
}
}
ReceiverMode::Streaming => {
output::print_entry(&mut stdout, &path, &config, &wants_to_quit);
if is_interactive && stdout.flush().is_err() {
// Probably a broken pipe. Exit gracefully.
return ExitCode::GeneralError;
}
}
}
num_results += 1;
if let Some(max_results) = config.max_results {
if num_results >= max_results {
break;
}
}
}
WorkerResult::Error(err) => {
if show_filesystem_errors {
print_error(err.to_string());
}
}
}
}
// If we have finished fast enough (faster than max_buffer_time), we haven't streamed
// anything to the console, yet. In this case, sort the results and print them:
buffer.sort();
for value in buffer {
output::print_entry(&mut stdout, &value, &config, &wants_to_quit);
}
if config.quiet {
ExitCode::HasResults(false)
} else {
ExitCode::Success
}
let mut rxbuffer = ReceiverBuffer::new(config, quit_flag, interrupt_flag, rx, stdout);
rxbuffer.process()
}
})
}
@@ -364,7 +466,7 @@ impl DirEntry {
fn spawn_senders(
config: &Arc<Config>,
wants_to_quit: &Arc<AtomicBool>,
quit_flag: &Arc<AtomicBool>,
pattern: Arc<Regex>,
parallel_walker: ignore::WalkParallel,
tx: Sender<WorkerResult>,
@@ -373,10 +475,10 @@ fn spawn_senders(
let config = Arc::clone(config);
let pattern = Arc::clone(&pattern);
let tx_thread = tx.clone();
let wants_to_quit = Arc::clone(wants_to_quit);
let quit_flag = Arc::clone(quit_flag);
Box::new(move |entry_o| {
if wants_to_quit.load(Ordering::Relaxed) {
if quit_flag.load(Ordering::Relaxed) {
return ignore::WalkState::Quit;
}
+27 -13
View File
@@ -130,6 +130,17 @@ fn normalize_output(s: &str, trim_start: bool, normalize_line: bool) -> String {
lines.join("\n")
}
/// Trim whitespace from the beginning of each line.
fn trim_lines(s: &str) -> String {
s.lines()
.map(|line| line.trim_start())
.fold(String::new(), |mut str, line| {
str.push_str(line);
str.push('\n');
str
})
}
impl TestEnv {
pub fn new(directories: &[&'static str], files: &[&'static str]) -> TestEnv {
let temp_dir = create_working_directory(directories, files).expect("working directory");
@@ -203,6 +214,19 @@ impl TestEnv {
output
}
pub fn assert_success_and_get_normalized_output<P: AsRef<Path>>(
&self,
path: P,
args: &[&str],
) -> String {
let output = self.assert_success_and_get_output(path, args);
normalize_output(
&String::from_utf8_lossy(&output.stdout),
false,
self.normalize_line,
)
}
/// Assert that calling *fd* with the specified arguments produces the expected output.
pub fn assert_output(&self, args: &[&str], expected: &str) {
self.assert_output_subdirectory(".", args, expected)
@@ -224,15 +248,9 @@ impl TestEnv {
args: &[&str],
expected: &str,
) {
let output = self.assert_success_and_get_output(path, args);
// Normalize both expected and actual output.
let expected = normalize_output(expected, true, self.normalize_line);
let actual = normalize_output(
&String::from_utf8_lossy(&output.stdout),
false,
self.normalize_line,
);
let actual = self.assert_success_and_get_normalized_output(path, args);
// Compare actual output to expected output.
if expected != actual {
@@ -280,12 +298,8 @@ impl TestEnv {
if let Some(expected) = expected {
// Normalize both expected and actual output.
let expected_error = normalize_output(expected, true, self.normalize_line);
let actual_err = normalize_output(
&String::from_utf8_lossy(&output.stderr),
false,
self.normalize_line,
);
let expected_error = trim_lines(expected);
let actual_err = trim_lines(&String::from_utf8_lossy(&output.stderr));
// Compare actual output to expected output.
if !actual_err.trim_start().starts_with(&expected_error) {
+20 -4
View File
@@ -614,6 +614,22 @@ fn test_no_ignore_vcs() {
);
}
/// Test that --no-ignore-vcs still respects .fdignored in parent directory
#[test]
fn test_no_ignore_vcs_child_dir() {
let te = TestEnv::new(
&["inner"],
&["inner/fdignored.foo", "inner/foo", "inner/gitignored.foo"],
);
te.assert_output_subdirectory(
"inner",
&["--no-ignore-vcs", "foo"],
"./foo
./gitignored.foo",
);
}
/// Custom ignore files (--ignore-file)
#[test]
fn test_custom_ignore_files() {
@@ -1404,12 +1420,12 @@ fn test_exec_batch() {
te.assert_failure_with_error(
&["foo", "--exec-batch", "echo", "{/}", ";", "-x", "echo"],
"error: The argument '--exec <cmd>' cannot be used with '--exec-batch <cmd>'",
"error: The argument '--exec-batch <cmd>...' cannot be used with '--exec <cmd>...'",
);
te.assert_failure_with_error(
&["foo", "--exec-batch"],
"error: The argument '--exec-batch <cmd>' requires a value but none was supplied",
"error: The argument '--exec-batch <cmd>...' requires a value but none was supplied",
);
te.assert_failure_with_error(
@@ -1954,8 +1970,8 @@ fn test_opposing(flag: &str, opposing_flags: &[&str]) {
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);
let out_no_flags = te.assert_success_and_get_normalized_output(".", &[]);
let out_opposing_flags = te.assert_success_and_get_normalized_output(".", &flags);
assert_eq!(
out_no_flags,