LSP code action commands (#1304)

* feat(lsp): codeAction commands

* Don't block on command call

* Fix lifetime of command execution

* Fix lint issues
pull/1330/head
Matouš Dzivjak 3 years ago committed by GitHub
parent 600ce70cf6
commit 75a8b789d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -202,7 +202,7 @@ impl Client {
Ok(result) => Output::Success(Success { Ok(result) => Output::Success(Success {
jsonrpc: Some(Version::V2), jsonrpc: Some(Version::V2),
id, id,
result, result: serde_json::to_value(result)?,
}), }),
Err(error) => Output::Failure(Failure { Err(error) => Output::Failure(Failure {
jsonrpc: Some(Version::V2), jsonrpc: Some(Version::V2),
@ -800,4 +800,16 @@ impl Client {
let response = self.request::<lsp::request::Rename>(params).await?; let response = self.request::<lsp::request::Rename>(params).await?;
Ok(response.unwrap_or_default()) Ok(response.unwrap_or_default())
} }
pub fn command(&self, command: lsp::Command) -> impl Future<Output = Result<Value>> {
let params = lsp::ExecuteCommandParams {
command: command.command,
arguments: command.arguments.unwrap_or_default(),
work_done_progress_params: lsp::WorkDoneProgressParams {
work_done_token: None,
},
};
self.call::<lsp::request::ExecuteCommand>(params)
}
} }

@ -203,6 +203,7 @@ pub mod util {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum MethodCall { pub enum MethodCall {
WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams), WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
} }
impl MethodCall { impl MethodCall {
@ -215,6 +216,12 @@ impl MethodCall {
.expect("Failed to parse WorkDoneCreate params"); .expect("Failed to parse WorkDoneCreate params");
Self::WorkDoneProgressCreate(params) Self::WorkDoneProgressCreate(params)
} }
lsp::request::ApplyWorkspaceEdit::METHOD => {
let params: lsp::ApplyWorkspaceEditParams = params
.parse()
.expect("Failed to parse ApplyWorkspaceEdit params");
Self::ApplyWorkspaceEdit(params)
}
_ => { _ => {
log::warn!("unhandled lsp request: {}", method); log::warn!("unhandled lsp request: {}", method);
return None; return None;

@ -1,8 +1,12 @@
use helix_core::{merge_toml_values, syntax}; use helix_core::{merge_toml_values, syntax};
use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap}; use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
use helix_view::{theme, Editor}; use helix_view::{theme, Editor};
use serde_json::json;
use crate::{args::Args, compositor::Compositor, config::Config, job::Jobs, ui}; use crate::{
args::Args, commands::apply_workspace_edit, compositor::Compositor, config::Config, job::Jobs,
ui,
};
use log::{error, warn}; use log::{error, warn};
@ -530,14 +534,6 @@ impl Application {
Call::MethodCall(helix_lsp::jsonrpc::MethodCall { Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
method, params, id, .. method, params, id, ..
}) => { }) => {
let language_server = match self.editor.language_servers.get_by_id(server_id) {
Some(language_server) => language_server,
None => {
warn!("can't find language server with id `{}`", server_id);
return;
}
};
let call = match MethodCall::parse(&method, params) { let call = match MethodCall::parse(&method, params) {
Some(call) => call, Some(call) => call,
None => { None => {
@ -567,8 +563,42 @@ impl Application {
if spinner.is_stopped() { if spinner.is_stopped() {
spinner.start(); spinner.start();
} }
let language_server =
match self.editor.language_servers.get_by_id(server_id) {
Some(language_server) => language_server,
None => {
warn!("can't find language server with id `{}`", server_id);
return;
}
};
tokio::spawn(language_server.reply(id, Ok(serde_json::Value::Null))); tokio::spawn(language_server.reply(id, Ok(serde_json::Value::Null)));
} }
MethodCall::ApplyWorkspaceEdit(params) => {
apply_workspace_edit(
&mut self.editor,
helix_lsp::OffsetEncoding::Utf8,
&params.edit,
);
let language_server =
match self.editor.language_servers.get_by_id(server_id) {
Some(language_server) => language_server,
None => {
warn!("can't find language server with id `{}`", server_id);
return;
}
};
tokio::spawn(language_server.reply(
id,
Ok(json!(lsp::ApplyWorkspaceEditResponse {
applied: true,
failure_reason: None,
failed_change: None,
})),
));
}
} }
} }
e => unreachable!("{:?}", e), e => unreachable!("{:?}", e),

@ -3277,12 +3277,19 @@ pub fn code_action(cx: &mut Context) {
move |editor, code_action, _action| match code_action { move |editor, code_action, _action| match code_action {
lsp::CodeActionOrCommand::Command(command) => { lsp::CodeActionOrCommand::Command(command) => {
log::debug!("code action command: {:?}", command); log::debug!("code action command: {:?}", command);
editor.set_error(String::from("Handling code action command is not implemented yet, see https://github.com/helix-editor/helix/issues/183")); execute_lsp_command(editor, command.clone());
} }
lsp::CodeActionOrCommand::CodeAction(code_action) => { lsp::CodeActionOrCommand::CodeAction(code_action) => {
log::debug!("code action: {:?}", code_action); log::debug!("code action: {:?}", code_action);
if let Some(ref workspace_edit) = code_action.edit { if let Some(ref workspace_edit) = code_action.edit {
apply_workspace_edit(editor, offset_encoding, workspace_edit) log::debug!("edit: {:?}", workspace_edit);
apply_workspace_edit(editor, offset_encoding, workspace_edit);
}
// if code action provides both edit and command first the edit
// should be applied and then the command
if let Some(command) = &code_action.command {
execute_lsp_command(editor, command.clone());
} }
} }
}, },
@ -3293,6 +3300,26 @@ pub fn code_action(cx: &mut Context) {
) )
} }
pub fn execute_lsp_command(editor: &mut Editor, cmd: lsp::Command) {
let (_view, doc) = current!(editor);
let language_server = match doc.language_server() {
Some(language_server) => language_server,
None => return,
};
// the command is executed on the server and communicated back
// to the client asynchronously using workspace edits
let command_future = language_server.command(cmd);
tokio::spawn(async move {
let res = command_future.await;
if let Err(e) = res {
log::error!("execute LSP command: {}", e);
}
});
}
pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> { pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> {
use lsp::ResourceOp; use lsp::ResourceOp;
use std::fs; use std::fs;
@ -3346,7 +3373,7 @@ pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> {
} }
} }
fn apply_workspace_edit( pub fn apply_workspace_edit(
editor: &mut Editor, editor: &mut Editor,
offset_encoding: OffsetEncoding, offset_encoding: OffsetEncoding,
workspace_edit: &lsp::WorkspaceEdit, workspace_edit: &lsp::WorkspaceEdit,

Loading…
Cancel
Save