Merge branch 'master' into pr/opposing-options

This commit is contained in:
Vukašin Stepanović 2021-08-23 15:55:17 +02:00
commit c749c95136
15 changed files with 144 additions and 44 deletions

View File

@ -1,2 +1 @@
blank_issues_enabled: true

View File

@ -6,5 +6,3 @@ labels: feature-request
assignees: ''
---

View File

@ -27,16 +27,22 @@ jobs:
toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy
components: clippy, rustfmt
- name: Ensure `cargo fmt` has been run
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --all-features
args: --locked --all-targets --all-features
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --locked
build:
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
@ -100,7 +106,7 @@ jobs:
with:
use-cross: ${{ matrix.job.use-cross }}
command: build
args: --release --target=${{ matrix.job.target }}
args: --locked --release --target=${{ matrix.job.target }}
- name: Strip debug information from executable
id: strip
@ -142,6 +148,7 @@ jobs:
id: test-options
shell: bash
run: |
# test only library unit tests and binary for arm-type targets
unset CARGO_TEST_OPTIONS
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--bin ${PROJECT_NAME}" ;; esac;
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
@ -151,7 +158,7 @@ jobs:
with:
use-cross: ${{ matrix.job.use-cross }}
command: test
args: --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
args: --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
- name: Create tarball
id: package

View File

@ -1,6 +1,9 @@
# Upcoming release
## Features
- Don't buffer command output from `--exec` when using a single thread. See #522
- Add new `-q, --quiet` flag, see #303 (@Asha20)
- Add opposing command-line options, see #595 (@Asha20)
@ -459,7 +462,7 @@ I'd also like to take this chance to say a special Thank You to a few people tha
* Add option to force colored output: `--color always`, see #49 (@Detegr)
* Generate Shell completions for Bash, ZSH, Fish and Powershell, see #64 (@ImbaKnugel)
* Better & extended `--help` text (@abaez and @Detegr)
* Proper Windows support, see #70
* Proper Windows support, see #70
## Changes
@ -487,9 +490,9 @@ I'd also like to take this chance to say a special Thank You to a few people tha
* Changed `--sensitive` to `--case-sensitive`
* Changed `--absolute` to `--absolute-path`
* Throw an error if root directory is not existent, see #39
* Use absolute paths if the root dir is an absolute path, see #40
* Handle invalid UTF-8, see #34 #38
* Throw an error if root directory is not existent, see #39
* Use absolute paths if the root dir is an absolute path, see #40
* Handle invalid UTF-8, see #34 #38
* Support `-V`, `--version` by switching from `getopts` to `clap`.
Misc:
@ -497,8 +500,8 @@ Misc:
# v1.1.0
- Windows compatibility (@sebasv), see #29 #35
- Safely exit on broken output pipes (e.g.: usage with `head`, `tail`, ..), see #24
- Windows compatibility (@sebasv), see #29 #35
- Safely exit on broken output pipes (e.g.: usage with `head`, `tail`, ..), see #24
- Backport for rust 1.16, see #23
# v1.0.0
@ -510,17 +513,17 @@ Misc:
# v0.3.0
- Parse dircolors files, closes #20
- Colorize each path component, closes #19
- Add short command line option for --hidden, see #18
- Parse dircolors files, closes #20
- Colorize each path component, closes #19
- Add short command line option for --hidden, see #18
# v0.2.0
- Option to follow symlinks, disable colors, closes #16, closes #17
- Option to follow symlinks, disable colors, closes #16, closes #17
- `--filename` instead of `--full-path`
- Option to search hidden directories, closes #12
- Configurable search depth, closes #13
- Detect interactive terminal, closes #11
- Option to search hidden directories, closes #12
- Configurable search depth, closes #13
- Detect interactive terminal, closes #11
# v0.1.0

40
doc/fd.1 vendored
View File

@ -32,19 +32,38 @@ Include hidden files and directories in the search results
.TP
.B \-I, \-\-no\-ignore
Show search results from files and directories that would otherwise be ignored by
.IR .gitignore ,
.IR .ignore ,
.IR .fdignore ,
or the global ignore file.
.RS
.IP \[bu] 2
.I .gitignore
.IP \[bu]
.I .git/info/exclude
.IP \[bu]
The global gitignore configuration (by default
.IR $HOME/.config/git/ignore )
.IP \[bu]
.I .ignore
.IP \[bu]
.I .fdignore
.IP \[bu]
The global fd ignore file (usually
.I $HOME/.config/fd/ignore
)
.RE
.IP
The flag can be overridden with '--ignore'.
.TP
.B \-u, \-\-unrestricted
Alias for '--no-ignore'. Can be repeated; '-uu' is an alias for '--no-ignore --hidden'.
.TP
.B \-\-no\-ignore\-vcs
Show search results from files and directories that would otherwise be ignored by
.I .gitignore
files.
Show search results from files and directories that would otherwise be ignored by gitignore files
including
.IR .gitignore ,
.IR .git/info/exclude ,
and the global gitignore configuration
.RI ( core.excludesFile
git setting, which defaults to
.IR $HOME/.config/git/ignore ).
The flag can be overridden with '--ignore-vcs'.
.TP
.B \-s, \-\-case\-sensitive
@ -96,6 +115,13 @@ Limit the number of search results to 'count' and quit immediately.
.B \-1
Limit the search to a single result and quit immediately. This is an alias for '--max-results=1'.
.TP
.B \-q, \-\-quiet
When the flag is present, the program does not print anything and will instead exit with a code of 0 if there is at least one search result.
Otherwise, the exit code will be 1.
This is mainly for usage in scripts and can be faster than checking for output because the search can be stopped early after the first match.
.B \-\-has\-results
can be used as an alias.
.TP
.B \-\-show-errors
Enable the display of filesystem errors for situations such as insufficient
permissions or dead symlinks.

2
doc/screencast.sh vendored
View File

@ -20,7 +20,7 @@ enter() {
}
prompt() {
printf '%b ' $PROMPT | pv -q
printf '%b ' "$PROMPT" | pv -q
}
type() {

View File

@ -554,6 +554,20 @@ pub fn build_app() -> App<'static, 'static> {
.long_help("Limit the search to a single result and quit immediately. \
This is an alias for '--max-results=1'.")
)
.arg(
Arg::with_name("quiet")
.long("quiet")
.short("q")
.alias("has-results")
.hidden_short_help(true)
.conflicts_with_all(&["exec", "exec-batch", "list-details", "max-results"])
.long_help(
"When the flag is present, the program does not print anything and will \
return with an exit code of 0 if there is at least one match. Otherwise, the \
exit code will be 1. \
'--has-results' can be used as an alias."
)
)
.arg(
Arg::with_name("show-errors")
.long("show-errors")

View File

@ -7,9 +7,19 @@ use crate::error::print_error;
use crate::exit_codes::ExitCode;
/// Executes a command.
pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode {
pub fn execute_command(
mut cmd: Command,
out_perm: &Mutex<()>,
enable_output_buffering: bool,
) -> ExitCode {
// Spawn the supplied command.
let output = cmd.output();
let output = if enable_output_buffering {
cmd.output()
} else {
// If running on only one thread, don't buffer output
// Allows for viewing and interacting with intermediate command output
cmd.spawn().and_then(|c| c.wait_with_output())
};
// Then wait for the command to exit, if it was spawned.
match output {

View File

@ -16,6 +16,7 @@ pub fn job(
cmd: Arc<CommandTemplate>,
out_perm: Arc<Mutex<()>>,
show_filesystem_errors: bool,
buffer_output: bool,
) -> ExitCode {
let mut results: Vec<ExitCode> = Vec::new();
loop {
@ -38,7 +39,7 @@ pub fn job(
// Drop the lock so that other threads can read from the receiver.
drop(lock);
// Generate a command, execute it and store its exit code.
results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm)))
results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm), buffer_output))
}
// Returns error in case of any error.
merge_exitcodes(&results)
@ -48,6 +49,7 @@ pub fn batch(
rx: Receiver<WorkerResult>,
cmd: &CommandTemplate,
show_filesystem_errors: bool,
buffer_output: bool,
) -> ExitCode {
let paths = rx.iter().filter_map(|value| match value {
WorkerResult::Entry(val) => Some(val),
@ -58,5 +60,5 @@ pub fn batch(
None
}
});
cmd.generate_and_execute_batch(paths)
cmd.generate_and_execute_batch(paths, buffer_output)
}

View File

@ -139,7 +139,12 @@ impl CommandTemplate {
///
/// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
/// build. Once all arguments have been processed, the command is executed.
pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) -> ExitCode {
pub fn generate_and_execute(
&self,
input: &Path,
out_perm: Arc<Mutex<()>>,
buffer_output: bool,
) -> ExitCode {
let input = strip_current_dir(input);
let mut cmd = Command::new(self.args[0].generate(&input, self.path_separator.as_deref()));
@ -147,14 +152,14 @@ impl CommandTemplate {
cmd.arg(arg.generate(&input, self.path_separator.as_deref()));
}
execute_command(cmd, &out_perm)
execute_command(cmd, &out_perm, buffer_output)
}
pub fn in_batch_mode(&self) -> bool {
self.mode == ExecutionMode::Batch
}
pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
pub fn generate_and_execute_batch<I>(&self, paths: I, buffer_output: bool) -> ExitCode
where
I: Iterator<Item = PathBuf>,
{
@ -182,7 +187,7 @@ impl CommandTemplate {
}
if has_path {
execute_command(cmd, &Mutex::new(()))
execute_command(cmd, &Mutex::new(()), buffer_output)
} else {
ExitCode::Success
}
@ -445,11 +450,11 @@ mod tests {
// This is uncommon, but valid
check!(r"C:foo\bar", "C:foo#bar");
// forward slashses should get normalized and interpreted as separators
// forward slashes should get normalized and interpreted as separators
check!("C:/foo/bar", "C:#foo#bar");
check!("C:foo/bar", "C:foo#bar");
// Rust does not intrepret "//server/share" as a UNC path, but rather as a normal
// Rust does not interpret "//server/share" as a UNC path, but rather as a normal
// absolute path that begins with RootDir, and the two slashes get combined together as
// a single path separator during normalization.
//check!("//server/share/path", "##server#share#path");

View File

@ -1,6 +1,7 @@
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExitCode {
Success,
HasResults(bool),
GeneralError,
KilledBySigint,
}
@ -9,6 +10,7 @@ impl From<ExitCode> for i32 {
fn from(code: ExitCode) -> Self {
match code {
ExitCode::Success => 0,
ExitCode::HasResults(has_results) => !has_results as i32,
ExitCode::GeneralError => 1,
ExitCode::KilledBySigint => 130,
}
@ -17,7 +19,7 @@ impl From<ExitCode> for i32 {
impl ExitCode {
fn is_error(self) -> bool {
self != ExitCode::Success
i32::from(self) != 0
}
}

View File

@ -346,6 +346,7 @@ fn run() -> Result<ExitCode> {
follow_links: matches.is_present("follow"),
one_file_system: matches.is_present("one-file-system"),
null_separator: matches.is_present("null_separator"),
quiet: matches.is_present("quiet"),
max_depth: matches
.value_of("max-depth")
.or_else(|| matches.value_of("rg-depth"))

View File

@ -54,6 +54,10 @@ pub struct Options {
/// The number of threads to use.
pub threads: usize,
/// If true, the program doesn't print anything and will instead return an exit code of 0
/// if there's at least one match. Otherwise, the exit code will be 1.
pub quiet: bool,
/// Time to buffer results internally before streaming to the console. This is useful to
/// provide a sorted output, in case the total execution time is shorter than
/// `max_buffer_time`.

View File

@ -170,12 +170,13 @@ fn spawn_receiver(
let show_filesystem_errors = config.show_filesystem_errors;
let threads = config.threads;
// This will be used to check if output should be buffered when only running a single thread
let enable_output_buffering: bool = threads > 1;
thread::spawn(move || {
// This will be set to `Some` if the `--exec` argument was supplied.
if let Some(ref cmd) = config.command {
if cmd.in_batch_mode() {
exec::batch(rx, cmd, show_filesystem_errors)
exec::batch(rx, cmd, show_filesystem_errors, enable_output_buffering)
} else {
let shared_rx = Arc::new(Mutex::new(rx));
@ -189,8 +190,15 @@ fn spawn_receiver(
let out_perm = Arc::clone(&out_perm);
// Spawn a job thread that will listen for and execute inputs.
let handle =
thread::spawn(move || exec::job(rx, cmd, out_perm, show_filesystem_errors));
let handle = thread::spawn(move || {
exec::job(
rx,
cmd,
out_perm,
show_filesystem_errors,
enable_output_buffering,
)
});
// Push the handle of the spawned thread into the vector for later joining.
handles.push(handle);
@ -225,6 +233,10 @@ fn spawn_receiver(
for worker_result in rx {
match worker_result {
WorkerResult::Entry(value) => {
if config.quiet {
return ExitCode::HasResults(true);
}
match mode {
ReceiverMode::Buffering => {
buffer.push(value);
@ -278,7 +290,11 @@ fn spawn_receiver(
}
}
ExitCode::Success
if config.quiet {
ExitCode::HasResults(false)
} else {
ExitCode::Success
}
}
})
}

View File

@ -1426,6 +1426,19 @@ fn test_exec_with_separator() {
);
}
/// Non-zero exit code (--quiet)
#[test]
fn test_quiet() {
let dirs = &[];
let files = &["a.foo", "b.foo"];
let te = TestEnv::new(dirs, files);
te.assert_output(&["-q"], "");
te.assert_output(&["--quiet"], "");
te.assert_output(&["--has-results"], "");
te.assert_failure_with_error(&["--quiet", "c.foo"], "")
}
/// Literal search (--fixed-strings)
#[test]
fn test_fixed_strings() {