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