mirror of
https://github.com/helix-editor/helix
synced 2024-05-08 11:56:04 +02:00
Compare commits
5 Commits
671747ec7e
...
f366db36d0
Author | SHA1 | Date | |
---|---|---|---|
John Toohey | f366db36d0 | ||
David Else | 109f53fb60 | ||
John Toohey | 20d8eca43f | ||
John Toohey | f0dbe91e08 | ||
John Toohey | 7693b48ef2 |
|
@ -329,6 +329,7 @@ pub fn doc(&self) -> &str {
|
|||
file_picker_in_current_buffer_directory, "Open file picker at current buffers's directory",
|
||||
file_picker_in_current_directory, "Open file picker at current working directory",
|
||||
code_action, "Perform code action",
|
||||
code_action_picker, "Perform code action in a picker",
|
||||
buffer_picker, "Open buffer picker",
|
||||
jumplist_picker, "Open jumplist picker",
|
||||
symbol_picker, "Open symbol picker",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range},
|
||||
Client, LanguageServerId, OffsetEncoding,
|
||||
};
|
||||
|
||||
use tokio_stream::StreamExt;
|
||||
use tui::{
|
||||
text::{Span, Spans},
|
||||
|
@ -568,6 +569,14 @@ fn action_fixes_diagnostics(action: &CodeActionOrCommand) -> bool {
|
|||
}
|
||||
|
||||
pub fn code_action(cx: &mut Context) {
|
||||
code_action_inner(cx, false);
|
||||
}
|
||||
|
||||
pub fn code_action_picker(cx: &mut Context) {
|
||||
code_action_inner(cx, true);
|
||||
}
|
||||
|
||||
pub fn code_action_inner(cx: &mut Context, use_picker: bool) {
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
||||
let selection_range = doc.selection(view.id).primary();
|
||||
|
@ -675,79 +684,117 @@ pub fn code_action(cx: &mut Context) {
|
|||
actions.append(&mut lsp_items);
|
||||
}
|
||||
|
||||
let call = move |editor: &mut Editor, compositor: &mut Compositor| {
|
||||
if actions.is_empty() {
|
||||
editor.set_error("No code actions available");
|
||||
if use_picker {
|
||||
code_action_inner_picker(actions)
|
||||
} else {
|
||||
code_action_inner_menu(actions)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn code_action_inner_menu(
|
||||
actions: Vec<CodeActionOrCommandItem>,
|
||||
) -> Result<Callback, anyhow::Error> {
|
||||
let call = move |editor: &mut Editor, compositor: &mut Compositor| {
|
||||
if actions.is_empty() {
|
||||
editor.set_error("No code actions available");
|
||||
return;
|
||||
}
|
||||
let mut picker = ui::Menu::new(actions, (), move |editor, action, event| {
|
||||
if event != PromptEvent::Validate {
|
||||
return;
|
||||
}
|
||||
let mut picker = ui::Menu::new(actions, (), move |editor, action, event| {
|
||||
if event != PromptEvent::Validate {
|
||||
return;
|
||||
}
|
||||
|
||||
// always present here
|
||||
let action = action.unwrap();
|
||||
let Some(language_server) = editor.language_server_by_id(action.language_server_id)
|
||||
else {
|
||||
editor.set_error("Language Server disappeared");
|
||||
return;
|
||||
};
|
||||
let offset_encoding = language_server.offset_encoding();
|
||||
// always present here
|
||||
let action = action.unwrap();
|
||||
|
||||
match &action.lsp_item {
|
||||
lsp::CodeActionOrCommand::Command(command) => {
|
||||
log::debug!("code action command: {:?}", command);
|
||||
execute_lsp_command(editor, action.language_server_id, command.clone());
|
||||
}
|
||||
lsp::CodeActionOrCommand::CodeAction(code_action) => {
|
||||
log::debug!("code action: {:?}", code_action);
|
||||
// we support lsp "codeAction/resolve" for `edit` and `command` fields
|
||||
let mut resolved_code_action = None;
|
||||
if code_action.edit.is_none() || code_action.command.is_none() {
|
||||
if let Some(future) =
|
||||
language_server.resolve_code_action(code_action.clone())
|
||||
{
|
||||
if let Ok(response) = helix_lsp::block_on(future) {
|
||||
if let Ok(code_action) =
|
||||
serde_json::from_value::<CodeAction>(response)
|
||||
{
|
||||
resolved_code_action = Some(code_action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let resolved_code_action =
|
||||
resolved_code_action.as_ref().unwrap_or(code_action);
|
||||
code_action_handle_lsp_item(editor, &action.lsp_item, action.language_server_id);
|
||||
});
|
||||
picker.move_down(); // pre-select the first item
|
||||
|
||||
if let Some(ref workspace_edit) = resolved_code_action.edit {
|
||||
let _ = editor.apply_workspace_edit(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, action.language_server_id, command.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
picker.move_down(); // pre-select the first item
|
||||
|
||||
let margin = if editor.menu_border() {
|
||||
Margin::vertical(1)
|
||||
} else {
|
||||
Margin::none()
|
||||
};
|
||||
|
||||
let popup = Popup::new("code-action", picker)
|
||||
.with_scrollbar(false)
|
||||
.margin(margin);
|
||||
|
||||
compositor.replace_or_push("code-action", popup);
|
||||
let margin = if editor.menu_border() {
|
||||
Margin::vertical(1)
|
||||
} else {
|
||||
Margin::none()
|
||||
};
|
||||
|
||||
Ok(Callback::EditorCompositor(Box::new(call)))
|
||||
});
|
||||
let popup = Popup::new("code-action", picker)
|
||||
.with_scrollbar(false)
|
||||
.margin(margin);
|
||||
|
||||
compositor.replace_or_push("code-action", popup);
|
||||
};
|
||||
|
||||
Ok(Callback::EditorCompositor(Box::new(call)))
|
||||
}
|
||||
|
||||
fn code_action_inner_picker(
|
||||
actions: Vec<CodeActionOrCommandItem>,
|
||||
) -> Result<Callback, anyhow::Error> {
|
||||
let call = move |editor: &mut Editor, compositor: &mut Compositor| {
|
||||
if actions.is_empty() {
|
||||
editor.set_error("No code actions available");
|
||||
return;
|
||||
}
|
||||
let picker = ui::Picker::new(
|
||||
actions,
|
||||
(),
|
||||
move |cx: &mut crate::compositor::Context, lsp_item, _event| {
|
||||
code_action_handle_lsp_item(
|
||||
cx.editor,
|
||||
&lsp_item.lsp_item,
|
||||
lsp_item.language_server_id,
|
||||
);
|
||||
},
|
||||
);
|
||||
compositor.push(Box::new(overlaid(picker)));
|
||||
};
|
||||
Ok(Callback::EditorCompositor(Box::new(call)))
|
||||
}
|
||||
|
||||
fn code_action_handle_lsp_item(
|
||||
editor: &mut Editor,
|
||||
lsp_item: &CodeActionOrCommand,
|
||||
language_server_id: usize,
|
||||
) {
|
||||
let Some(language_server) = editor.language_server_by_id(language_server_id)
|
||||
else {
|
||||
editor.set_error("Language Server disappeared");
|
||||
return;
|
||||
};
|
||||
let offset_encoding = language_server.offset_encoding();
|
||||
|
||||
match lsp_item {
|
||||
lsp::CodeActionOrCommand::Command(command) => {
|
||||
log::debug!("code action command: {:?}", command);
|
||||
execute_lsp_command(editor, language_server_id, command.clone());
|
||||
}
|
||||
lsp::CodeActionOrCommand::CodeAction(code_action) => {
|
||||
log::debug!("code action: {:?}", code_action);
|
||||
// we support lsp "codeAction/resolve" for `edit` and `command` fields
|
||||
let mut resolved_code_action = None;
|
||||
if code_action.edit.is_none() || code_action.command.is_none() {
|
||||
if let Some(future) = language_server.resolve_code_action(code_action.clone()) {
|
||||
if let Ok(response) = helix_lsp::block_on(future) {
|
||||
if let Ok(code_action) = serde_json::from_value::<CodeAction>(response) {
|
||||
resolved_code_action = Some(code_action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let resolved_code_action = resolved_code_action.as_ref().unwrap_or(code_action);
|
||||
|
||||
if let Some(ref workspace_edit) = resolved_code_action.edit {
|
||||
let _ = editor.apply_workspace_edit(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, language_server_id, command.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ui::menu::Item for lsp::Command {
|
||||
|
|
|
@ -77,6 +77,9 @@
|
|||
"ui.virtual.indent-guide" = { fg = "dark_gray4" }
|
||||
"ui.virtual.inlay-hint" = { fg = "dark_gray5"}
|
||||
"ui.virtual.jump-label" = { fg = "dark_gray", modifiers = ["bold"] }
|
||||
"ui.highlight.frameline" = { bg = "#4b4b18" }
|
||||
"ui.debug.active" = { fg = "#ffcc00" }
|
||||
"ui.debug.breakpoint" = { fg = "#e51400" }
|
||||
"warning" = { fg = "gold2" }
|
||||
"error" = { fg = "red" }
|
||||
"info" = { fg = "light_blue" }
|
||||
|
|
Loading…
Reference in New Issue