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 replace_previous_arguments: false,
70 after_completion: completion.run_command.into(),
71 })
72 .collect(),
73 )
74 }
75 .boxed()
76 }
77 })
78 .await
79 })
80 }
81
82 fn run(
83 self: Arc<Self>,
84 arguments: &[String],
85 _workspace: WeakView<Workspace>,
86 delegate: Option<Arc<dyn LspAdapterDelegate>>,
87 cx: &mut WindowContext,
88 ) -> Task<Result<SlashCommandOutput>> {
89 let arguments = arguments.to_owned();
90 let output = cx.background_executor().spawn(async move {
91 self.extension
92 .call({
93 let this = self.clone();
94 move |extension, store| {
95 async move {
96 let resource = if let Some(delegate) = delegate {
97 Some(store.data_mut().table().push(delegate)?)
98 } else {
99 None
100 };
101 let output = extension
102 .call_run_slash_command(store, &this.command, &arguments, resource)
103 .await?
104 .map_err(|e| anyhow!("{}", e))?;
105
106 anyhow::Ok(output)
107 }
108 .boxed()
109 }
110 })
111 .await
112 });
113 cx.foreground_executor().spawn(async move {
114 let output = output.await?;
115 Ok(SlashCommandOutput {
116 text: output.text,
117 sections: output
118 .sections
119 .into_iter()
120 .map(|section| SlashCommandOutputSection {
121 range: section.range.into(),
122 icon: IconName::Code,
123 label: section.label.into(),
124 })
125 .collect(),
126 run_commands_in_text: false,
127 })
128 })
129 }
130}