Wind 7d17c2eb5e
add a helpful msg to indicate a job has been frozen (#15206)
# Description
As stated in the title, when pressing ctrl-z, I sometimes feel confused
because I return to the REPL without any message. I don't know if the
process has been killed or suspended.

This PR aims to add a message to notify the user that the process has
been frozen.

# User-Facing Changes
After pressing `ctrl-z`.  A message will be printed in repl.


![图片](https://github.com/user-attachments/assets/5fe502eb-439e-4022-889f-64ba52cc2825)

# Tests + Formatting
NaN

# After Submitting
NaN
2025-03-06 11:20:58 -05:00

164 lines
5.0 KiB
Rust

use nu_engine::command_prelude::*;
use nu_protocol::{
engine::{FrozenJob, Job, ThreadJob},
process::check_ok,
shell_error, JobId,
};
use nu_system::{kill_by_pid, ForegroundWaitStatus};
#[derive(Clone)]
pub struct JobUnfreeze;
impl Command for JobUnfreeze {
fn name(&self) -> &str {
"job unfreeze"
}
fn description(&self) -> &str {
"Unfreeze a frozen process job in foreground."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("job unfreeze")
.category(Category::Experimental)
.optional("id", SyntaxShape::Int, "The process id to unfreeze.")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
}
fn search_terms(&self) -> Vec<&str> {
vec!["fg"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let option_id: Option<Spanned<i64>> = call.opt(engine_state, stack, 0)?;
let mut jobs = engine_state.jobs.lock().expect("jobs lock is poisoned!");
if let Some(id_arg) = option_id {
if id_arg.item < 0 {
return Err(ShellError::NeedsPositiveValue { span: id_arg.span });
}
}
let id = option_id
.map(|it| JobId::new(it.item as usize))
.or_else(|| jobs.most_recent_frozen_job_id())
.ok_or_else(|| ShellError::NoFrozenJob { span: head })?;
let job = match jobs.lookup(id) {
None => {
return Err(ShellError::JobNotFound {
id: id.get(),
span: head,
})
}
Some(Job::Thread(ThreadJob { .. })) => {
return Err(ShellError::JobNotFrozen {
id: id.get(),
span: head,
})
}
Some(Job::Frozen(FrozenJob { .. })) => jobs
.remove_job(id)
.expect("job was supposed to be in job list"),
};
drop(jobs);
unfreeze_job(engine_state, id, job, head)?;
Ok(Value::nothing(head).into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
example: "job unfreeze",
description: "Unfreeze the latest frozen job",
result: None,
},
Example {
example: "job unfreeze 4",
description: "Unfreeze a specific frozen job by its PID",
result: None,
},
]
}
fn extra_description(&self) -> &str {
r#"When a running process is frozen (with the SIGTSTP signal or with the Ctrl-Z key on unix),
a background job gets registered for this process, which can then be resumed using this command."#
}
}
fn unfreeze_job(
state: &EngineState,
old_id: JobId,
job: Job,
span: Span,
) -> Result<(), ShellError> {
match job {
Job::Thread(ThreadJob { .. }) => Err(ShellError::JobNotFrozen {
id: old_id.get(),
span,
}),
Job::Frozen(FrozenJob { unfreeze: handle }) => {
let pid = handle.pid();
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(
err.kind(),
"job was interrupted; could not kill foreground process",
nu_protocol::location!(),
))
})?;
}
}
let result = handle.unfreeze(
state
.is_interactive
.then(|| state.pipeline_externals_state.clone()),
);
if let Some(thread_job) = &state.current_thread_job {
thread_job.remove_pid(pid);
}
match result {
Ok(ForegroundWaitStatus::Frozen(handle)) => {
let mut jobs = state.jobs.lock().expect("jobs lock is poisoned!");
jobs.add_job_with_id(old_id, Job::Frozen(FrozenJob { unfreeze: handle }))
.expect("job was supposed to be removed");
if state.is_interactive {
println!("\nJob {} is re-frozen", old_id.get());
}
Ok(())
}
Ok(ForegroundWaitStatus::Finished(status)) => check_ok(status, false, span),
Err(err) => Err(ShellError::Io(IoError::new_internal(
shell_error::io::ErrorKind::Std(err.kind()),
"Failed to unfreeze foreground process",
nu_protocol::location!(),
))),
}
}
}
}