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