1use std::sync::{atomic::AtomicBool, Arc};
2
3use anyhow::{anyhow, Result};
4use assistant_slash_command::{
5 ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
6};
7use futures::FutureExt;
8use gpui::{AppContext, Task, WeakView, WindowContext};
9use language::LspAdapterDelegate;
10use ui::prelude::*;
11use wasmtime_wasi::WasiView;
12use workspace::Workspace;
13
14use crate::wasm_host::{WasmExtension, WasmHost};
15
16pub struct ExtensionSlashCommand {
17 pub(crate) extension: WasmExtension,
18 #[allow(unused)]
19 pub(crate) host: Arc<WasmHost>,
20 pub(crate) command: crate::wit::SlashCommand,
21}
22
23impl SlashCommand for ExtensionSlashCommand {
24 fn name(&self) -> String {
25 self.command.name.clone()
26 }
27
28 fn description(&self) -> String {
29 self.command.description.clone()
30 }
31
32 fn menu_text(&self) -> String {
33 self.command.tooltip_text.clone()
34 }
35
36 fn requires_argument(&self) -> bool {
37 self.command.requires_argument
38 }
39
40 fn complete_argument(
41 self: Arc<Self>,
42 query: String,
43 _cancel: Arc<AtomicBool>,
44 _workspace: Option<WeakView<Workspace>>,
45 cx: &mut AppContext,
46 ) -> Task<Result<Vec<ArgumentCompletion>>> {
47 cx.background_executor().spawn(async move {
48 self.extension
49 .call({
50 let this = self.clone();
51 move |extension, store| {
52 async move {
53 let completions = extension
54 .call_complete_slash_command_argument(
55 store,
56 &this.command,
57 query.as_ref(),
58 )
59 .await?
60 .map_err(|e| anyhow!("{}", e))?;
61
62 anyhow::Ok(
63 completions
64 .into_iter()
65 .map(|completion| ArgumentCompletion {
66 label: completion.label,
67 new_text: completion.new_text,
68 run_command: completion.run_command,
69 })
70 .collect(),
71 )
72 }
73 .boxed()
74 }
75 })
76 .await
77 })
78 }
79
80 fn run(
81 self: Arc<Self>,
82 argument: Option<&str>,
83 _workspace: WeakView<Workspace>,
84 delegate: Arc<dyn LspAdapterDelegate>,
85 cx: &mut WindowContext,
86 ) -> Task<Result<SlashCommandOutput>> {
87 let argument = argument.map(|arg| arg.to_string());
88 let output = cx.background_executor().spawn(async move {
89 self.extension
90 .call({
91 let this = self.clone();
92 move |extension, store| {
93 async move {
94 let resource = store.data_mut().table().push(delegate)?;
95 let output = extension
96 .call_run_slash_command(
97 store,
98 &this.command,
99 argument.as_deref(),
100 resource,
101 )
102 .await?
103 .map_err(|e| anyhow!("{}", e))?;
104
105 anyhow::Ok(output)
106 }
107 .boxed()
108 }
109 })
110 .await
111 });
112 cx.foreground_executor().spawn(async move {
113 let output = output.await?;
114 Ok(SlashCommandOutput {
115 text: output.text,
116 sections: output
117 .sections
118 .into_iter()
119 .map(|section| SlashCommandOutputSection {
120 range: section.range.into(),
121 icon: IconName::Code,
122 label: section.label.into(),
123 })
124 .collect(),
125 run_commands_in_text: false,
126 })
127 })
128 }
129}