From bffa9d3278b6a7f801363e504a37a43393d8c627 Mon Sep 17 00:00:00 2001 From: cosineblast <55855728+cosineblast@users.noreply.github.com> Date: Wed, 5 Mar 2025 00:42:31 -0300 Subject: [PATCH] Implement `job id` command This cmomit also modifies run_external and job_unfreeze because the engine_state stores the id of the current job in the current_thread_job field, so its usages are accessed differently now. --- crates/nu-command/src/default_context.rs | 1 + crates/nu-command/src/experimental/job_id.rs | 54 +++++++++++++++++++ .../nu-command/src/experimental/job_spawn.rs | 7 ++- .../src/experimental/job_unfreeze.rs | 4 +- crates/nu-command/src/experimental/mod.rs | 3 +- crates/nu-command/src/system/run_external.rs | 4 +- crates/nu-protocol/src/engine/engine_state.rs | 17 +++--- 7 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 crates/nu-command/src/experimental/job_id.rs diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 0c1b38182a..5cd326a222 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -451,6 +451,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { JobSpawn, JobList, JobKill, + JobId, Job, }; diff --git a/crates/nu-command/src/experimental/job_id.rs b/crates/nu-command/src/experimental/job_id.rs new file mode 100644 index 0000000000..93bdbdd1ae --- /dev/null +++ b/crates/nu-command/src/experimental/job_id.rs @@ -0,0 +1,54 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct JobId; + +impl Command for JobId { + fn name(&self) -> &str { + "job id" + } + + fn description(&self) -> &str { + "Get id of current job." + } + + fn extra_description(&self) -> &str { + "This command returns the job id for the current background job. +The special id 0 indicates that this command was not called from a background job thread, and +was instead spawned by main nushell execution thread." + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("job id") + .category(Category::Experimental) + .input_output_types(vec![(Type::Nothing, Type::Int)]) + } + + fn search_terms(&self) -> Vec<&str> { + vec!["self", "this", "my-id", "this-id"] + } + + fn run( + &self, + engine_state: &EngineState, + _stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let head = call.head; + + if let Some((id, _)) = &engine_state.thread_job_entry { + Ok(Value::int(id.get() as i64, head).into_pipeline_data()) + } else { + Ok(Value::int(0, head).into_pipeline_data()) + } + } + + fn examples(&self) -> Vec { + vec![Example { + example: "job id", + description: "Get id of current job", + result: None, + }] + } +} diff --git a/crates/nu-command/src/experimental/job_spawn.rs b/crates/nu-command/src/experimental/job_spawn.rs index 60f30024be..44d03b7a6e 100644 --- a/crates/nu-command/src/experimental/job_spawn.rs +++ b/crates/nu-command/src/experimental/job_spawn.rs @@ -69,8 +69,11 @@ impl Command for JobSpawn { let id = { let thread_job = ThreadJob::new(job_signals); - job_state.current_thread_job = Some(thread_job.clone()); - jobs.add_job(Job::Thread(thread_job)) + let id = jobs.add_job(Job::Thread(thread_job.clone())); + + job_state.thread_job_entry = Some((id, thread_job)); + + id }; let result = thread::Builder::new() diff --git a/crates/nu-command/src/experimental/job_unfreeze.rs b/crates/nu-command/src/experimental/job_unfreeze.rs index 46ee5b46f5..05078b2399 100644 --- a/crates/nu-command/src/experimental/job_unfreeze.rs +++ b/crates/nu-command/src/experimental/job_unfreeze.rs @@ -115,7 +115,7 @@ fn unfreeze_job( Job::Frozen(FrozenJob { unfreeze: handle }) => { let pid = handle.pid(); - if let Some(thread_job) = &state.current_thread_job { + if let Some(thread_job) = &state.current_thread_job() { if !thread_job.try_add_pid(pid) { kill_by_pid(pid.into()).map_err(|err| { ShellError::Io(IoError::new_internal( @@ -133,7 +133,7 @@ fn unfreeze_job( .then(|| state.pipeline_externals_state.clone()), ); - if let Some(thread_job) = &state.current_thread_job { + if let Some(thread_job) = &state.current_thread_job() { thread_job.remove_pid(pid); } diff --git a/crates/nu-command/src/experimental/mod.rs b/crates/nu-command/src/experimental/mod.rs index ff4f6b0399..a892ac5550 100644 --- a/crates/nu-command/src/experimental/mod.rs +++ b/crates/nu-command/src/experimental/mod.rs @@ -1,5 +1,6 @@ mod is_admin; mod job; +mod job_id; mod job_kill; mod job_list; mod job_spawn; @@ -9,9 +10,9 @@ mod job_unfreeze; pub use is_admin::IsAdmin; pub use job::Job; +pub use job_id::JobId; pub use job_kill::JobKill; pub use job_list::JobList; - pub use job_spawn::JobSpawn; #[cfg(all(unix, feature = "os"))] diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index 38b9fb165e..392bfada18 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -279,7 +279,7 @@ impl Command for External { ) })?; - if let Some(thread_job) = &engine_state.current_thread_job { + if let Some(thread_job) = engine_state.current_thread_job() { if !thread_job.try_add_pid(child.pid()) { kill_by_pid(child.pid().into()).map_err(|err| { ShellError::Io(IoError::new_internal( @@ -312,8 +312,8 @@ 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 this_job = engine_state.current_thread_job().cloned(); let child_pid = child.pid(); // Wrap the output into a `PipelineData::ByteStream`. diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 76dd15fc47..dd3faeff70 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -8,9 +8,9 @@ use crate::{ }, eval_const::create_nu_constant, shell_error::io::IoError, - BlockId, Category, Config, DeclId, FileId, GetSpan, Handlers, HistoryConfig, Module, ModuleId, - OverlayId, ShellError, SignalAction, Signals, Signature, Span, SpanId, Type, Value, VarId, - VirtualPathId, + BlockId, Category, Config, DeclId, FileId, GetSpan, Handlers, HistoryConfig, JobId, Module, + ModuleId, OverlayId, ShellError, SignalAction, Signals, Signature, Span, SpanId, Type, Value, + VarId, VirtualPathId, }; use fancy_regex::Regex; use lru::LruCache; @@ -117,7 +117,7 @@ pub struct EngineState { pub jobs: Arc>, // The job being executed with this engine state, or None if main thread - pub current_thread_job: Option, + pub thread_job_entry: Option<(JobId, ThreadJob)>, // When there are background jobs running, the interactive behavior of `exit` changes depending on // the value of this flag: @@ -196,7 +196,7 @@ impl EngineState { is_debugging: IsDebugging::new(false), debugger: Arc::new(Mutex::new(Box::new(NoopDebugger))), jobs: Arc::new(Mutex::new(Jobs::default())), - current_thread_job: None, + thread_job_entry: None, exit_warning_given: Arc::new(AtomicBool::new(false)), } } @@ -1080,7 +1080,12 @@ impl EngineState { // Determines whether the current state is being held by a background job pub fn is_background_job(&self) -> bool { - self.current_thread_job.is_some() + self.thread_job_entry.is_some() + } + + // Gets the thread job entry + pub fn current_thread_job(&self) -> Option<&ThreadJob> { + self.thread_job_entry.as_ref().map(|(_, job)| job) } }