on master help sign in
sort codeaction by their kind instead of alphabetically
commit8673c1ec0cb4074a1082d4681b655110d5b7facb authorPascal Kuthe <pascal.kuthe@semimod.de> dateFri 7 Oct 2022, 01:44 (4 years ago) parentce39947
3 files changed+70−25
helix-term/src/commands/lsp.rs+62−22 helix-term/src/ui/completion.rs+1−1 helix-term/src/ui/menu.rs+7−2
helix-term/src/commands/lsp.rs+62−22
@@ -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+1−1
@@ -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+7−2
@@ -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 }