1use std::sync::atomic::AtomicBool;
2use std::sync::Arc;
3
4use anyhow::{anyhow, Result};
5use assistant_slash_command::{SlashCommand, SlashCommandCleanup, SlashCommandInvocation};
6use futures::channel::oneshot;
7use futures::FutureExt;
8use gpui::{AppContext, Task};
9use language::LspAdapterDelegate;
10use wasmtime_wasi::WasiView;
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 requires_argument(&self) -> bool {
31 self.command.requires_argument
32 }
33
34 fn complete_argument(
35 &self,
36 _query: String,
37 _cancel: Arc<AtomicBool>,
38 _cx: &mut AppContext,
39 ) -> Task<Result<Vec<String>>> {
40 Task::ready(Ok(Vec::new()))
41 }
42
43 fn run(
44 self: Arc<Self>,
45 argument: Option<&str>,
46 delegate: Arc<dyn LspAdapterDelegate>,
47 cx: &mut AppContext,
48 ) -> SlashCommandInvocation {
49 let argument = argument.map(|arg| arg.to_string());
50
51 let output = cx.background_executor().spawn(async move {
52 let output = self
53 .extension
54 .call({
55 let this = self.clone();
56 move |extension, store| {
57 async move {
58 let resource = store.data_mut().table().push(delegate)?;
59 let output = extension
60 .call_run_slash_command(
61 store,
62 &this.command,
63 argument.as_deref(),
64 resource,
65 )
66 .await?
67 .map_err(|e| anyhow!("{}", e))?;
68
69 anyhow::Ok(output)
70 }
71 .boxed()
72 }
73 })
74 .await?;
75
76 output.ok_or_else(|| anyhow!("no output from command: {}", self.command.name))
77 });
78
79 SlashCommandInvocation {
80 output,
81 invalidated: oneshot::channel().1,
82 cleanup: SlashCommandCleanup::default(),
83 }
84 }
85}