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}