@@ -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);