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    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}