mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-01-08 13:23:34 +02:00
c8e4a84519
Fixes #2694
140 lines
4.9 KiB
Rust
140 lines
4.9 KiB
Rust
/*!
|
|
This module defines some macros and some light shared mutable state.
|
|
|
|
This state is responsible for keeping track of whether we should emit certain
|
|
kinds of messages to the user (such as errors) that are distinct from the
|
|
standard "debug" or "trace" log messages. This state is specifically set at
|
|
startup time when CLI arguments are parsed and then never changed.
|
|
|
|
The other state tracked here is whether ripgrep experienced an error
|
|
condition. Aside from errors associated with invalid CLI arguments, ripgrep
|
|
generally does not abort when an error occurs (e.g., if reading a file failed).
|
|
But when an error does occur, it will alter ripgrep's exit status. Thus, when
|
|
an error message is emitted via `err_message`, then a global flag is toggled
|
|
indicating that at least one error occurred. When ripgrep exits, this flag is
|
|
consulted to determine what the exit status ought to be.
|
|
*/
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
/// When false, "messages" will not be printed.
|
|
static MESSAGES: AtomicBool = AtomicBool::new(false);
|
|
/// When false, "messages" related to ignore rules will not be printed.
|
|
static IGNORE_MESSAGES: AtomicBool = AtomicBool::new(false);
|
|
/// Flipped to true when an error message is printed.
|
|
static ERRORED: AtomicBool = AtomicBool::new(false);
|
|
|
|
/// Like eprintln, but locks stdout to prevent interleaving lines.
|
|
///
|
|
/// This locks stdout, not stderr, even though this prints to stderr. This
|
|
/// avoids the appearance of interleaving output when stdout and stderr both
|
|
/// correspond to a tty.)
|
|
#[macro_export]
|
|
macro_rules! eprintln_locked {
|
|
($($tt:tt)*) => {{
|
|
{
|
|
use std::io::Write;
|
|
|
|
// This is a bit of an abstraction violation because we explicitly
|
|
// lock stdout before printing to stderr. This avoids interleaving
|
|
// lines within ripgrep because `search_parallel` uses `termcolor`,
|
|
// which accesses the same stdout lock when writing lines.
|
|
let stdout = std::io::stdout().lock();
|
|
let mut stderr = std::io::stderr().lock();
|
|
// We specifically ignore any errors here. One plausible error we
|
|
// can get in some cases is a broken pipe error. And when that
|
|
// occurs, we should exit gracefully. Otherwise, just abort with
|
|
// an error code because there isn't much else we can do.
|
|
//
|
|
// See: https://github.com/BurntSushi/ripgrep/issues/1966
|
|
if let Err(err) = write!(stderr, "rg: ") {
|
|
if err.kind() == std::io::ErrorKind::BrokenPipe {
|
|
std::process::exit(0);
|
|
} else {
|
|
std::process::exit(2);
|
|
}
|
|
}
|
|
if let Err(err) = writeln!(stderr, $($tt)*) {
|
|
if err.kind() == std::io::ErrorKind::BrokenPipe {
|
|
std::process::exit(0);
|
|
} else {
|
|
std::process::exit(2);
|
|
}
|
|
}
|
|
drop(stdout);
|
|
}
|
|
}}
|
|
}
|
|
|
|
/// Emit a non-fatal error message, unless messages were disabled.
|
|
#[macro_export]
|
|
macro_rules! message {
|
|
($($tt:tt)*) => {
|
|
if crate::messages::messages() {
|
|
eprintln_locked!($($tt)*);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Like message, but sets ripgrep's "errored" flag, which controls the exit
|
|
/// status.
|
|
#[macro_export]
|
|
macro_rules! err_message {
|
|
($($tt:tt)*) => {
|
|
crate::messages::set_errored();
|
|
message!($($tt)*);
|
|
}
|
|
}
|
|
|
|
/// Emit a non-fatal ignore-related error message (like a parse error), unless
|
|
/// ignore-messages were disabled.
|
|
#[macro_export]
|
|
macro_rules! ignore_message {
|
|
($($tt:tt)*) => {
|
|
if crate::messages::messages() && crate::messages::ignore_messages() {
|
|
eprintln_locked!($($tt)*);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns true if and only if messages should be shown.
|
|
pub(crate) fn messages() -> bool {
|
|
MESSAGES.load(Ordering::SeqCst)
|
|
}
|
|
|
|
/// Set whether messages should be shown or not.
|
|
///
|
|
/// By default, they are not shown.
|
|
pub(crate) fn set_messages(yes: bool) {
|
|
MESSAGES.store(yes, Ordering::SeqCst)
|
|
}
|
|
|
|
/// Returns true if and only if "ignore" related messages should be shown.
|
|
pub(crate) fn ignore_messages() -> bool {
|
|
IGNORE_MESSAGES.load(Ordering::SeqCst)
|
|
}
|
|
|
|
/// Set whether "ignore" related messages should be shown or not.
|
|
///
|
|
/// By default, they are not shown.
|
|
///
|
|
/// Note that this is overridden if `messages` is disabled. Namely, if
|
|
/// `messages` is disabled, then "ignore" messages are never shown, regardless
|
|
/// of this setting.
|
|
pub(crate) fn set_ignore_messages(yes: bool) {
|
|
IGNORE_MESSAGES.store(yes, Ordering::SeqCst)
|
|
}
|
|
|
|
/// Returns true if and only if ripgrep came across a non-fatal error.
|
|
pub(crate) fn errored() -> bool {
|
|
ERRORED.load(Ordering::SeqCst)
|
|
}
|
|
|
|
/// Indicate that ripgrep has come across a non-fatal error.
|
|
///
|
|
/// Callers should not use this directly. Instead, it is called automatically
|
|
/// via the `err_message` macro.
|
|
pub(crate) fn set_errored() {
|
|
ERRORED.store(true, Ordering::SeqCst);
|
|
}
|