1
0
Fork 0
mirror of https://github.com/helix-editor/helix synced 2024-05-08 11:56:04 +02:00

Compare commits

...

5 Commits

Author SHA1 Message Date
John Toohey f366db36d0
Merge 20d8eca43f into 109f53fb60 2024-04-26 15:04:23 -06:00
David Else 109f53fb60
Add debug highlights to the dark plus theme (#10593) 2024-04-25 07:48:14 -05:00
John Toohey 20d8eca43f
Make Clippy happy 2024-03-15 18:09:52 +00:00
John Toohey f0dbe91e08
Split code_action to code_action and code_action_picker
Fixes #3502
2024-03-14 20:04:54 +00:00
John Toohey 7693b48ef2
Make code_action use a picker 2024-03-13 19:51:55 +00:00
3 changed files with 117 additions and 66 deletions

View File

@ -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",

View File

@ -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 {

View File

@ -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" }