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 menu_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        _workspace: Option<WeakView<Workspace>>,
 43        _cx: &mut AppContext,
 44    ) -> Task<Result<Vec<String>>> {
 45        Task::ready(Ok(Vec::new()))
 46    }
 47
 48    fn run(
 49        self: Arc<Self>,
 50        argument: Option<&str>,
 51        _workspace: WeakView<Workspace>,
 52        delegate: Arc<dyn LspAdapterDelegate>,
 53        cx: &mut WindowContext,
 54    ) -> Task<Result<SlashCommandOutput>> {
 55        let command_name = SharedString::from(self.command.name.clone());
 56        let argument = argument.map(|arg| arg.to_string());
 57        let text = cx.background_executor().spawn(async move {
 58            let output = self
 59                .extension
 60                .call({
 61                    let this = self.clone();
 62                    move |extension, store| {
 63                        async move {
 64                            let resource = store.data_mut().table().push(delegate)?;
 65                            let output = extension
 66                                .call_run_slash_command(
 67                                    store,
 68                                    &this.command,
 69                                    argument.as_deref(),
 70                                    resource,
 71                                )
 72                                .await?
 73                                .map_err(|e| anyhow!("{}", e))?;
 74
 75                            anyhow::Ok(output)
 76                        }
 77                        .boxed()
 78                    }
 79                })
 80                .await?;
 81            output.ok_or_else(|| anyhow!("no output from command: {}", self.command.name))
 82        });
 83        cx.foreground_executor().spawn(async move {
 84            let text = text.await?;
 85            let range = 0..text.len();
 86            Ok(SlashCommandOutput {
 87                text,
 88                sections: vec![SlashCommandOutputSection {
 89                    range,
 90                    render_placeholder: Arc::new({
 91                        let command_name = command_name.clone();
 92                        move |id, unfold, _cx| {
 93                            ButtonLike::new(id)
 94                                .style(ButtonStyle::Filled)
 95                                .layer(ElevationIndex::ElevatedSurface)
 96                                .child(Icon::new(IconName::Code))
 97                                .child(Label::new(command_name.clone()))
 98                                .on_click(move |_event, cx| unfold(cx))
 99                                .into_any_element()
100                        }
101                    }),
102                }],
103                run_commands_in_text: false,
104            })
105        })
106    }
107}