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::{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 arguments: &[String],
43 _cancel: Arc<AtomicBool>,
44 _workspace: Option<WeakView<Workspace>>,
45 cx: &mut WindowContext,
46 ) -> Task<Result<Vec<ArgumentCompletion>>> {
47 let arguments = arguments.to_owned();
48 cx.background_executor().spawn(async move {
49 self.extension
50 .call({
51 let this = self.clone();
52 move |extension, store| {
53 async move {
54 let completions = extension
55 .call_complete_slash_command_argument(
56 store,
57 &this.command,
58 &arguments,
59 )
60 .await?
61 .map_err(|e| anyhow!("{}", e))?;
62
63 anyhow::Ok(
64 completions
65 .into_iter()
66 .map(|completion| ArgumentCompletion {
67 label: completion.label.into(),
68 new_text: completion.new_text,
69 run_command: completion.run_command,
70 })
71 .collect(),
72 )
73 }
74 .boxed()
75 }
76 })
77 .await
78 })
79 }
80
81 fn run(
82 self: Arc<Self>,
83 arguments: &[String],
84 _workspace: WeakView<Workspace>,
85 delegate: Option<Arc<dyn LspAdapterDelegate>>,
86 cx: &mut WindowContext,
87 ) -> Task<Result<SlashCommandOutput>> {
88 let arguments = arguments.to_owned();
89 let output = cx.background_executor().spawn(async move {
90 self.extension
91 .call({
92 let this = self.clone();
93 move |extension, store| {
94 async move {
95 let resource = if let Some(delegate) = delegate {
96 Some(store.data_mut().table().push(delegate)?)
97 } else {
98 None
99 };
100 let output = extension
101 .call_run_slash_command(store, &this.command, &arguments, resource)
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}