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: 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 = store.data_mut().table().push(delegate)?;
 95                            let output = extension
 96                                .call_run_slash_command(
 97                                    store,
 98                                    &this.command,
 99                                    argument.as_deref(),
100                                    resource,
101                                )
102                                .await?
103                                .map_err(|e| anyhow!("{}", e))?;
104
105                            anyhow::Ok(output)
106                        }
107                        .boxed()
108                    }
109                })
110                .await
111        });
112        cx.foreground_executor().spawn(async move {
113            let output = output.await?;
114            Ok(SlashCommandOutput {
115                text: output.text,
116                sections: output
117                    .sections
118                    .into_iter()
119                    .map(|section| SlashCommandOutputSection {
120                        range: section.range.into(),
121                        icon: IconName::Code,
122                        label: section.label.into(),
123                    })
124                    .collect(),
125                run_commands_in_text: false,
126            })
127        })
128    }
129}