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