Add placeholder for extension slash commands (#12387)

Marshall Bowers created

This PR adds a slightly better placeholder for extension slash commands
than the current "TODO" text.

Right now we display the command name and an arbitrary icon.

<img width="644" alt="Screenshot 2024-05-28 at 12 15 07 PM"
src="https://github.com/zed-industries/zed/assets/1486634/11761797-5ccc-4209-8b00-70b714f10a78">

Release Notes:

- N/A

Change summary

Cargo.lock                                      |  1 
crates/extension/Cargo.toml                     |  1 
crates/extension/src/extension_slash_command.rs | 21 +++++++++++++++---
3 files changed, 19 insertions(+), 4 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3774,6 +3774,7 @@ dependencies = [
  "task",
  "theme",
  "toml 0.8.10",
+ "ui",
  "url",
  "util",
  "wasm-encoder",

crates/extension/Cargo.toml 🔗

@@ -39,6 +39,7 @@ serde_json.workspace = true
 settings.workspace = true
 theme.workspace = true
 toml.workspace = true
+ui.workspace = true
 url.workspace = true
 util.workspace = true
 wasm-encoder.workspace = true

crates/extension/src/extension_slash_command.rs 🔗

@@ -1,13 +1,16 @@
-use crate::wasm_host::{WasmExtension, WasmHost};
+use std::sync::{atomic::AtomicBool, Arc};
+
 use anyhow::{anyhow, Result};
 use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
 use futures::FutureExt;
 use gpui::{AppContext, IntoElement, Task, WeakView, WindowContext};
 use language::LspAdapterDelegate;
-use std::sync::{atomic::AtomicBool, Arc};
+use ui::{prelude::*, ButtonLike, ElevationIndex};
 use wasmtime_wasi::WasiView;
 use workspace::Workspace;
 
+use crate::wasm_host::{WasmExtension, WasmHost};
+
 pub struct ExtensionSlashCommand {
     pub(crate) extension: WasmExtension,
     #[allow(unused)]
@@ -48,6 +51,7 @@ impl SlashCommand for ExtensionSlashCommand {
         delegate: Arc<dyn LspAdapterDelegate>,
         cx: &mut WindowContext,
     ) -> Task<Result<SlashCommandOutput>> {
+        let command_name = SharedString::from(self.command.name.clone());
         let argument = argument.map(|arg| arg.to_string());
         let text = cx.background_executor().spawn(async move {
             let output = self
@@ -82,8 +86,17 @@ impl SlashCommand for ExtensionSlashCommand {
                 text,
                 sections: vec![SlashCommandOutputSection {
                     range,
-                    render_placeholder: Arc::new(|_, _, _| {
-                        "TODO: Extension command output".into_any_element()
+                    render_placeholder: Arc::new({
+                        let command_name = command_name.clone();
+                        move |id, unfold, _cx| {
+                            ButtonLike::new(id)
+                                .style(ButtonStyle::Filled)
+                                .layer(ElevationIndex::ElevatedSurface)
+                                .child(Icon::new(IconName::Code))
+                                .child(Label::new(command_name.clone()))
+                                .on_click(move |_event, cx| unfold(cx))
+                                .into_any_element()
+                        }
                     }),
                 }],
             })