1use std::sync::{atomic::AtomicBool, Arc};
2
3use anyhow::{anyhow, Result};
4use assistant_slash_command::{
5 ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
6 SlashCommandResult,
7};
8use futures::FutureExt;
9use gpui::{Task, WeakView, WindowContext};
10use language::{BufferSnapshot, LspAdapterDelegate};
11use ui::prelude::*;
12use wasmtime_wasi::WasiView;
13use workspace::Workspace;
14
15use crate::wasm_host::{WasmExtension, WasmHost};
16
17pub struct ExtensionSlashCommand {
18 pub(crate) extension: WasmExtension,
19 #[allow(unused)]
20 pub(crate) host: Arc<WasmHost>,
21 pub(crate) command: crate::wit::SlashCommand,
22}
23
24impl SlashCommand for ExtensionSlashCommand {
25 fn name(&self) -> String {
26 self.command.name.clone()
27 }
28
29 fn description(&self) -> String {
30 self.command.description.clone()
31 }
32
33 fn menu_text(&self) -> String {
34 self.command.tooltip_text.clone()
35 }
36
37 fn requires_argument(&self) -> bool {
38 self.command.requires_argument
39 }
40
41 fn complete_argument(
42 self: Arc<Self>,
43 arguments: &[String],
44 _cancel: Arc<AtomicBool>,
45 _workspace: Option<WeakView<Workspace>>,
46 cx: &mut WindowContext,
47 ) -> Task<Result<Vec<ArgumentCompletion>>> {
48 let arguments = arguments.to_owned();
49 cx.background_executor().spawn(async move {
50 self.extension
51 .call({
52 let this = self.clone();
53 move |extension, store| {
54 async move {
55 let completions = extension
56 .call_complete_slash_command_argument(
57 store,
58 &this.command,
59 &arguments,
60 )
61 .await?
62 .map_err(|e| anyhow!("{}", e))?;
63
64 anyhow::Ok(
65 completions
66 .into_iter()
67 .map(|completion| ArgumentCompletion {
68 label: completion.label.into(),
69 new_text: completion.new_text,
70 replace_previous_arguments: false,
71 after_completion: completion.run_command.into(),
72 })
73 .collect(),
74 )
75 }
76 .boxed()
77 }
78 })
79 .await
80 })
81 }
82
83 fn run(
84 self: Arc<Self>,
85 arguments: &[String],
86 _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
87 _context_buffer: BufferSnapshot,
88 _workspace: WeakView<Workspace>,
89 delegate: Option<Arc<dyn LspAdapterDelegate>>,
90 cx: &mut WindowContext,
91 ) -> Task<SlashCommandResult> {
92 let arguments = arguments.to_owned();
93 let output = cx.background_executor().spawn(async move {
94 self.extension
95 .call({
96 let this = self.clone();
97 move |extension, store| {
98 async move {
99 let resource = if let Some(delegate) = delegate {
100 Some(store.data_mut().table().push(delegate)?)
101 } else {
102 None
103 };
104 let output = extension
105 .call_run_slash_command(store, &this.command, &arguments, resource)
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 metadata: None,
128 })
129 .collect(),
130 run_commands_in_text: false,
131 }
132 .to_event_stream())
133 })
134 }
135}