Add and use new Signals struct (#13314)

# Description
This PR introduces a new `Signals` struct to replace our adhoc passing
around of `ctrlc: Option<Arc<AtomicBool>>`. Doing so has a few benefits:
- We can better enforce when/where resetting or triggering an interrupt
is allowed.
- Consolidates `nu_utils::ctrl_c::was_pressed` and other ad-hoc
re-implementations into a single place: `Signals::check`.
- This allows us to add other types of signals later if we want. E.g.,
exiting or suspension.
- Similarly, we can more easily change the underlying implementation if
we need to in the future.
- Places that used to have a `ctrlc` of `None` now use
`Signals::empty()`, so we can double check these usages for correctness
in the future.
This commit is contained in:
Ian Manske
2024-07-07 22:29:01 +00:00
committed by GitHub
parent c6b6b1b7a8
commit 399a7c8836
246 changed files with 1332 additions and 1234 deletions

View File

@ -182,5 +182,5 @@ fn run_ps(
Ok(output
.into_iter()
.into_pipeline_data(span, engine_state.ctrlc.clone()))
.into_pipeline_data(span, engine_state.signals().clone()))
}

View File

@ -106,7 +106,7 @@ fn registry_query(
*registry_key_span,
))
}
Ok(reg_values.into_pipeline_data(call_span, engine_state.ctrlc.clone()))
Ok(reg_values.into_pipeline_data(call_span, engine_state.signals().clone()))
} else {
match registry_value {
Some(value) => {

View File

@ -2,7 +2,7 @@ use nu_cmd_base::hook::eval_hook;
use nu_engine::{command_prelude::*, env_to_strings, get_eval_expression};
use nu_path::{dots::expand_ndots, expand_tilde};
use nu_protocol::{
ast::Expression, did_you_mean, process::ChildProcess, ByteStream, NuGlob, OutDest,
ast::Expression, did_you_mean, process::ChildProcess, ByteStream, NuGlob, OutDest, Signals,
};
use nu_system::ForegroundChild;
use nu_utils::IgnoreCaseExt;
@ -13,7 +13,7 @@ use std::{
io::Write,
path::{Path, PathBuf},
process::Stdio,
sync::{atomic::AtomicBool, Arc},
sync::Arc,
thread,
};
@ -221,7 +221,6 @@ pub fn eval_arguments_from_call(
stack: &mut Stack,
call: &Call,
) -> Result<Vec<Spanned<OsString>>, ShellError> {
let ctrlc = &engine_state.ctrlc;
let cwd = engine_state.cwd(Some(stack))?;
let mut args: Vec<Spanned<OsString>> = vec![];
for (expr, spread) in call.rest_iter(1) {
@ -229,7 +228,7 @@ pub fn eval_arguments_from_call(
match arg {
// Expand globs passed to run-external
Value::Glob { val, no_expand, .. } if !no_expand => args.extend(
expand_glob(&val, &cwd, expr.span, ctrlc)?
expand_glob(&val, &cwd, expr.span, engine_state.signals())?
.into_iter()
.map(|s| s.into_spanned(expr.span)),
),
@ -289,7 +288,7 @@ fn expand_glob(
arg: &str,
cwd: &Path,
span: Span,
interrupt: &Option<Arc<AtomicBool>>,
signals: &Signals,
) -> Result<Vec<OsString>, ShellError> {
const GLOB_CHARS: &[char] = &['*', '?', '['];
@ -307,9 +306,7 @@ fn expand_glob(
let mut result: Vec<OsString> = vec![];
for m in matches {
if nu_utils::ctrl_c::was_pressed(interrupt) {
return Err(ShellError::InterruptedByUser { span: Some(span) });
}
signals.check(span)?;
if let Ok(arg) = m {
let arg = resolve_globbed_path_to_cwd_relative(arg, prefix.as_ref(), cwd);
result.push(arg.into());
@ -611,30 +608,30 @@ mod test {
let cwd = dirs.test();
let actual = expand_glob("*.txt", cwd, Span::unknown(), &None).unwrap();
let actual = expand_glob("*.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
let expected = &["a.txt", "b.txt"];
assert_eq!(actual, expected);
let actual = expand_glob("./*.txt", cwd, Span::unknown(), &None).unwrap();
let actual = expand_glob("./*.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
assert_eq!(actual, expected);
let actual = expand_glob("'*.txt'", cwd, Span::unknown(), &None).unwrap();
let actual = expand_glob("'*.txt'", cwd, Span::unknown(), &Signals::empty()).unwrap();
let expected = &["'*.txt'"];
assert_eq!(actual, expected);
let actual = expand_glob(".", cwd, Span::unknown(), &None).unwrap();
let actual = expand_glob(".", cwd, Span::unknown(), &Signals::empty()).unwrap();
let expected = &["."];
assert_eq!(actual, expected);
let actual = expand_glob("./a.txt", cwd, Span::unknown(), &None).unwrap();
let actual = expand_glob("./a.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
let expected = &["./a.txt"];
assert_eq!(actual, expected);
let actual = expand_glob("[*.txt", cwd, Span::unknown(), &None).unwrap();
let actual = expand_glob("[*.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
let expected = &["[*.txt"];
assert_eq!(actual, expected);
let actual = expand_glob("~/foo.txt", cwd, Span::unknown(), &None).unwrap();
let actual = expand_glob("~/foo.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
let home = dirs_next::home_dir().expect("failed to get home dir");
let expected: Vec<OsString> = vec![home.join("foo.txt").into()];
assert_eq!(actual, expected);
@ -666,7 +663,7 @@ mod test {
ByteStream::read(
b"foo".as_slice(),
Span::unknown(),
None,
Signals::empty(),
ByteStreamType::Unknown,
),
None,

View File

@ -188,7 +188,6 @@ fn which(
applications: call.rest(engine_state, stack, 0)?,
all: call.has_flag(engine_state, stack, "all")?,
};
let ctrlc = engine_state.ctrlc.clone();
if which_args.applications.is_empty() {
return Err(ShellError::MissingParameter {
@ -214,7 +213,9 @@ fn which(
output.extend(values);
}
Ok(output.into_iter().into_pipeline_data(head, ctrlc))
Ok(output
.into_iter()
.into_pipeline_data(head, engine_state.signals().clone()))
}
#[cfg(test)]