Skip to content

Commit 6b16b9d

Browse files
committed
sort codeaction by their kind instead of alphabetically
1 parent 4d4be0e commit 6b16b9d

File tree

3 files changed

+70
-25
lines changed

3 files changed

+70
-25
lines changed

helix-term/src/commands/lsp.rs

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -452,43 +452,83 @@ pub fn code_action(cx: &mut Context) {
452452
cx.callback(
453453
future,
454454
move |editor, compositor, response: Option<lsp::CodeActionResponse>| {
455-
let actions = match response {
455+
let mut actions = match response {
456456
Some(a) => a,
457457
None => return,
458458
};
459+
459460
if actions.is_empty() {
460461
editor.set_status("No code actions available");
461462
return;
462463
}
463464

464-
let mut picker = ui::Menu::new(actions, (), move |editor, code_action, event| {
465-
if event != PromptEvent::Validate {
466-
return;
467-
}
465+
// sort by CodeActionKind
466+
// this ensures that the most relevant codeactions (quickfix) show up first
467+
// while more situational commands (like refactors) show up later
468+
// this behaviour is modeled after the behaviour of vscode (editor/contrib/codeAction/browser/codeActionWidget.ts)
469+
470+
let mut categories = vec![Vec::new(); 8];
471+
for action in actions.drain(..) {
472+
let category = match &action {
473+
lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
474+
kind: Some(kind),
475+
..
476+
}) => {
477+
let mut components = kind.as_str().split('.');
478+
479+
match components.next() {
480+
Some("quickfix") => 0,
481+
Some("refactor") => match components.next() {
482+
Some("extract") => 1,
483+
Some("inline") => 2,
484+
Some("rewrite") => 3,
485+
Some("move") => 4,
486+
Some("surround") => 5,
487+
_ => 7,
488+
},
489+
Some("source") => 6,
490+
_ => 7,
491+
}
492+
}
493+
_ => 7,
494+
};
495+
496+
categories[category].push(action);
497+
}
468498

469-
// always present here
470-
let code_action = code_action.unwrap();
499+
for category in categories {
500+
actions.extend(category.into_iter())
501+
}
471502

472-
match code_action {
473-
lsp::CodeActionOrCommand::Command(command) => {
474-
log::debug!("code action command: {:?}", command);
475-
execute_lsp_command(editor, command.clone());
503+
let mut picker =
504+
ui::Menu::new(actions, false, (), move |editor, code_action, event| {
505+
if event != PromptEvent::Validate {
506+
return;
476507
}
477-
lsp::CodeActionOrCommand::CodeAction(code_action) => {
478-
log::debug!("code action: {:?}", code_action);
479-
if let Some(ref workspace_edit) = code_action.edit {
480-
log::debug!("edit: {:?}", workspace_edit);
481-
apply_workspace_edit(editor, offset_encoding, workspace_edit);
482-
}
483508

484-
// if code action provides both edit and command first the edit
485-
// should be applied and then the command
486-
if let Some(command) = &code_action.command {
509+
// always present here
510+
let code_action = code_action.unwrap();
511+
512+
match code_action {
513+
lsp::CodeActionOrCommand::Command(command) => {
514+
log::debug!("code action command: {:?}", command);
487515
execute_lsp_command(editor, command.clone());
488516
}
517+
lsp::CodeActionOrCommand::CodeAction(code_action) => {
518+
log::debug!("code action: {:?}", code_action);
519+
if let Some(ref workspace_edit) = code_action.edit {
520+
log::debug!("edit: {:?}", workspace_edit);
521+
apply_workspace_edit(editor, offset_encoding, workspace_edit);
522+
}
523+
524+
// if code action provides both edit and command first the edit
525+
// should be applied and then the command
526+
if let Some(command) = &code_action.command {
527+
execute_lsp_command(editor, command.clone());
528+
}
529+
}
489530
}
490-
}
491-
});
531+
});
492532
picker.move_down(); // pre-select the first item
493533

494534
let popup = Popup::new("code-action", picker);

helix-term/src/ui/completion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl Completion {
9797
start_offset: usize,
9898
trigger_offset: usize,
9999
) -> Self {
100-
let menu = Menu::new(items, (), move |editor: &mut Editor, item, event| {
100+
let menu = Menu::new(items, true, (), move |editor: &mut Editor, item, event| {
101101
fn item_to_transaction(
102102
doc: &Document,
103103
item: &CompletionItem,

helix-term/src/ui/menu.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl<T: Item> Menu<T> {
7474
// rendering)
7575
pub fn new(
7676
options: Vec<T>,
77+
sort: bool,
7778
editor_data: <T as Item>::Data,
7879
callback_fn: impl Fn(&mut Editor, Option<&T>, MenuEvent) + 'static,
7980
) -> Self {
@@ -91,8 +92,12 @@ impl<T: Item> Menu<T> {
9192
recalculate: true,
9293
};
9394

94-
// TODO: scoring on empty input should just use a fastpath
95-
menu.score("");
95+
if sort {
96+
// TODO: scoring on empty input should just use a fastpath
97+
menu.score("");
98+
} else {
99+
menu.matches = (0..menu.options.len()).map(|i| (i, 0)).collect();
100+
}
96101

97102
menu
98103
}

0 commit comments

Comments
 (0)