@@ -452,43 +452,83 @@ pub fn code_action(cx: &mut Context) {
452 cx.callback(
452 cx.callback(
454 move |editor, compositor, response: Option<lsp::CodeActionResponse>| {
454 move |editor, compositor, response: Option<lsp::CodeActionResponse>| {
455− let actions = match response {
455+ let mut actions = match response {
456 Some(a) => a,
456 Some(a) => a,
457 None => return,
457 None => return,
459 if actions.is_empty() {
460 if actions.is_empty() {
460 editor.set_status("No code actions available");
461 editor.set_status("No code actions available");
464− let mut picker = ui::Menu::new(actions, (), move |editor, code_action, event| {
465+ // sort by CodeActionKind
465− if event != PromptEvent::Validate {
466+ // this ensures that the most relevant codeactions (quickfix) show up first
466− return;
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)
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 {
477+ let mut components = kind.as_str().split('.');
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,
486+ Some("surround") => 5,
489+ Some("source") => 6,
496+ categories[category].push(action);
469− // always present here
499+ for category in categories {
470− let code_action = code_action.unwrap();
500+ actions.extend(category.into_iter())
472− match code_action {
503+ let mut picker =
473− lsp::CodeActionOrCommand::Command(command) => {
504+ ui::Menu::new(actions, false, (), move |editor, code_action, event| {
474− log::debug!("code action command: {:?}", command);
505+ if event != PromptEvent::Validate {
475− execute_lsp_command(editor, command.clone());
506+ return;
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);
484− // if code action provides both edit and command first the edit
509+ // always present here
485− // should be applied and then the command
486− if let Some(command) = &code_action.command {
510+ let code_action = code_action.unwrap();
513+ lsp::CodeActionOrCommand::Command(command) => {
514+ log::debug!("code action command: {:?}", command);
487 execute_lsp_command(editor, command.clone());
515 execute_lsp_command(editor, command.clone());
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);
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());
492 picker.move_down(); // pre-select the first item
532 picker.move_down(); // pre-select the first item
494 let popup = Popup::new("code-action", picker);
534 let popup = Popup::new("code-action", picker);