diff --git a/crates/nu-command/src/env/config/config_.rs b/crates/nu-command/src/env/config/config_.rs index 14c490145c..1c238cdb03 100644 --- a/crates/nu-command/src/env/config/config_.rs +++ b/crates/nu-command/src/env/config/config_.rs @@ -1,5 +1,6 @@ use nu_cmd_base::util::get_editor; use nu_engine::{command_prelude::*, env_to_strings, get_full_help}; +use nu_protocol::{process::PostWaitCallback, shell_error::io::IoError}; use nu_system::ForegroundChild; #[derive(Clone)] @@ -61,7 +62,6 @@ pub(super) fn start_editor( ) -> Result { // Find the editor executable. - use nu_protocol::shell_error::io::IoError; let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?; let paths = nu_engine::env::path_str(engine_state, stack, call.head)?; let cwd = engine_state.cwd(Some(stack))?; @@ -119,8 +119,17 @@ pub(super) fn start_editor( ) })?; + let post_wait_callback = PostWaitCallback::for_job_control(engine_state, None); + // Wrap the output into a `PipelineData::ByteStream`. - let child = nu_protocol::process::ChildProcess::new(child, None, false, call.head, None)?; + let child = nu_protocol::process::ChildProcess::new( + child, + None, + false, + call.head, + Some(post_wait_callback), + )?; + Ok(PipelineData::ByteStream( ByteStream::child(child, call.head), None, diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index 03e2d3de02..007bfb553b 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -3,12 +3,11 @@ use nu_engine::{command_prelude::*, env_to_strings}; use nu_path::{dots::expand_ndots_safe, expand_tilde, AbsolutePath}; use nu_protocol::{ did_you_mean, - engine::{FrozenJob, Job}, process::{ChildProcess, PostWaitCallback}, shell_error::io::IoError, ByteStream, NuGlob, OutDest, Signals, UseAnsiColoring, }; -use nu_system::{kill_by_pid, ForegroundChild, ForegroundWaitStatus}; +use nu_system::{kill_by_pid, ForegroundChild}; use nu_utils::IgnoreCaseExt; use pathdiff::diff_paths; #[cfg(windows)] @@ -312,9 +311,6 @@ impl Command for External { })?; } - let jobs = engine_state.jobs.clone(); - let this_job = engine_state.current_thread_job.clone(); - let is_interactive = engine_state.is_interactive; let child_pid = child.pid(); // Wrap the output into a `PipelineData::ByteStream`. @@ -323,21 +319,10 @@ impl Command for External { merged_stream, matches!(stderr, OutDest::Pipe), call.head, - // handle wait statuses for job control - Some(PostWaitCallback(Box::new(move |status| { - if let Some(this_job) = this_job { - this_job.remove_pid(child_pid); - } - - if let ForegroundWaitStatus::Frozen(unfreeze) = status { - let mut jobs = jobs.lock().expect("jobs lock is poisoned!"); - - let job_id = jobs.add_job(Job::Frozen(FrozenJob { unfreeze })); - if is_interactive { - println!("\nJob {} is frozen", job_id.get()); - } - } - }))), + Some(PostWaitCallback::for_job_control( + engine_state, + Some(child_pid), + )), )?; if matches!(stdout, OutDest::Pipe | OutDest::PipeSeparate) diff --git a/crates/nu-protocol/src/process/child.rs b/crates/nu-protocol/src/process/child.rs index d2f1b6c617..7903c03e03 100644 --- a/crates/nu-protocol/src/process/child.rs +++ b/crates/nu-protocol/src/process/child.rs @@ -1,4 +1,9 @@ -use crate::{byte_stream::convert_file, shell_error::io::IoError, ShellError, Span}; +use crate::{ + byte_stream::convert_file, + engine::{EngineState, FrozenJob, Job}, + shell_error::io::IoError, + ShellError, Span, +}; use nu_system::{ExitStatus, ForegroundChild, ForegroundWaitStatus}; use os_pipe::PipeReader; @@ -166,8 +171,45 @@ pub struct ChildProcess { span: Span, } +/// A wrapper for a closure that runs once the shell finishes waiting on the process. pub struct PostWaitCallback(pub Box); +impl PostWaitCallback { + pub fn new(f: F) -> Self + where + F: FnOnce(ForegroundWaitStatus) + Send + 'static, + { + PostWaitCallback(Box::new(f)) + } + + /// Creates a PostWaitCallback that checks creates a frozen job in the job table + /// if the incoming wait status indicates that the job was frozen. + /// + /// If `child_pid` is provided, the returned callback will also remove + /// it from the pid list of the current running job. + pub fn for_job_control(engine_state: &EngineState, child_pid: Option) -> Self { + let this_job = engine_state.current_thread_job.clone(); + let jobs = engine_state.jobs.clone(); + let is_interactive = engine_state.is_interactive; + + PostWaitCallback::new(move |status| { + if let (Some(this_job), Some(child_pid)) = (this_job, child_pid) { + this_job.remove_pid(child_pid); + } + + if let ForegroundWaitStatus::Frozen(unfreeze) = status { + let mut jobs = jobs.lock().expect("jobs lock is poisoned!"); + + let job_id = jobs.add_job(Job::Frozen(FrozenJob { unfreeze })); + + if is_interactive { + println!("\nJob {} is frozen", job_id.get()); + } + } + }) + } +} + impl Debug for PostWaitCallback { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "")