extension_slash_command.rs

  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::{BufferSnapshot, 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        _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
 86        _context_buffer: BufferSnapshot,
 87        _workspace: WeakView<Workspace>,
 88        delegate: Option<Arc<dyn LspAdapterDelegate>>,
 89        cx: &mut WindowContext,
 90    ) -> Task<Result<SlashCommandOutput>> {
 91        let arguments = arguments.to_owned();
 92        let output = cx.background_executor().spawn(async move {
 93            self.extension
 94                .call({
 95                    let this = self.clone();
 96                    move |extension, store| {
 97                        async move {
 98                            let resource = if let Some(delegate) = delegate {
 99                                Some(store.data_mut().table().push(delegate)?)
100                            } else {
101                                None
102                            };
103                            let output = extension
104                                .call_run_slash_command(store, &this.command, &arguments, resource)
105                                .await?
106                                .map_err(|e| anyhow!("{}", e))?;
107
108                            anyhow::Ok(output)
109                        }
110                        .boxed()
111                    }
112                })
113                .await
114        });
115        cx.foreground_executor().spawn(async move {
116            let output = output.await?;
117            Ok(SlashCommandOutput {
118                text: output.text,
119                sections: output
120                    .sections
121                    .into_iter()
122                    .map(|section| SlashCommandOutputSection {
123                        range: section.range.into(),
124                        icon: IconName::Code,
125                        label: section.label.into(),
126                        metadata: None,
127                    })
128                    .collect(),
129                run_commands_in_text: false,
130            })
131        })
132    }
133}