mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-21 03:18:47 +03:00
Make slash commands defined in extensions return SlashCommandOutput
(#13237)
This PR extends the interface for slash commands defined in extensions to have them return `SlashCommandOutput`. This allows for slash commands to return multiple output sections for a single piece of generated text. Note that we don't allow specifying the icon to display in the placeholder, as we don't want to commit to that in our API at the moment. Release Notes: - N/A
This commit is contained in:
parent
ca18549e02
commit
ad4e52842c
@ -52,11 +52,9 @@ impl SlashCommand for ExtensionSlashCommand {
|
|||||||
delegate: Arc<dyn LspAdapterDelegate>,
|
delegate: Arc<dyn LspAdapterDelegate>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<SlashCommandOutput>> {
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
let command_name = SharedString::from(self.command.name.clone());
|
|
||||||
let argument = argument.map(|arg| arg.to_string());
|
let argument = argument.map(|arg| arg.to_string());
|
||||||
let text = cx.background_executor().spawn(async move {
|
let output = cx.background_executor().spawn(async move {
|
||||||
let output = self
|
self.extension
|
||||||
.extension
|
|
||||||
.call({
|
.call({
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
move |extension, store| {
|
move |extension, store| {
|
||||||
@ -77,19 +75,21 @@ impl SlashCommand for ExtensionSlashCommand {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await?;
|
.await
|
||||||
output.ok_or_else(|| anyhow!("no output from command: {}", self.command.name))
|
|
||||||
});
|
});
|
||||||
cx.foreground_executor().spawn(async move {
|
cx.foreground_executor().spawn(async move {
|
||||||
let text = text.await?;
|
let output = output.await?;
|
||||||
let range = 0..text.len();
|
|
||||||
Ok(SlashCommandOutput {
|
Ok(SlashCommandOutput {
|
||||||
text,
|
text: output.text,
|
||||||
sections: vec![SlashCommandOutputSection {
|
sections: output
|
||||||
range,
|
.sections
|
||||||
icon: IconName::Code,
|
.into_iter()
|
||||||
label: command_name,
|
.map(|section| SlashCommandOutputSection {
|
||||||
}],
|
range: section.range.into(),
|
||||||
|
icon: IconName::Code,
|
||||||
|
label: section.label.into(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
run_commands_in_text: false,
|
run_commands_in_text: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -6,7 +6,7 @@ use release_channel::ReleaseChannel;
|
|||||||
use since_v0_0_7 as latest;
|
use since_v0_0_7 as latest;
|
||||||
|
|
||||||
use super::{wasm_engine, WasmState};
|
use super::{wasm_engine, WasmState};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use language::{LanguageServerName, LspAdapterDelegate};
|
use language::{LanguageServerName, LspAdapterDelegate};
|
||||||
use semantic_version::SemanticVersion;
|
use semantic_version::SemanticVersion;
|
||||||
use std::{ops::RangeInclusive, sync::Arc};
|
use std::{ops::RangeInclusive, sync::Arc};
|
||||||
@ -19,6 +19,7 @@ use wasmtime::{
|
|||||||
pub use latest::CodeLabelSpanLiteral;
|
pub use latest::CodeLabelSpanLiteral;
|
||||||
pub use latest::{
|
pub use latest::{
|
||||||
zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
|
zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
|
||||||
|
zed::extension::slash_command::SlashCommandOutput,
|
||||||
CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
|
CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
|
||||||
};
|
};
|
||||||
pub use since_v0_0_4::LanguageServerConfig;
|
pub use since_v0_0_4::LanguageServerConfig;
|
||||||
@ -262,13 +263,15 @@ impl Extension {
|
|||||||
command: &SlashCommand,
|
command: &SlashCommand,
|
||||||
argument: Option<&str>,
|
argument: Option<&str>,
|
||||||
resource: Resource<Arc<dyn LspAdapterDelegate>>,
|
resource: Resource<Arc<dyn LspAdapterDelegate>>,
|
||||||
) -> Result<Result<Option<String>, String>> {
|
) -> Result<Result<SlashCommandOutput, String>> {
|
||||||
match self {
|
match self {
|
||||||
Extension::V007(ext) => {
|
Extension::V007(ext) => {
|
||||||
ext.call_run_slash_command(store, command, argument, resource)
|
ext.call_run_slash_command(store, command, argument, resource)
|
||||||
.await
|
.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"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,9 @@ impl HostWorktree for WasmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl common::Host for WasmState {}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl nodejs::Host for WasmState {
|
impl nodejs::Host for WasmState {
|
||||||
async fn node_binary_path(&mut self) -> wasmtime::Result<Result<String, String>> {
|
async fn node_binary_path(&mut self) -> wasmtime::Result<Result<String, String>> {
|
||||||
|
@ -24,7 +24,7 @@ pub use wit::{
|
|||||||
npm_package_latest_version,
|
npm_package_latest_version,
|
||||||
},
|
},
|
||||||
zed::extension::platform::{current_platform, Architecture, Os},
|
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,
|
CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command, DownloadedFileType, EnvVars,
|
||||||
LanguageServerInstallationStatus, Range, Worktree,
|
LanguageServerInstallationStatus, Range, Worktree,
|
||||||
};
|
};
|
||||||
@ -114,8 +114,8 @@ pub trait Extension: Send + Sync {
|
|||||||
_command: SlashCommand,
|
_command: SlashCommand,
|
||||||
_argument: Option<String>,
|
_argument: Option<String>,
|
||||||
_worktree: &Worktree,
|
_worktree: &Worktree,
|
||||||
) -> Result<Option<String>, String> {
|
) -> Result<SlashCommandOutput, String> {
|
||||||
Ok(None)
|
Err("`run_slash_command` not implemented".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ impl wit::Guest for Component {
|
|||||||
command: SlashCommand,
|
command: SlashCommand,
|
||||||
argument: Option<String>,
|
argument: Option<String>,
|
||||||
worktree: &Worktree,
|
worktree: &Worktree,
|
||||||
) -> Result<Option<String>, String> {
|
) -> Result<SlashCommandOutput, String> {
|
||||||
extension().run_slash_command(command, argument, worktree)
|
extension().run_slash_command(command, argument, worktree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
crates/extension_api/wit/since_v0.0.7/common.wit
Normal file
9
crates/extension_api/wit/since_v0.0.7/common.wit
Normal file
@ -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,
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,9 @@ world extension {
|
|||||||
import platform;
|
import platform;
|
||||||
import nodejs;
|
import nodejs;
|
||||||
|
|
||||||
|
use common.{range};
|
||||||
use lsp.{completion, symbol};
|
use lsp.{completion, symbol};
|
||||||
use slash-command.{slash-command};
|
use slash-command.{slash-command, slash-command-output};
|
||||||
|
|
||||||
/// Initializes the extension.
|
/// Initializes the extension.
|
||||||
export init-extension: func();
|
export init-extension: func();
|
||||||
@ -118,17 +119,9 @@ world extension {
|
|||||||
highlight-name: option<string>,
|
highlight-name: option<string>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<completion>) -> result<list<option<code-label>>, string>;
|
export labels-for-completions: func(language-server-id: string, completions: list<completion>) -> result<list<option<code-label>>, string>;
|
||||||
export labels-for-symbols: func(language-server-id: string, symbols: list<symbol>) -> result<list<option<code-label>>, string>;
|
export labels-for-symbols: func(language-server-id: string, symbols: list<symbol>) -> result<list<option<code-label>>, string>;
|
||||||
|
|
||||||
/// Runs the provided slash command.
|
/// Runs the provided slash command.
|
||||||
export run-slash-command: func(command: slash-command, argument: option<string>, worktree: borrow<worktree>) -> result<option<string>, string>;
|
export run-slash-command: func(command: slash-command, argument: option<string>, worktree: borrow<worktree>) -> result<slash-command-output, string>;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
interface slash-command {
|
interface slash-command {
|
||||||
|
use common.{range};
|
||||||
|
|
||||||
/// A slash command for use in the Assistant.
|
/// A slash command for use in the Assistant.
|
||||||
record slash-command {
|
record slash-command {
|
||||||
/// The name of the slash command.
|
/// The name of the slash command.
|
||||||
@ -10,4 +12,20 @@ interface slash-command {
|
|||||||
/// Whether this slash command requires an argument.
|
/// Whether this slash command requires an argument.
|
||||||
requires-argument: bool,
|
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<slash-command-output-section>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use zed::lsp::CompletionKind;
|
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};
|
use zed_extension_api::{self as zed, Result};
|
||||||
|
|
||||||
struct GleamExtension {
|
struct GleamExtension {
|
||||||
@ -148,18 +151,24 @@ impl zed::Extension for GleamExtension {
|
|||||||
command: SlashCommand,
|
command: SlashCommand,
|
||||||
_argument: Option<String>,
|
_argument: Option<String>,
|
||||||
worktree: &zed::Worktree,
|
worktree: &zed::Worktree,
|
||||||
) -> Result<Option<String>, String> {
|
) -> Result<SlashCommandOutput, String> {
|
||||||
match command.name.as_str() {
|
match command.name.as_str() {
|
||||||
"gleam-project" => {
|
"gleam-project" => {
|
||||||
let mut message = String::new();
|
let mut text = String::new();
|
||||||
message.push_str("You are in a Gleam project.\n");
|
text.push_str("You are in a Gleam project.\n");
|
||||||
|
|
||||||
if let Some(gleam_toml) = worktree.read_text_file("gleam.toml").ok() {
|
if let Some(gleam_toml) = worktree.read_text_file("gleam.toml").ok() {
|
||||||
message.push_str("The `gleam.toml` is as follows:\n");
|
text.push_str("The `gleam.toml` is as follows:\n");
|
||||||
message.push_str(&gleam_toml);
|
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}\"")),
|
command => Err(format!("unknown slash command: \"{command}\"")),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user