diff --git a/crates/extension/src/extension_slash_command.rs b/crates/extension/src/extension_slash_command.rs index 4f555f94ab..23de82174c 100644 --- a/crates/extension/src/extension_slash_command.rs +++ b/crates/extension/src/extension_slash_command.rs @@ -52,11 +52,9 @@ impl SlashCommand for ExtensionSlashCommand { delegate: Arc, cx: &mut WindowContext, ) -> Task> { - 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 - .extension + let output = cx.background_executor().spawn(async move { + self.extension .call({ let this = self.clone(); move |extension, store| { @@ -77,19 +75,21 @@ impl SlashCommand for ExtensionSlashCommand { .boxed() } }) - .await?; - output.ok_or_else(|| anyhow!("no output from command: {}", self.command.name)) + .await }); cx.foreground_executor().spawn(async move { - let text = text.await?; - let range = 0..text.len(); + let output = output.await?; Ok(SlashCommandOutput { - text, - sections: vec![SlashCommandOutputSection { - range, - icon: IconName::Code, - label: command_name, - }], + text: output.text, + sections: output + .sections + .into_iter() + .map(|section| SlashCommandOutputSection { + range: section.range.into(), + icon: IconName::Code, + label: section.label.into(), + }) + .collect(), run_commands_in_text: false, }) }) diff --git a/crates/extension/src/wasm_host/wit.rs b/crates/extension/src/wasm_host/wit.rs index 57a0e140a3..9963840b14 100644 --- a/crates/extension/src/wasm_host/wit.rs +++ b/crates/extension/src/wasm_host/wit.rs @@ -6,7 +6,7 @@ use release_channel::ReleaseChannel; use since_v0_0_7 as latest; use super::{wasm_engine, WasmState}; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use language::{LanguageServerName, LspAdapterDelegate}; use semantic_version::SemanticVersion; use std::{ops::RangeInclusive, sync::Arc}; @@ -19,6 +19,7 @@ use wasmtime::{ pub use latest::CodeLabelSpanLiteral; pub use latest::{ zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind}, + zed::extension::slash_command::SlashCommandOutput, CodeLabel, CodeLabelSpan, Command, Range, SlashCommand, }; pub use since_v0_0_4::LanguageServerConfig; @@ -262,13 +263,15 @@ impl Extension { command: &SlashCommand, argument: Option<&str>, resource: Resource>, - ) -> Result, String>> { + ) -> Result> { match self { Extension::V007(ext) => { ext.call_run_slash_command(store, command, argument, resource) .await } - Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(None)), + Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => { + Err(anyhow!("`run_slash_command` not available prior to v0.0.7")) + } } } } diff --git a/crates/extension/src/wasm_host/wit/since_v0_0_7.rs b/crates/extension/src/wasm_host/wit/since_v0_0_7.rs index 35874b6cd6..08a2993f57 100644 --- a/crates/extension/src/wasm_host/wit/since_v0_0_7.rs +++ b/crates/extension/src/wasm_host/wit/since_v0_0_7.rs @@ -98,6 +98,9 @@ impl HostWorktree for WasmState { } } +#[async_trait] +impl common::Host for WasmState {} + #[async_trait] impl nodejs::Host for WasmState { async fn node_binary_path(&mut self) -> wasmtime::Result> { diff --git a/crates/extension_api/src/extension_api.rs b/crates/extension_api/src/extension_api.rs index e117b2b127..a15826f2c1 100644 --- a/crates/extension_api/src/extension_api.rs +++ b/crates/extension_api/src/extension_api.rs @@ -24,7 +24,7 @@ pub use wit::{ npm_package_latest_version, }, zed::extension::platform::{current_platform, Architecture, Os}, - zed::extension::slash_command::SlashCommand, + zed::extension::slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection}, CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command, DownloadedFileType, EnvVars, LanguageServerInstallationStatus, Range, Worktree, }; @@ -114,8 +114,8 @@ pub trait Extension: Send + Sync { _command: SlashCommand, _argument: Option, _worktree: &Worktree, - ) -> Result, String> { - Ok(None) + ) -> Result { + Err("`run_slash_command` not implemented".to_string()) } } @@ -229,7 +229,7 @@ impl wit::Guest for Component { command: SlashCommand, argument: Option, worktree: &Worktree, - ) -> Result, String> { + ) -> Result { extension().run_slash_command(command, argument, worktree) } } diff --git a/crates/extension_api/wit/since_v0.0.7/common.wit b/crates/extension_api/wit/since_v0.0.7/common.wit new file mode 100644 index 0000000000..c4f321f4c7 --- /dev/null +++ b/crates/extension_api/wit/since_v0.0.7/common.wit @@ -0,0 +1,9 @@ +interface common { + /// A (half-open) range (`[start, end)`). + record range { + /// The start of the range (inclusive). + start: u32, + /// The end of the range (exclusive). + end: u32, + } +} diff --git a/crates/extension_api/wit/since_v0.0.7/extension.wit b/crates/extension_api/wit/since_v0.0.7/extension.wit index 6758e5f691..253011194f 100644 --- a/crates/extension_api/wit/since_v0.0.7/extension.wit +++ b/crates/extension_api/wit/since_v0.0.7/extension.wit @@ -5,8 +5,9 @@ world extension { import platform; import nodejs; + use common.{range}; use lsp.{completion, symbol}; - use slash-command.{slash-command}; + use slash-command.{slash-command, slash-command-output}; /// Initializes the extension. export init-extension: func(); @@ -118,17 +119,9 @@ world extension { highlight-name: option, } - /// A (half-open) range (`[start, end)`). - record range { - /// The start of the range (inclusive). - start: u32, - /// The end of the range (exclusive). - end: u32, - } - export labels-for-completions: func(language-server-id: string, completions: list) -> result>, string>; export labels-for-symbols: func(language-server-id: string, symbols: list) -> result>, string>; /// Runs the provided slash command. - export run-slash-command: func(command: slash-command, argument: option, worktree: borrow) -> result, string>; + export run-slash-command: func(command: slash-command, argument: option, worktree: borrow) -> result; } diff --git a/crates/extension_api/wit/since_v0.0.7/slash-command.wit b/crates/extension_api/wit/since_v0.0.7/slash-command.wit index 1be04f3319..17d4a61552 100644 --- a/crates/extension_api/wit/since_v0.0.7/slash-command.wit +++ b/crates/extension_api/wit/since_v0.0.7/slash-command.wit @@ -1,4 +1,6 @@ interface slash-command { + use common.{range}; + /// A slash command for use in the Assistant. record slash-command { /// The name of the slash command. @@ -10,4 +12,20 @@ interface slash-command { /// Whether this slash command requires an argument. requires-argument: bool, } + + /// The output of a slash command. + record slash-command-output { + /// The text produced by the slash command. + text: string, + /// The list of sections to show in the slash command placeholder. + sections: list, + } + + /// A section in the slash command output. + record slash-command-output-section { + /// The range this section occupies. + range: range, + /// The label to display in the placeholder for this section. + label: string, + } } diff --git a/extensions/gleam/src/gleam.rs b/extensions/gleam/src/gleam.rs index d75ac8bc25..1671fdf400 100644 --- a/extensions/gleam/src/gleam.rs +++ b/extensions/gleam/src/gleam.rs @@ -1,6 +1,9 @@ use std::fs; use zed::lsp::CompletionKind; -use zed::{CodeLabel, CodeLabelSpan, LanguageServerId, SlashCommand}; +use zed::{ + CodeLabel, CodeLabelSpan, LanguageServerId, SlashCommand, SlashCommandOutput, + SlashCommandOutputSection, +}; use zed_extension_api::{self as zed, Result}; struct GleamExtension { @@ -148,18 +151,24 @@ impl zed::Extension for GleamExtension { command: SlashCommand, _argument: Option, worktree: &zed::Worktree, - ) -> Result, String> { + ) -> Result { match command.name.as_str() { "gleam-project" => { - let mut message = String::new(); - message.push_str("You are in a Gleam project.\n"); + let mut text = String::new(); + text.push_str("You are in a Gleam project.\n"); if let Some(gleam_toml) = worktree.read_text_file("gleam.toml").ok() { - message.push_str("The `gleam.toml` is as follows:\n"); - message.push_str(&gleam_toml); + text.push_str("The `gleam.toml` is as follows:\n"); + text.push_str(&gleam_toml); } - Ok(Some(message)) + Ok(SlashCommandOutput { + sections: vec![SlashCommandOutputSection { + range: (0..text.len()).into(), + label: "gleam-project".to_string(), + }], + text, + }) } command => Err(format!("unknown slash command: \"{command}\"")), }