mirror of
https://github.com/nushell/nushell.git
synced 2025-04-15 16:58:19 +02:00
Merge 27baea4bbe
into 885b87a842
This commit is contained in:
commit
a1cec17723
@ -452,6 +452,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
JobSpawn,
|
||||
JobList,
|
||||
JobKill,
|
||||
JobTag,
|
||||
Job,
|
||||
};
|
||||
|
||||
|
@ -37,7 +37,7 @@ impl Command for JobList {
|
||||
let values = jobs
|
||||
.iter()
|
||||
.map(|(id, job)| {
|
||||
let record = record! {
|
||||
let mut record = record! {
|
||||
"id" => Value::int(id.get() as i64, head),
|
||||
"type" => match job {
|
||||
Job::Thread(_) => Value::string("thread", head),
|
||||
@ -52,12 +52,16 @@ impl Command for JobList {
|
||||
head,
|
||||
),
|
||||
|
||||
Job::Frozen(FrozenJob { unfreeze }) => {
|
||||
Job::Frozen(FrozenJob { unfreeze, .. }) => {
|
||||
Value::list(vec![ Value::int(unfreeze.pid() as i64, head) ], head)
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(tag) = job.tag() {
|
||||
record.push("tag", Value::string(tag, head));
|
||||
}
|
||||
|
||||
Value::record(record, head)
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
|
@ -28,6 +28,12 @@ impl Command for JobSpawn {
|
||||
Signature::build("job spawn")
|
||||
.category(Category::Experimental)
|
||||
.input_output_types(vec![(Type::Nothing, Type::Int)])
|
||||
.named(
|
||||
"tag",
|
||||
SyntaxShape::String,
|
||||
"An optional description tag for this job",
|
||||
Some('t'),
|
||||
)
|
||||
.required(
|
||||
"closure",
|
||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||
@ -50,6 +56,8 @@ impl Command for JobSpawn {
|
||||
|
||||
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||
|
||||
let tag: Option<String> = call.get_flag(engine_state, stack, "tag")?;
|
||||
|
||||
let mut job_state = engine_state.clone();
|
||||
job_state.is_interactive = false;
|
||||
|
||||
@ -68,7 +76,7 @@ impl Command for JobSpawn {
|
||||
let mut jobs = jobs.lock().expect("jobs lock is poisoned!");
|
||||
|
||||
let id = {
|
||||
let thread_job = ThreadJob::new(job_signals);
|
||||
let thread_job = ThreadJob::new(job_signals, tag);
|
||||
job_state.current_thread_job = Some(thread_job.clone());
|
||||
jobs.add_job(Job::Thread(thread_job))
|
||||
};
|
||||
|
75
crates/nu-command/src/experimental/job_tag.rs
Normal file
75
crates/nu-command/src/experimental/job_tag.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::JobId;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JobTag;
|
||||
|
||||
impl Command for JobTag {
|
||||
fn name(&self) -> &str {
|
||||
"job tag"
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Add a description tag to a background job."
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("job tag")
|
||||
.category(Category::Experimental)
|
||||
.required("id", SyntaxShape::Int, "The id of the job to tag.")
|
||||
.required(
|
||||
"tag",
|
||||
SyntaxShape::OneOf(vec![SyntaxShape::String, SyntaxShape::Nothing]),
|
||||
"The tag to assign to the job.",
|
||||
)
|
||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||
.allow_variants_without_examples(true)
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["describe", "desc"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
|
||||
let id_arg: Spanned<i64> = call.req(engine_state, stack, 0)?;
|
||||
|
||||
if id_arg.item < 0 {
|
||||
return Err(ShellError::NeedsPositiveValue { span: id_arg.span });
|
||||
}
|
||||
|
||||
let id: JobId = JobId::new(id_arg.item as usize);
|
||||
|
||||
let tag: Option<String> = call.req(engine_state, stack, 1)?;
|
||||
|
||||
let mut jobs = engine_state.jobs.lock().expect("jobs lock is poisoned!");
|
||||
|
||||
match jobs.lookup_mut(id) {
|
||||
None => {
|
||||
return Err(ShellError::JobNotFound {
|
||||
id: id.get(),
|
||||
span: head,
|
||||
});
|
||||
}
|
||||
|
||||
Some(job) => job.assign_tag(tag),
|
||||
}
|
||||
|
||||
Ok(Value::nothing(head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
example: "let id = job spawn { sleep 10sec }; job tag $id abc ",
|
||||
description: "tag a newly spawned job",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
@ -112,7 +112,10 @@ fn unfreeze_job(
|
||||
span,
|
||||
}),
|
||||
|
||||
Job::Frozen(FrozenJob { unfreeze: handle }) => {
|
||||
Job::Frozen(FrozenJob {
|
||||
unfreeze: handle,
|
||||
tag,
|
||||
}) => {
|
||||
let pid = handle.pid();
|
||||
|
||||
if let Some(thread_job) = &state.current_thread_job {
|
||||
@ -141,7 +144,13 @@ fn unfreeze_job(
|
||||
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 }))
|
||||
jobs.add_job_with_id(
|
||||
old_id,
|
||||
Job::Frozen(FrozenJob {
|
||||
unfreeze: handle,
|
||||
tag,
|
||||
}),
|
||||
)
|
||||
.expect("job was supposed to be removed");
|
||||
|
||||
if state.is_interactive {
|
||||
|
@ -3,6 +3,7 @@ mod job;
|
||||
mod job_kill;
|
||||
mod job_list;
|
||||
mod job_spawn;
|
||||
mod job_tag;
|
||||
|
||||
#[cfg(all(unix, feature = "os"))]
|
||||
mod job_unfreeze;
|
||||
@ -11,8 +12,8 @@ pub use is_admin::IsAdmin;
|
||||
pub use job::Job;
|
||||
pub use job_kill::JobKill;
|
||||
pub use job_list::JobList;
|
||||
|
||||
pub use job_spawn::JobSpawn;
|
||||
pub use job_tag::JobTag;
|
||||
|
||||
#[cfg(all(unix, feature = "os"))]
|
||||
pub use job_unfreeze::JobUnfreeze;
|
||||
|
@ -332,7 +332,12 @@ impl Command for External {
|
||||
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 }));
|
||||
// TODO: use name of process as a tag
|
||||
let job_id = jobs.add_job(Job::Frozen(FrozenJob {
|
||||
unfreeze,
|
||||
tag: None,
|
||||
}));
|
||||
|
||||
if is_interactive {
|
||||
println!("\nJob {} is frozen", job_id.get());
|
||||
}
|
||||
|
@ -215,3 +215,53 @@ fn job_extern_into_pipe_is_not_silent() {
|
||||
assert_eq!(actual.out, "11");
|
||||
assert_eq!(actual.err, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn job_list_returns_no_tag_when_job_is_untagged() {
|
||||
let actual = nu!(r#"
|
||||
job spawn { sleep 10sec }
|
||||
job spawn { sleep 10sec }
|
||||
job spawn { sleep 10sec }
|
||||
|
||||
('tag' in (job list | columns)) | to nuon"#);
|
||||
|
||||
assert_eq!(actual.out, "false");
|
||||
assert_eq!(actual.err, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn job_list_returns_tag_when_job_is_spawned_with_tag() {
|
||||
let actual = nu!(r#"
|
||||
job spawn { sleep 10sec } --tag abc
|
||||
job list | where id == 1 | get tag.0
|
||||
"#);
|
||||
|
||||
assert_eq!(actual.out, "abc");
|
||||
assert_eq!(actual.err, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn job_tag_modifies_untagged_job_tag() {
|
||||
let actual = nu!(r#"
|
||||
job spawn { sleep 10sec }
|
||||
|
||||
job tag 1 beep
|
||||
|
||||
job list | where id == 1 | get tag.0"#);
|
||||
|
||||
assert_eq!(actual.out, "beep");
|
||||
assert_eq!(actual.err, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn job_tag_modifies_tagged_job_tag() {
|
||||
let actual = nu!(r#"
|
||||
job spawn { sleep 10sec } --tag abc
|
||||
|
||||
job tag 1 beep
|
||||
|
||||
job list | where id == 1 | get tag.0"#);
|
||||
|
||||
assert_eq!(actual.out, "beep");
|
||||
assert_eq!(actual.err, "");
|
||||
}
|
||||
|
@ -38,6 +38,10 @@ impl Jobs {
|
||||
self.jobs.get(&id)
|
||||
}
|
||||
|
||||
pub fn lookup_mut(&mut self, id: JobId) -> Option<&mut Job> {
|
||||
self.jobs.get_mut(&id)
|
||||
}
|
||||
|
||||
pub fn remove_job(&mut self, id: JobId) -> Option<Job> {
|
||||
if self.last_frozen_job_id.is_some_and(|last| id == last) {
|
||||
self.last_frozen_job_id = None;
|
||||
@ -134,13 +138,15 @@ pub enum Job {
|
||||
pub struct ThreadJob {
|
||||
signals: Signals,
|
||||
pids: Arc<Mutex<HashSet<u32>>>,
|
||||
tag: Option<String>,
|
||||
}
|
||||
|
||||
impl ThreadJob {
|
||||
pub fn new(signals: Signals) -> Self {
|
||||
pub fn new(signals: Signals, tag: Option<String>) -> Self {
|
||||
ThreadJob {
|
||||
signals,
|
||||
pids: Arc::new(Mutex::new(HashSet::default())),
|
||||
tag,
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,10 +203,25 @@ impl Job {
|
||||
Job::Frozen(frozen_job) => frozen_job.kill(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> Option<&String> {
|
||||
match self {
|
||||
Job::Thread(thread_job) => thread_job.tag.as_ref(),
|
||||
Job::Frozen(frozen_job) => frozen_job.tag.as_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assign_tag(&mut self, tag: Option<String>) {
|
||||
match self {
|
||||
Job::Thread(thread_job) => thread_job.tag = tag,
|
||||
Job::Frozen(frozen_job) => frozen_job.tag = tag,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FrozenJob {
|
||||
pub unfreeze: UnfreezeHandle,
|
||||
pub tag: Option<String>,
|
||||
}
|
||||
|
||||
impl FrozenJob {
|
||||
|
Loading…
Reference in New Issue
Block a user