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: Option<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 = if let Some(delegate) = delegate {
95 Some(store.data_mut().table().push(delegate)?)
96 } else {
97 None
98 };
99 let output = extension
100 .call_run_slash_command(
101 store,
102 &this.command,
103 argument.as_deref(),
104 resource,
105 )
106 .await?
107 .map_err(|e| anyhow!("{}", e))?;
108
109 anyhow::Ok(output)
110 }
111 .boxed()
112 }
113 })
114 .await
115 });
116 cx.foreground_executor().spawn(async move {
117 let output = output.await?;
118 Ok(SlashCommandOutput {
119 text: output.text,
120 sections: output
121 .sections
122 .into_iter()
123 .map(|section| SlashCommandOutputSection {
124 range: section.range.into(),
125 icon: IconName::Code,
126 label: section.label.into(),
127 })
128 .collect(),
129 run_commands_in_text: false,
130 })
131 })
132 }
133}