1//! A source of tasks, based on ad-hoc user command prompt input.
2
3use std::sync::Arc;
4
5use crate::{
6 static_source::RevealStrategy, SpawnInTerminal, Task, TaskContext, TaskId, TaskSource,
7};
8use gpui::{AppContext, Context, Model};
9
10/// A storage and source of tasks generated out of user command prompt inputs.
11pub struct OneshotSource {
12 tasks: Vec<Arc<dyn Task>>,
13}
14
15#[derive(Clone)]
16struct OneshotTask {
17 id: TaskId,
18}
19
20impl OneshotTask {
21 fn new(prompt: String) -> Self {
22 Self { id: TaskId(prompt) }
23 }
24}
25
26impl Task for OneshotTask {
27 fn id(&self) -> &TaskId {
28 &self.id
29 }
30
31 fn name(&self) -> &str {
32 &self.id.0
33 }
34
35 fn cwd(&self) -> Option<&str> {
36 None
37 }
38
39 fn prepare_exec(&self, cx: TaskContext) -> Option<SpawnInTerminal> {
40 if self.id().0.is_empty() {
41 return None;
42 }
43 let TaskContext {
44 cwd,
45 task_variables,
46 } = cx;
47 Some(SpawnInTerminal {
48 id: self.id().clone(),
49 label: self.name().to_owned(),
50 command: self.id().0.clone(),
51 args: vec![],
52 cwd,
53 env: task_variables.into_env_variables(),
54 use_new_terminal: Default::default(),
55 allow_concurrent_runs: Default::default(),
56 reveal: RevealStrategy::default(),
57 })
58 }
59}
60
61impl OneshotSource {
62 /// Initializes the oneshot source, preparing to store user prompts.
63 pub fn new(cx: &mut AppContext) -> Model<Box<dyn TaskSource>> {
64 cx.new_model(|_| Box::new(Self { tasks: Vec::new() }) as Box<dyn TaskSource>)
65 }
66
67 /// Spawns a certain task based on the user prompt.
68 pub fn spawn(&mut self, prompt: String) -> Arc<dyn Task> {
69 if let Some(task) = self.tasks.iter().find(|task| task.id().0 == prompt) {
70 // If we already have an oneshot task with that command, let's just reuse it.
71 task.clone()
72 } else {
73 let new_oneshot = Arc::new(OneshotTask::new(prompt));
74 self.tasks.push(new_oneshot.clone());
75 new_oneshot
76 }
77 }
78 /// Removes a task with a given ID from this source.
79 pub fn remove(&mut self, id: &TaskId) {
80 let position = self.tasks.iter().position(|task| task.id() == id);
81 if let Some(position) = position {
82 self.tasks.remove(position);
83 }
84 }
85}
86
87impl TaskSource for OneshotSource {
88 fn as_any(&mut self) -> &mut dyn std::any::Any {
89 self
90 }
91
92 fn tasks_to_schedule(
93 &mut self,
94 _cx: &mut gpui::ModelContext<Box<dyn TaskSource>>,
95 ) -> Vec<Arc<dyn Task>> {
96 self.tasks.clone()
97 }
98}