oneshot_source.rs

 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_for_path(
93        &mut self,
94        _path: Option<&std::path::Path>,
95        _cx: &mut gpui::ModelContext<Box<dyn TaskSource>>,
96    ) -> Vec<Arc<dyn Task>> {
97        self.tasks.clone()
98    }
99}