2023-09-28 22:24:40 +02:00
|
|
|
use std::{
|
|
|
|
io::{self, Write},
|
|
|
|
time::Instant,
|
|
|
|
};
|
2018-08-03 23:26:22 +02:00
|
|
|
|
|
|
|
use ignore::WalkState;
|
2016-02-27 18:07:26 +02:00
|
|
|
|
2023-09-28 22:24:40 +02:00
|
|
|
use crate::{args::Args, subject::Subject};
|
2016-08-27 07:01:06 +02:00
|
|
|
|
2018-08-03 23:26:22 +02:00
|
|
|
#[macro_use]
|
|
|
|
mod messages;
|
2016-02-27 18:07:26 +02:00
|
|
|
|
Switch from Docopt to Clap.
There were two important reasons for the switch:
1. Performance. Docopt does poorly when the argv becomes large, which is
a reasonable common use case for search tools. (e.g., use with xargs)
2. Better failure modes. Clap knows a lot more about how a particular
argv might be invalid, and can therefore provide much clearer error
messages.
While both were important, (1) made it urgent.
Note that since Clap requires at least Rust 1.11, this will in turn
increase the minimum Rust version supported by ripgrep from Rust 1.9 to
Rust 1.11. It is therefore a breaking change, so the soonest release of
ripgrep with Clap will have to be 0.3.
There is also at least one subtle breaking change in real usage.
Previous to this commit, this used to work:
rg -e -foo
Where this would cause ripgrep to search for the string `-foo`. Clap
currently has problems supporting this use case
(see: https://github.com/kbknapp/clap-rs/issues/742),
but it can be worked around by using this instead:
rg -e [-]foo
or even
rg [-]foo
and this still works:
rg -- -foo
This commit also adds Bash, Fish and PowerShell completion files to the
release, fixes a bug that prevented ripgrep from working on file
paths containing invalid UTF-8 and shows short descriptions in the
output of `-h` but longer descriptions in the output of `--help`.
Fixes #136, Fixes #189, Fixes #210, Fixes #230
2016-11-13 04:48:11 +02:00
|
|
|
mod app;
|
2016-09-05 06:52:23 +02:00
|
|
|
mod args;
|
2018-02-04 03:33:52 +02:00
|
|
|
mod config;
|
2018-02-04 04:31:28 +02:00
|
|
|
mod logger;
|
2018-08-03 23:26:22 +02:00
|
|
|
mod search;
|
|
|
|
mod subject;
|
2016-03-11 03:48:44 +02:00
|
|
|
|
2019-04-24 23:21:38 +02:00
|
|
|
// Since Rust no longer uses jemalloc by default, ripgrep will, by default,
|
|
|
|
// use the system allocator. On Linux, this would normally be glibc's
|
|
|
|
// allocator, which is pretty good. In particular, ripgrep does not have a
|
|
|
|
// particularly allocation heavy workload, so there really isn't much
|
|
|
|
// difference (for ripgrep's purposes) between glibc's allocator and jemalloc.
|
|
|
|
//
|
|
|
|
// However, when ripgrep is built with musl, this means ripgrep will use musl's
|
|
|
|
// allocator, which appears to be substantially worse. (musl's goal is not to
|
|
|
|
// have the fastest version of everything. Its goal is to be small and amenable
|
|
|
|
// to static compilation.) Even though ripgrep isn't particularly allocation
|
|
|
|
// heavy, musl's allocator appears to slow down ripgrep quite a bit. Therefore,
|
|
|
|
// when building with musl, we use jemalloc.
|
|
|
|
//
|
|
|
|
// We don't unconditionally use jemalloc because it can be nice to use the
|
|
|
|
// system's default allocator by default. Moreover, jemalloc seems to increase
|
|
|
|
// compilation times by a bit.
|
2019-04-25 16:49:59 +02:00
|
|
|
//
|
|
|
|
// Moreover, we only do this on 64-bit systems since jemalloc doesn't support
|
|
|
|
// i686.
|
|
|
|
#[cfg(all(target_env = "musl", target_pointer_width = "64"))]
|
2019-04-24 23:21:38 +02:00
|
|
|
#[global_allocator]
|
|
|
|
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
|
|
|
|
2018-08-20 23:34:40 +02:00
|
|
|
fn main() {
|
2019-01-26 22:42:55 +02:00
|
|
|
if let Err(err) = Args::parse().and_then(try_main) {
|
2023-10-12 18:16:42 +02:00
|
|
|
eprintln_locked!("{:#}", err);
|
2023-09-28 22:24:40 +02:00
|
|
|
std::process::exit(2);
|
2016-02-27 18:07:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 18:16:42 +02:00
|
|
|
fn try_main(args: Args) -> anyhow::Result<()> {
|
2018-08-03 23:26:22 +02:00
|
|
|
use args::Command::*;
|
|
|
|
|
2023-01-05 15:15:09 +02:00
|
|
|
let matched = match args.command() {
|
2019-11-21 16:03:27 +02:00
|
|
|
Search => search(&args),
|
|
|
|
SearchParallel => search_parallel(&args),
|
|
|
|
SearchNever => Ok(false),
|
|
|
|
Files => files(&args),
|
|
|
|
FilesParallel => files_parallel(&args),
|
|
|
|
Types => types(&args),
|
|
|
|
PCRE2Version => pcre2_version(&args),
|
|
|
|
}?;
|
2019-01-26 22:42:55 +02:00
|
|
|
if matched && (args.quiet() || !messages::errored()) {
|
2023-09-28 22:24:40 +02:00
|
|
|
std::process::exit(0)
|
2019-01-26 22:42:55 +02:00
|
|
|
} else if messages::errored() {
|
2023-09-28 22:24:40 +02:00
|
|
|
std::process::exit(2)
|
2019-01-26 22:42:55 +02:00
|
|
|
} else {
|
2023-09-28 22:24:40 +02:00
|
|
|
std::process::exit(1)
|
2016-11-06 20:09:53 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The top-level entry point for single-threaded search. This recursively
|
|
|
|
/// steps through the file list (current directory by default) and searches
|
|
|
|
/// each file sequentially.
|
2023-10-12 18:16:42 +02:00
|
|
|
fn search(args: &Args) -> anyhow::Result<bool> {
|
2022-11-28 06:36:15 +02:00
|
|
|
/// The meat of the routine is here. This lets us call the same iteration
|
|
|
|
/// code over each file regardless of whether we stream over the files
|
|
|
|
/// as they're produced by the underlying directory traversal or whether
|
|
|
|
/// they've been collected and sorted (for example) first.
|
|
|
|
fn iter(
|
|
|
|
args: &Args,
|
|
|
|
subjects: impl Iterator<Item = Subject>,
|
|
|
|
started_at: std::time::Instant,
|
2023-10-12 18:16:42 +02:00
|
|
|
) -> anyhow::Result<bool> {
|
2022-11-28 06:36:15 +02:00
|
|
|
let quit_after_match = args.quit_after_match()?;
|
|
|
|
let mut stats = args.stats()?;
|
|
|
|
let mut searcher = args.search_worker(args.stdout())?;
|
|
|
|
let mut matched = false;
|
|
|
|
let mut searched = false;
|
2018-08-03 23:26:22 +02:00
|
|
|
|
2022-11-28 06:36:15 +02:00
|
|
|
for subject in subjects {
|
|
|
|
searched = true;
|
|
|
|
let search_result = match searcher.search(&subject) {
|
|
|
|
Ok(search_result) => search_result,
|
2018-08-03 23:26:22 +02:00
|
|
|
// A broken pipe means graceful termination.
|
2022-11-28 06:36:15 +02:00
|
|
|
Err(err) if err.kind() == io::ErrorKind::BrokenPipe => break,
|
|
|
|
Err(err) => {
|
|
|
|
err_message!("{}: {}", subject.path().display(), err);
|
|
|
|
continue;
|
2018-08-03 23:26:22 +02:00
|
|
|
}
|
2022-11-28 06:36:15 +02:00
|
|
|
};
|
|
|
|
matched |= search_result.has_match();
|
|
|
|
if let Some(ref mut stats) = stats {
|
|
|
|
*stats += search_result.stats().unwrap();
|
|
|
|
}
|
|
|
|
if matched && quit_after_match {
|
|
|
|
break;
|
2018-08-03 23:26:22 +02:00
|
|
|
}
|
|
|
|
}
|
2022-11-28 06:36:15 +02:00
|
|
|
if args.using_default_path() && !searched {
|
|
|
|
eprint_nothing_searched();
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
2022-11-28 06:36:15 +02:00
|
|
|
if let Some(ref stats) = stats {
|
|
|
|
let elapsed = Instant::now().duration_since(started_at);
|
|
|
|
// We don't care if we couldn't print this successfully.
|
|
|
|
let _ = searcher.print_stats(elapsed, stats);
|
|
|
|
}
|
|
|
|
Ok(matched)
|
2016-09-26 03:27:17 +02:00
|
|
|
}
|
2022-11-28 06:36:15 +02:00
|
|
|
|
|
|
|
let started_at = Instant::now();
|
|
|
|
let subject_builder = args.subject_builder();
|
|
|
|
let subjects = args
|
|
|
|
.walker()?
|
|
|
|
.filter_map(|result| subject_builder.build_from_result(result));
|
|
|
|
if args.needs_stat_sort() {
|
|
|
|
let subjects = args.sort_by_stat(subjects).into_iter();
|
|
|
|
iter(args, subjects, started_at)
|
|
|
|
} else {
|
|
|
|
iter(args, subjects, started_at)
|
2018-08-03 23:26:22 +02:00
|
|
|
}
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
|
|
|
|
2018-08-03 23:26:22 +02:00
|
|
|
/// The top-level entry point for multi-threaded search. The parallelism is
|
|
|
|
/// itself achieved by the recursive directory traversal. All we need to do is
|
|
|
|
/// feed it a worker for performing a search on each file.
|
2022-11-28 06:36:15 +02:00
|
|
|
///
|
|
|
|
/// Requesting a sorted output from ripgrep (such as with `--sort path`) will
|
|
|
|
/// automatically disable parallelism and hence sorting is not handled here.
|
2023-10-12 18:16:42 +02:00
|
|
|
fn search_parallel(args: &Args) -> anyhow::Result<bool> {
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
|
2016-11-06 03:44:15 +02:00
|
|
|
|
2018-08-03 23:26:22 +02:00
|
|
|
let quit_after_match = args.quit_after_match()?;
|
|
|
|
let started_at = Instant::now();
|
2019-11-21 16:03:27 +02:00
|
|
|
let subject_builder = args.subject_builder();
|
|
|
|
let bufwtr = args.buffer_writer()?;
|
2023-09-28 22:24:40 +02:00
|
|
|
let stats = args.stats()?.map(std::sync::Mutex::new);
|
2019-11-21 16:03:27 +02:00
|
|
|
let matched = AtomicBool::new(false);
|
cli: print warning if nothing was searched
This was once part of ripgrep, but at some point, was unintentionally
removed. The value of this warning is that since ripgrep tries to be
"smart" by default, it can be surprising if it doesn't search certain
things. This warning covers the case when ripgrep searches *nothing*,
which happens somewhat more frequently than you might expect. e.g., If
you're searching within an ignore directory.
Note that for now, we only print this message when the user has not
supplied any explicit paths. It's not clear that we want to print this
otherwise, and in particular, it seems that the message shows up too
eagerly. e.g., 'rg foo does-not-exist' will both print an error about
'does-not-exist' not existing, *and* the message about no files being
searched, which seems annoying in this case. We can always refine this
logic later.
Fixes #1404, Closes #1762
2020-12-15 09:59:55 +02:00
|
|
|
let searched = AtomicBool::new(false);
|
2018-08-03 23:26:22 +02:00
|
|
|
let mut searcher_err = None;
|
|
|
|
args.walker_parallel()?.run(|| {
|
2019-11-21 16:03:27 +02:00
|
|
|
let bufwtr = &bufwtr;
|
|
|
|
let stats = &stats;
|
|
|
|
let matched = &matched;
|
cli: print warning if nothing was searched
This was once part of ripgrep, but at some point, was unintentionally
removed. The value of this warning is that since ripgrep tries to be
"smart" by default, it can be surprising if it doesn't search certain
things. This warning covers the case when ripgrep searches *nothing*,
which happens somewhat more frequently than you might expect. e.g., If
you're searching within an ignore directory.
Note that for now, we only print this message when the user has not
supplied any explicit paths. It's not clear that we want to print this
otherwise, and in particular, it seems that the message shows up too
eagerly. e.g., 'rg foo does-not-exist' will both print an error about
'does-not-exist' not existing, *and* the message about no files being
searched, which seems annoying in this case. We can always refine this
logic later.
Fixes #1404, Closes #1762
2020-12-15 09:59:55 +02:00
|
|
|
let searched = &searched;
|
2019-11-21 16:03:27 +02:00
|
|
|
let subject_builder = &subject_builder;
|
2018-08-03 23:26:22 +02:00
|
|
|
let mut searcher = match args.search_worker(bufwtr.buffer()) {
|
|
|
|
Ok(searcher) => searcher,
|
|
|
|
Err(err) => {
|
|
|
|
searcher_err = Some(err);
|
2019-11-21 16:03:27 +02:00
|
|
|
return Box::new(move |_| WalkState::Quit);
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Box::new(move |result| {
|
|
|
|
let subject = match subject_builder.build_from_result(result) {
|
|
|
|
Some(subject) => subject,
|
|
|
|
None => return WalkState::Continue,
|
2016-08-29 02:18:34 +02:00
|
|
|
};
|
cli: print warning if nothing was searched
This was once part of ripgrep, but at some point, was unintentionally
removed. The value of this warning is that since ripgrep tries to be
"smart" by default, it can be surprising if it doesn't search certain
things. This warning covers the case when ripgrep searches *nothing*,
which happens somewhat more frequently than you might expect. e.g., If
you're searching within an ignore directory.
Note that for now, we only print this message when the user has not
supplied any explicit paths. It's not clear that we want to print this
otherwise, and in particular, it seems that the message shows up too
eagerly. e.g., 'rg foo does-not-exist' will both print an error about
'does-not-exist' not existing, *and* the message about no files being
searched, which seems annoying in this case. We can always refine this
logic later.
Fixes #1404, Closes #1762
2020-12-15 09:59:55 +02:00
|
|
|
searched.store(true, SeqCst);
|
2018-08-03 23:26:22 +02:00
|
|
|
searcher.printer().get_mut().clear();
|
|
|
|
let search_result = match searcher.search(&subject) {
|
|
|
|
Ok(search_result) => search_result,
|
|
|
|
Err(err) => {
|
2019-01-26 22:42:55 +02:00
|
|
|
err_message!("{}: {}", subject.path().display(), err);
|
2018-08-03 23:26:22 +02:00
|
|
|
return WalkState::Continue;
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
};
|
|
|
|
if search_result.has_match() {
|
|
|
|
matched.store(true, SeqCst);
|
|
|
|
}
|
|
|
|
if let Some(ref locked_stats) = *stats {
|
|
|
|
let mut stats = locked_stats.lock().unwrap();
|
|
|
|
*stats += search_result.stats().unwrap();
|
|
|
|
}
|
|
|
|
if let Err(err) = bufwtr.print(searcher.printer().get_mut()) {
|
|
|
|
// A broken pipe means graceful termination.
|
|
|
|
if err.kind() == io::ErrorKind::BrokenPipe {
|
|
|
|
return WalkState::Quit;
|
2018-02-12 19:17:22 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
// Otherwise, we continue on our merry way.
|
2019-01-26 22:42:55 +02:00
|
|
|
err_message!("{}: {}", subject.path().display(), err);
|
2018-08-03 23:26:22 +02:00
|
|
|
}
|
|
|
|
if matched.load(SeqCst) && quit_after_match {
|
|
|
|
WalkState::Quit
|
|
|
|
} else {
|
|
|
|
WalkState::Continue
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
2018-08-03 23:26:22 +02:00
|
|
|
if let Some(err) = searcher_err.take() {
|
|
|
|
return Err(err);
|
2016-08-29 02:18:34 +02:00
|
|
|
}
|
cli: print warning if nothing was searched
This was once part of ripgrep, but at some point, was unintentionally
removed. The value of this warning is that since ripgrep tries to be
"smart" by default, it can be surprising if it doesn't search certain
things. This warning covers the case when ripgrep searches *nothing*,
which happens somewhat more frequently than you might expect. e.g., If
you're searching within an ignore directory.
Note that for now, we only print this message when the user has not
supplied any explicit paths. It's not clear that we want to print this
otherwise, and in particular, it seems that the message shows up too
eagerly. e.g., 'rg foo does-not-exist' will both print an error about
'does-not-exist' not existing, *and* the message about no files being
searched, which seems annoying in this case. We can always refine this
logic later.
Fixes #1404, Closes #1762
2020-12-15 09:59:55 +02:00
|
|
|
if args.using_default_path() && !searched.load(SeqCst) {
|
|
|
|
eprint_nothing_searched();
|
|
|
|
}
|
2019-11-21 16:03:27 +02:00
|
|
|
if let Some(ref locked_stats) = stats {
|
2018-08-03 23:26:22 +02:00
|
|
|
let elapsed = Instant::now().duration_since(started_at);
|
|
|
|
let stats = locked_stats.lock().unwrap();
|
|
|
|
let mut searcher = args.search_worker(args.stdout())?;
|
|
|
|
// We don't care if we couldn't print this successfully.
|
|
|
|
let _ = searcher.print_stats(elapsed, &stats);
|
2018-02-12 19:17:22 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
Ok(matched.load(SeqCst))
|
2016-08-29 02:18:34 +02:00
|
|
|
}
|
2016-08-26 03:44:37 +02:00
|
|
|
|
cli: print warning if nothing was searched
This was once part of ripgrep, but at some point, was unintentionally
removed. The value of this warning is that since ripgrep tries to be
"smart" by default, it can be surprising if it doesn't search certain
things. This warning covers the case when ripgrep searches *nothing*,
which happens somewhat more frequently than you might expect. e.g., If
you're searching within an ignore directory.
Note that for now, we only print this message when the user has not
supplied any explicit paths. It's not clear that we want to print this
otherwise, and in particular, it seems that the message shows up too
eagerly. e.g., 'rg foo does-not-exist' will both print an error about
'does-not-exist' not existing, *and* the message about no files being
searched, which seems annoying in this case. We can always refine this
logic later.
Fixes #1404, Closes #1762
2020-12-15 09:59:55 +02:00
|
|
|
fn eprint_nothing_searched() {
|
|
|
|
err_message!(
|
|
|
|
"No files were searched, which means ripgrep probably \
|
|
|
|
applied a filter you didn't expect.\n\
|
|
|
|
Running with --debug will show why files are being skipped."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-08-03 23:26:22 +02:00
|
|
|
/// The top-level entry point for listing files without searching them. This
|
|
|
|
/// recursively steps through the file list (current directory by default) and
|
|
|
|
/// prints each path sequentially using a single thread.
|
2023-10-12 18:16:42 +02:00
|
|
|
fn files(args: &Args) -> anyhow::Result<bool> {
|
2022-11-28 06:36:15 +02:00
|
|
|
/// The meat of the routine is here. This lets us call the same iteration
|
|
|
|
/// code over each file regardless of whether we stream over the files
|
|
|
|
/// as they're produced by the underlying directory traversal or whether
|
|
|
|
/// they've been collected and sorted (for example) first.
|
|
|
|
fn iter(
|
|
|
|
args: &Args,
|
|
|
|
subjects: impl Iterator<Item = Subject>,
|
2023-10-12 18:16:42 +02:00
|
|
|
) -> anyhow::Result<bool> {
|
2022-11-28 06:36:15 +02:00
|
|
|
let quit_after_match = args.quit_after_match()?;
|
|
|
|
let mut matched = false;
|
|
|
|
let mut path_printer = args.path_printer(args.stdout())?;
|
|
|
|
|
|
|
|
for subject in subjects {
|
|
|
|
matched = true;
|
|
|
|
if quit_after_match {
|
2016-10-12 01:57:09 +02:00
|
|
|
break;
|
2016-09-26 03:27:17 +02:00
|
|
|
}
|
2023-09-21 23:28:58 +02:00
|
|
|
if let Err(err) = path_printer.write(subject.path()) {
|
2022-11-28 06:36:15 +02:00
|
|
|
// A broken pipe means graceful termination.
|
|
|
|
if err.kind() == io::ErrorKind::BrokenPipe {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Otherwise, we have some other error that's preventing us from
|
|
|
|
// writing to stdout, so we should bubble it up.
|
|
|
|
return Err(err.into());
|
|
|
|
}
|
2016-11-06 21:36:08 +02:00
|
|
|
}
|
2022-11-28 06:36:15 +02:00
|
|
|
Ok(matched)
|
|
|
|
}
|
|
|
|
|
|
|
|
let subject_builder = args.subject_builder();
|
|
|
|
let subjects = args
|
|
|
|
.walker()?
|
|
|
|
.filter_map(|result| subject_builder.build_from_result(result));
|
|
|
|
if args.needs_stat_sort() {
|
|
|
|
let subjects = args.sort_by_stat(subjects).into_iter();
|
|
|
|
iter(args, subjects)
|
|
|
|
} else {
|
|
|
|
iter(args, subjects)
|
2016-09-26 03:27:17 +02:00
|
|
|
}
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
|
|
|
|
2018-08-03 23:26:22 +02:00
|
|
|
/// The top-level entry point for listing files without searching them. This
|
|
|
|
/// recursively steps through the file list (current directory by default) and
|
|
|
|
/// prints each path sequentially using multiple threads.
|
2022-11-28 06:36:15 +02:00
|
|
|
///
|
|
|
|
/// Requesting a sorted output from ripgrep (such as with `--sort path`) will
|
|
|
|
/// automatically disable parallelism and hence sorting is not handled here.
|
2023-10-12 18:16:42 +02:00
|
|
|
fn files_parallel(args: &Args) -> anyhow::Result<bool> {
|
2018-08-03 23:26:22 +02:00
|
|
|
use std::sync::atomic::AtomicBool;
|
|
|
|
use std::sync::atomic::Ordering::SeqCst;
|
|
|
|
use std::sync::mpsc;
|
|
|
|
use std::thread;
|
|
|
|
|
|
|
|
let quit_after_match = args.quit_after_match()?;
|
2019-11-21 16:03:27 +02:00
|
|
|
let subject_builder = args.subject_builder();
|
2018-08-03 23:26:22 +02:00
|
|
|
let mut path_printer = args.path_printer(args.stdout())?;
|
2019-11-21 16:03:27 +02:00
|
|
|
let matched = AtomicBool::new(false);
|
2018-08-03 23:26:22 +02:00
|
|
|
let (tx, rx) = mpsc::channel::<Subject>();
|
|
|
|
|
|
|
|
let print_thread = thread::spawn(move || -> io::Result<()> {
|
|
|
|
for subject in rx.iter() {
|
2023-09-21 23:28:58 +02:00
|
|
|
path_printer.write(subject.path())?;
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
Ok(())
|
2016-11-06 03:44:15 +02:00
|
|
|
});
|
2018-08-03 23:26:22 +02:00
|
|
|
args.walker_parallel()?.run(|| {
|
2019-11-21 16:03:27 +02:00
|
|
|
let subject_builder = &subject_builder;
|
|
|
|
let matched = &matched;
|
2016-11-06 03:44:15 +02:00
|
|
|
let tx = tx.clone();
|
2018-08-03 23:26:22 +02:00
|
|
|
|
2016-11-06 03:44:15 +02:00
|
|
|
Box::new(move |result| {
|
2018-08-03 23:26:22 +02:00
|
|
|
let subject = match subject_builder.build_from_result(result) {
|
|
|
|
Some(subject) => subject,
|
|
|
|
None => return WalkState::Continue,
|
|
|
|
};
|
|
|
|
matched.store(true, SeqCst);
|
|
|
|
if quit_after_match {
|
|
|
|
WalkState::Quit
|
|
|
|
} else {
|
|
|
|
match tx.send(subject) {
|
|
|
|
Ok(_) => WalkState::Continue,
|
|
|
|
Err(_) => WalkState::Quit,
|
2018-07-22 17:05:24 +02:00
|
|
|
}
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
2018-08-03 23:26:22 +02:00
|
|
|
drop(tx);
|
|
|
|
if let Err(err) = print_thread.join().unwrap() {
|
|
|
|
// A broken pipe means graceful termination, so fall through.
|
|
|
|
// Otherwise, something bad happened while writing to stdout, so bubble
|
|
|
|
// it up.
|
|
|
|
if err.kind() != io::ErrorKind::BrokenPipe {
|
|
|
|
return Err(err.into());
|
2017-05-19 14:12:38 +02:00
|
|
|
}
|
2016-09-04 03:48:23 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
Ok(matched.load(SeqCst))
|
2018-02-02 04:11:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-03 23:26:22 +02:00
|
|
|
/// The top-level entry point for --type-list.
|
2023-10-12 18:16:42 +02:00
|
|
|
fn types(args: &Args) -> anyhow::Result<bool> {
|
2018-08-03 23:26:22 +02:00
|
|
|
let mut count = 0;
|
|
|
|
let mut stdout = args.stdout();
|
|
|
|
for def in args.type_defs()? {
|
|
|
|
count += 1;
|
|
|
|
stdout.write_all(def.name().as_bytes())?;
|
|
|
|
stdout.write_all(b": ")?;
|
2018-02-02 04:11:02 +02:00
|
|
|
|
2018-08-03 23:26:22 +02:00
|
|
|
let mut first = true;
|
|
|
|
for glob in def.globs() {
|
|
|
|
if !first {
|
|
|
|
stdout.write_all(b", ")?;
|
2016-11-06 03:44:15 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
stdout.write_all(glob.as_bytes())?;
|
|
|
|
first = false;
|
2016-09-05 16:15:13 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
stdout.write_all(b"\n")?;
|
2016-09-05 06:52:23 +02:00
|
|
|
}
|
2018-08-03 23:26:22 +02:00
|
|
|
Ok(count > 0)
|
2017-08-27 20:32:20 +02:00
|
|
|
}
|
2019-04-14 22:46:02 +02:00
|
|
|
|
|
|
|
/// The top-level entry point for --pcre2-version.
|
2023-10-12 18:16:42 +02:00
|
|
|
fn pcre2_version(args: &Args) -> anyhow::Result<bool> {
|
2019-04-14 22:46:02 +02:00
|
|
|
#[cfg(feature = "pcre2")]
|
2023-10-12 18:16:42 +02:00
|
|
|
fn imp(args: &Args) -> anyhow::Result<bool> {
|
2019-04-14 22:46:02 +02:00
|
|
|
use grep::pcre2;
|
|
|
|
|
|
|
|
let mut stdout = args.stdout();
|
|
|
|
|
|
|
|
let (major, minor) = pcre2::version();
|
|
|
|
writeln!(stdout, "PCRE2 {}.{} is available", major, minor)?;
|
|
|
|
|
|
|
|
if cfg!(target_pointer_width = "64") && pcre2::is_jit_available() {
|
|
|
|
writeln!(stdout, "JIT is available")?;
|
|
|
|
}
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "pcre2"))]
|
2023-10-12 18:16:42 +02:00
|
|
|
fn imp(args: &Args) -> anyhow::Result<bool> {
|
2019-04-14 22:46:02 +02:00
|
|
|
let mut stdout = args.stdout();
|
|
|
|
writeln!(stdout, "PCRE2 is not available in this build of ripgrep.")?;
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
imp(args)
|
|
|
|
}
|