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:
parent
97c0a0a86e
commit
af5af9d7c5
9 changed files with 217 additions and 87 deletions
|
@ -11,8 +11,8 @@ use crate::{
|
|||
toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent},
|
||||
worktree_store::{WorktreeStore, WorktreeStoreEvent},
|
||||
yarn::YarnPathStore,
|
||||
CodeAction, Completion, CoreCompletion, Hover, InlayHint, ProjectItem as _, ProjectPath,
|
||||
ProjectTransaction, ResolveState, Symbol, ToolchainStore,
|
||||
ActionVariant, CodeAction, Completion, CoreCompletion, Hover, InlayHint, ProjectItem as _,
|
||||
ProjectPath, ProjectTransaction, ResolveState, Symbol, ToolchainStore,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use async_trait::async_trait;
|
||||
|
@ -1666,15 +1666,21 @@ impl LocalLspStore {
|
|||
lang_server: &LanguageServer,
|
||||
action: &mut CodeAction,
|
||||
) -> anyhow::Result<()> {
|
||||
if GetCodeActions::can_resolve_actions(&lang_server.capabilities())
|
||||
&& action.lsp_action.data.is_some()
|
||||
&& (action.lsp_action.command.is_none() || action.lsp_action.edit.is_none())
|
||||
{
|
||||
action.lsp_action = lang_server
|
||||
.request::<lsp::request::CodeActionResolveRequest>(action.lsp_action.clone())
|
||||
.await?;
|
||||
match &mut action.lsp_action {
|
||||
ActionVariant::Action(lsp_action) => {
|
||||
if GetCodeActions::can_resolve_actions(&lang_server.capabilities())
|
||||
&& lsp_action.data.is_some()
|
||||
&& (lsp_action.command.is_none() || lsp_action.edit.is_none())
|
||||
{
|
||||
*lsp_action = Box::new(
|
||||
lang_server
|
||||
.request::<lsp::request::CodeActionResolveRequest>(*lsp_action.clone())
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
}
|
||||
ActionVariant::Command(_) => {}
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
}
|
||||
|
||||
|
@ -2102,7 +2108,7 @@ impl LocalLspStore {
|
|||
}
|
||||
|
||||
async fn execute_code_actions_on_servers(
|
||||
this: &WeakEntity<LspStore>,
|
||||
lsp_store: &WeakEntity<LspStore>,
|
||||
adapters_and_servers: &[(Arc<CachedLspAdapter>, Arc<LanguageServer>)],
|
||||
code_actions: Vec<lsp::CodeActionKind>,
|
||||
buffer: &Entity<Buffer>,
|
||||
|
@ -2113,7 +2119,7 @@ impl LocalLspStore {
|
|||
for (lsp_adapter, language_server) in adapters_and_servers.iter() {
|
||||
let code_actions = code_actions.clone();
|
||||
|
||||
let actions = this
|
||||
let actions = lsp_store
|
||||
.update(cx, move |this, cx| {
|
||||
let request = GetCodeActions {
|
||||
range: text::Anchor::MIN..text::Anchor::MAX,
|
||||
|
@ -2129,14 +2135,14 @@ impl LocalLspStore {
|
|||
.await
|
||||
.context("resolving a formatting code action")?;
|
||||
|
||||
if let Some(edit) = action.lsp_action.edit {
|
||||
if let Some(edit) = action.lsp_action.edit() {
|
||||
if edit.changes.is_none() && edit.document_changes.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new = Self::deserialize_workspace_edit(
|
||||
this.upgrade().ok_or_else(|| anyhow!("project dropped"))?,
|
||||
edit,
|
||||
lsp_store.upgrade().context("project dropped")?,
|
||||
edit.clone(),
|
||||
push_to_history,
|
||||
lsp_adapter.clone(),
|
||||
language_server.clone(),
|
||||
|
@ -2146,32 +2152,42 @@ impl LocalLspStore {
|
|||
project_transaction.0.extend(new.0);
|
||||
}
|
||||
|
||||
if let Some(command) = action.lsp_action.command {
|
||||
this.update(cx, |this, _| {
|
||||
if let LspStoreMode::Local(mode) = &mut this.mode {
|
||||
mode.last_workspace_edits_by_language_server
|
||||
.remove(&language_server.server_id());
|
||||
}
|
||||
})?;
|
||||
|
||||
language_server
|
||||
.request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
|
||||
command: command.command,
|
||||
arguments: command.arguments.unwrap_or_default(),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
this.update(cx, |this, _| {
|
||||
if let LspStoreMode::Local(mode) = &mut this.mode {
|
||||
project_transaction.0.extend(
|
||||
if let Some(command) = action.lsp_action.command() {
|
||||
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();
|
||||
if available_commands.contains(&command.command) {
|
||||
lsp_store.update(cx, |lsp_store, _| {
|
||||
if let LspStoreMode::Local(mode) = &mut lsp_store.mode {
|
||||
mode.last_workspace_edits_by_language_server
|
||||
.remove(&language_server.server_id())
|
||||
.unwrap_or_default()
|
||||
.0,
|
||||
)
|
||||
}
|
||||
})?;
|
||||
.remove(&language_server.server_id());
|
||||
}
|
||||
})?;
|
||||
|
||||
language_server
|
||||
.request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
|
||||
command: command.command.clone(),
|
||||
arguments: command.arguments.clone().unwrap_or_default(),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
lsp_store.update(cx, |this, _| {
|
||||
if let LspStoreMode::Local(mode) = &mut this.mode {
|
||||
project_transaction.0.extend(
|
||||
mode.last_workspace_edits_by_language_server
|
||||
.remove(&language_server.server_id())
|
||||
.unwrap_or_default()
|
||||
.0,
|
||||
)
|
||||
}
|
||||
})?;
|
||||
} else {
|
||||
log::warn!("Cannot execute a command {} not listed in the language server capabilities", command.command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3896,17 +3912,17 @@ impl LspStore {
|
|||
self.language_server_for_local_buffer(buffer, action.server_id, cx)
|
||||
.map(|(adapter, server)| (adapter.clone(), server.clone()))
|
||||
}) else {
|
||||
return Task::ready(Ok(Default::default()));
|
||||
return Task::ready(Ok(ProjectTransaction::default()));
|
||||
};
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
LocalLspStore::try_resolve_code_action(&lang_server, &mut action)
|
||||
.await
|
||||
.context("resolving a code action")?;
|
||||
if let Some(edit) = action.lsp_action.edit {
|
||||
if let Some(edit) = action.lsp_action.edit() {
|
||||
if edit.changes.is_some() || edit.document_changes.is_some() {
|
||||
return LocalLspStore::deserialize_workspace_edit(
|
||||
this.upgrade().ok_or_else(|| anyhow!("no app present"))?,
|
||||
edit,
|
||||
edit.clone(),
|
||||
push_to_history,
|
||||
lsp_adapter.clone(),
|
||||
lang_server.clone(),
|
||||
|
@ -3916,31 +3932,41 @@ impl LspStore {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(command) = action.lsp_action.command {
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.as_local_mut()
|
||||
.unwrap()
|
||||
.last_workspace_edits_by_language_server
|
||||
.remove(&lang_server.server_id());
|
||||
})?;
|
||||
if let Some(command) = action.lsp_action.command() {
|
||||
let server_capabilities = lang_server.capabilities();
|
||||
let available_commands = server_capabilities
|
||||
.execute_command_provider
|
||||
.as_ref()
|
||||
.map(|options| options.commands.as_slice())
|
||||
.unwrap_or_default();
|
||||
if available_commands.contains(&command.command) {
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.as_local_mut()
|
||||
.unwrap()
|
||||
.last_workspace_edits_by_language_server
|
||||
.remove(&lang_server.server_id());
|
||||
})?;
|
||||
|
||||
let result = lang_server
|
||||
.request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
|
||||
command: command.command,
|
||||
arguments: command.arguments.unwrap_or_default(),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
let result = lang_server
|
||||
.request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
|
||||
command: command.command.clone(),
|
||||
arguments: command.arguments.clone().unwrap_or_default(),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
|
||||
result?;
|
||||
result?;
|
||||
|
||||
return this.update(&mut cx, |this, _| {
|
||||
this.as_local_mut()
|
||||
.unwrap()
|
||||
.last_workspace_edits_by_language_server
|
||||
.remove(&lang_server.server_id())
|
||||
.unwrap_or_default()
|
||||
});
|
||||
return this.update(&mut cx, |this, _| {
|
||||
this.as_local_mut()
|
||||
.unwrap()
|
||||
.last_workspace_edits_by_language_server
|
||||
.remove(&lang_server.server_id())
|
||||
.unwrap_or_default()
|
||||
});
|
||||
} else {
|
||||
log::warn!("Cannot execute a command {} not listed in the language server capabilities", command.command);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ProjectTransaction::default())
|
||||
|
@ -8158,11 +8184,23 @@ impl LspStore {
|
|||
}
|
||||
|
||||
pub(crate) fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
|
||||
let (kind, lsp_action) = match &action.lsp_action {
|
||||
ActionVariant::Action(code_action) => (
|
||||
proto::code_action::Kind::Action as i32,
|
||||
serde_json::to_vec(code_action).unwrap(),
|
||||
),
|
||||
ActionVariant::Command(command) => (
|
||||
proto::code_action::Kind::Command as i32,
|
||||
serde_json::to_vec(command).unwrap(),
|
||||
),
|
||||
};
|
||||
|
||||
proto::CodeAction {
|
||||
server_id: action.server_id.0 as u64,
|
||||
start: Some(serialize_anchor(&action.range.start)),
|
||||
end: Some(serialize_anchor(&action.range.end)),
|
||||
lsp_action: serde_json::to_vec(&action.lsp_action).unwrap(),
|
||||
lsp_action,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8175,7 +8213,15 @@ impl LspStore {
|
|||
.end
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("invalid end"))?;
|
||||
let lsp_action = serde_json::from_slice(&action.lsp_action)?;
|
||||
let lsp_action = match proto::code_action::Kind::from_i32(action.kind) {
|
||||
Some(proto::code_action::Kind::Action) => {
|
||||
ActionVariant::Action(serde_json::from_slice(&action.lsp_action)?)
|
||||
}
|
||||
Some(proto::code_action::Kind::Command) => {
|
||||
ActionVariant::Command(serde_json::from_slice(&action.lsp_action)?)
|
||||
}
|
||||
None => anyhow::bail!("Unknown action kind {}", action.kind),
|
||||
};
|
||||
Ok(CodeAction {
|
||||
server_id: LanguageServerId(action.server_id as usize),
|
||||
range: start..end,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue