Support workspace/executeCommand for actions' data (#26239)

Closes https://github.com/zed-industries/zed/issues/16746
Part of https://github.com/zed-extensions/deno/issues/2

Changes the action-related code so, that

* `lsp::Command` as actions are supported, if server replies with them
* actions with commands are filtered out based on servers'
`executeCommandOptions`
(https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#executeCommandOptions)
— commands that are not listed won't be executed and the corresponding
actions will be hidden in Zed

Release Notes:

- Added support of `workspace/executeCommand` for actions' data

---------

Co-authored-by: Peter Tripp <petertripp@gmail.com>
This commit is contained in:
Kirill Bulatov 2025-03-06 23:26:46 +02:00 committed by GitHub
parent 97c0a0a86e
commit af5af9d7c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 217 additions and 87 deletions

View file

@ -2,9 +2,10 @@ mod signature_help;
use crate::{
lsp_store::{LocalLspStore, LspStore},
CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint,
InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,
LocationLink, MarkupContent, PrepareRenameResponse, ProjectTransaction, ResolveState,
ActionVariant, CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock,
HoverBlockKind, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip,
InlayHintTooltip, Location, LocationLink, MarkupContent, PrepareRenameResponse,
ProjectTransaction, ResolveState,
};
use anyhow::{anyhow, Context as _, Result};
use async_trait::async_trait;
@ -2218,10 +2219,10 @@ impl LspCommand for GetCodeActions {
async fn response_from_lsp(
self,
actions: Option<lsp::CodeActionResponse>,
_: Entity<LspStore>,
lsp_store: Entity<LspStore>,
_: Entity<Buffer>,
server_id: LanguageServerId,
_: AsyncApp,
cx: AsyncApp,
) -> Result<Vec<CodeAction>> {
let requested_kinds_set = if let Some(kinds) = self.kinds {
Some(kinds.into_iter().collect::<HashSet<_>>())
@ -2229,18 +2230,47 @@ impl LspCommand for GetCodeActions {
None
};
let language_server = cx.update(|cx| {
lsp_store
.read(cx)
.language_server_for_id(server_id)
.with_context(|| {
format!("Missing the language server that just returned a response {server_id}")
})
})??;
let server_capabilities = language_server.capabilities();
let available_commands = server_capabilities
.execute_command_provider
.as_ref()
.map(|options| options.commands.as_slice())
.unwrap_or_default();
Ok(actions
.unwrap_or_default()
.into_iter()
.filter_map(|entry| {
let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry else {
return None;
let lsp_action = match entry {
lsp::CodeActionOrCommand::CodeAction(lsp_action) => {
if let Some(command) = lsp_action.command.as_ref() {
if !available_commands.contains(&command.command) {
return None;
}
}
ActionVariant::Action(Box::new(lsp_action))
}
lsp::CodeActionOrCommand::Command(command) => {
if available_commands.contains(&command.command) {
ActionVariant::Command(command)
} else {
return None;
}
}
};
if let Some((requested_kinds, kind)) =
requested_kinds_set.as_ref().zip(lsp_action.kind.as_ref())
requested_kinds_set.as_ref().zip(lsp_action.action_kind())
{
if !requested_kinds.contains(kind) {
if !requested_kinds.contains(&kind) {
return None;
}
}