prompt_command.rs

 1use super::{SlashCommand, SlashCommandOutput};
 2use crate::prompt_library::PromptStore;
 3use anyhow::{anyhow, Context, Result};
 4use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
 5use gpui::{AppContext, Task, WeakView};
 6use language::LspAdapterDelegate;
 7use std::sync::{atomic::AtomicBool, Arc};
 8use ui::prelude::*;
 9use workspace::Workspace;
10
11pub(crate) struct PromptSlashCommand;
12
13impl SlashCommand for PromptSlashCommand {
14    fn name(&self) -> String {
15        "prompt".into()
16    }
17
18    fn description(&self) -> String {
19        "insert prompt from library".into()
20    }
21
22    fn menu_text(&self) -> String {
23        "Insert Prompt from Library".into()
24    }
25
26    fn requires_argument(&self) -> bool {
27        true
28    }
29
30    fn complete_argument(
31        self: Arc<Self>,
32        query: String,
33        _cancellation_flag: Arc<AtomicBool>,
34        _workspace: Option<WeakView<Workspace>>,
35        cx: &mut AppContext,
36    ) -> Task<Result<Vec<ArgumentCompletion>>> {
37        let store = PromptStore::global(cx);
38        cx.background_executor().spawn(async move {
39            let prompts = store.await?.search(query).await;
40            Ok(prompts
41                .into_iter()
42                .filter_map(|prompt| {
43                    let prompt_title = prompt.title?.to_string();
44                    Some(ArgumentCompletion {
45                        label: prompt_title.clone(),
46                        new_text: prompt_title,
47                        run_command: true,
48                    })
49                })
50                .collect())
51        })
52    }
53
54    fn run(
55        self: Arc<Self>,
56        title: Option<&str>,
57        _workspace: WeakView<Workspace>,
58        _delegate: Arc<dyn LspAdapterDelegate>,
59        cx: &mut WindowContext,
60    ) -> Task<Result<SlashCommandOutput>> {
61        let Some(title) = title else {
62            return Task::ready(Err(anyhow!("missing prompt name")));
63        };
64
65        let store = PromptStore::global(cx);
66        let title = SharedString::from(title.to_string());
67        let prompt = cx.background_executor().spawn({
68            let title = title.clone();
69            async move {
70                let store = store.await?;
71                let prompt_id = store
72                    .id_for_title(&title)
73                    .with_context(|| format!("no prompt found with title {:?}", title))?;
74                let body = store.load(prompt_id).await?;
75                anyhow::Ok(body)
76            }
77        });
78        cx.foreground_executor().spawn(async move {
79            let mut prompt = prompt.await?;
80            if prompt.is_empty() {
81                prompt.push('\n');
82            }
83            let range = 0..prompt.len();
84            Ok(SlashCommandOutput {
85                text: prompt,
86                sections: vec![SlashCommandOutputSection {
87                    range,
88                    icon: IconName::Library,
89                    label: title,
90                }],
91                run_commands_in_text: true,
92            })
93        })
94    }
95}