extension_slash_command.rs

  1use std::sync::{atomic::AtomicBool, Arc};
  2
  3use anyhow::{anyhow, Result};
  4use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
  5use futures::FutureExt;
  6use gpui::{AppContext, IntoElement, Task, WeakView, WindowContext};
  7use language::LspAdapterDelegate;
  8use ui::{prelude::*, ButtonLike, ElevationIndex};
  9use wasmtime_wasi::WasiView;
 10use workspace::Workspace;
 11
 12use crate::wasm_host::{WasmExtension, WasmHost};
 13
 14pub struct ExtensionSlashCommand {
 15    pub(crate) extension: WasmExtension,
 16    #[allow(unused)]
 17    pub(crate) host: Arc<WasmHost>,
 18    pub(crate) command: crate::wit::SlashCommand,
 19}
 20
 21impl SlashCommand for ExtensionSlashCommand {
 22    fn name(&self) -> String {
 23        self.command.name.clone()
 24    }
 25
 26    fn description(&self) -> String {
 27        self.command.description.clone()
 28    }
 29
 30    fn tooltip_text(&self) -> String {
 31        self.command.tooltip_text.clone()
 32    }
 33
 34    fn requires_argument(&self) -> bool {
 35        self.command.requires_argument
 36    }
 37
 38    fn complete_argument(
 39        &self,
 40        _query: String,
 41        _cancel: Arc<AtomicBool>,
 42        _cx: &mut AppContext,
 43    ) -> Task<Result<Vec<String>>> {
 44        Task::ready(Ok(Vec::new()))
 45    }
 46
 47    fn run(
 48        self: Arc<Self>,
 49        argument: Option<&str>,
 50        _workspace: WeakView<Workspace>,
 51        delegate: Arc<dyn LspAdapterDelegate>,
 52        cx: &mut WindowContext,
 53    ) -> Task<Result<SlashCommandOutput>> {
 54        let command_name = SharedString::from(self.command.name.clone());
 55        let argument = argument.map(|arg| arg.to_string());
 56        let text = cx.background_executor().spawn(async move {
 57            let output = self
 58                .extension
 59                .call({
 60                    let this = self.clone();
 61                    move |extension, store| {
 62                        async move {
 63                            let resource = store.data_mut().table().push(delegate)?;
 64                            let output = extension
 65                                .call_run_slash_command(
 66                                    store,
 67                                    &this.command,
 68                                    argument.as_deref(),
 69                                    resource,
 70                                )
 71                                .await?
 72                                .map_err(|e| anyhow!("{}", e))?;
 73
 74                            anyhow::Ok(output)
 75                        }
 76                        .boxed()
 77                    }
 78                })
 79                .await?;
 80            output.ok_or_else(|| anyhow!("no output from command: {}", self.command.name))
 81        });
 82        cx.foreground_executor().spawn(async move {
 83            let text = text.await?;
 84            let range = 0..text.len();
 85            Ok(SlashCommandOutput {
 86                text,
 87                sections: vec![SlashCommandOutputSection {
 88                    range,
 89                    render_placeholder: Arc::new({
 90                        let command_name = command_name.clone();
 91                        move |id, unfold, _cx| {
 92                            ButtonLike::new(id)
 93                                .style(ButtonStyle::Filled)
 94                                .layer(ElevationIndex::ElevatedSurface)
 95                                .child(Icon::new(IconName::Code))
 96                                .child(Label::new(command_name.clone()))
 97                                .on_click(move |_event, cx| unfold(cx))
 98                                .into_any_element()
 99                        }
100                    }),
101                }],
102            })
103        })
104    }
105}