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