mouse_context_menu.rs

  1use crate::{
  2    DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
  3    Rename, RevealInFinder, SelectMode, ToggleCodeActions,
  4};
  5use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
  6
  7pub struct MouseContextMenu {
  8    pub(crate) position: Point<Pixels>,
  9    pub(crate) context_menu: View<ui::ContextMenu>,
 10    _subscription: Subscription,
 11}
 12
 13pub fn deploy_context_menu(
 14    editor: &mut Editor,
 15    position: Point<Pixels>,
 16    point: DisplayPoint,
 17    cx: &mut ViewContext<Editor>,
 18) {
 19    if !editor.is_focused(cx) {
 20        editor.focus(cx);
 21    }
 22
 23    // Don't show context menu for inline editors
 24    if editor.mode() != EditorMode::Full {
 25        return;
 26    }
 27
 28    // Don't show the context menu if there isn't a project associated with this editor
 29    if editor.project.is_none() {
 30        return;
 31    }
 32
 33    // Move the cursor to the clicked location so that dispatched actions make sense
 34    editor.change_selections(None, cx, |s| {
 35        s.clear_disjoint();
 36        s.set_pending_display_range(point..point, SelectMode::Character);
 37    });
 38
 39    let context_menu = ui::ContextMenu::build(cx, |menu, cx| {
 40        menu.action("Rename Symbol", Box::new(Rename))
 41            .action("Go to Definition", Box::new(GoToDefinition))
 42            .action("Go to Type Definition", Box::new(GoToTypeDefinition))
 43            .action("Find All References", Box::new(FindAllReferences))
 44            .action(
 45                "Code Actions",
 46                Box::new(ToggleCodeActions {
 47                    deployed_from_indicator: false,
 48                }),
 49            )
 50            .separator()
 51            .action("Reveal in Finder", Box::new(RevealInFinder))
 52    });
 53    let context_menu_focus = context_menu.focus_handle(cx);
 54    cx.focus(&context_menu_focus);
 55
 56    let _subscription = cx.subscribe(&context_menu, move |this, _, event: &DismissEvent, cx| {
 57        this.mouse_context_menu.take();
 58        if context_menu_focus.contains_focused(cx) {
 59            this.focus(cx);
 60        }
 61    });
 62
 63    editor.mouse_context_menu = Some(MouseContextMenu {
 64        position,
 65        context_menu,
 66        _subscription,
 67    });
 68    cx.notify();
 69}
 70
 71// #[cfg(test)]
 72// mod tests {
 73//     use super::*;
 74//     use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
 75//     use indoc::indoc;
 76
 77//     #[gpui::test]
 78//     async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
 79//         init_test(cx, |_| {});
 80
 81//         let mut cx = EditorLspTestContext::new_rust(
 82//             lsp::ServerCapabilities {
 83//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
 84//                 ..Default::default()
 85//             },
 86//             cx,
 87//         )
 88//         .await;
 89
 90//         cx.set_state(indoc! {"
 91//             fn teˇst() {
 92//                 do_work();
 93//             }
 94//         "});
 95//         let point = cx.display_point(indoc! {"
 96//             fn test() {
 97//                 do_wˇork();
 98//             }
 99//         "});
100//         cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
101
102//         cx.assert_editor_state(indoc! {"
103//             fn test() {
104//                 do_wˇork();
105//             }
106//         "});
107//         cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
108//     }
109// }