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), cx)
 41            .action("Go to Definition", Box::new(GoToDefinition), cx)
 42            .action("Go to Type Definition", Box::new(GoToTypeDefinition), cx)
 43            .action("Find All References", Box::new(FindAllReferences), cx)
 44            .action(
 45                "Code Actions",
 46                Box::new(ToggleCodeActions {
 47                    deployed_from_indicator: false,
 48                }),
 49                cx,
 50            )
 51            .separator()
 52            .action("Reveal in Finder", Box::new(RevealInFinder), cx)
 53    });
 54    let context_menu_focus = context_menu.focus_handle(cx);
 55    cx.focus(&context_menu_focus);
 56
 57    let _subscription = cx.subscribe(&context_menu, move |this, _, event: &DismissEvent, cx| {
 58        this.mouse_context_menu.take();
 59        if context_menu_focus.contains_focused(cx) {
 60            this.focus(cx);
 61        }
 62    });
 63
 64    editor.mouse_context_menu = Some(MouseContextMenu {
 65        position,
 66        context_menu,
 67        _subscription,
 68    });
 69    cx.notify();
 70}
 71
 72// #[cfg(test)]
 73// mod tests {
 74//     use super::*;
 75//     use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
 76//     use indoc::indoc;
 77
 78//     #[gpui::test]
 79//     async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
 80//         init_test(cx, |_| {});
 81
 82//         let mut cx = EditorLspTestContext::new_rust(
 83//             lsp::ServerCapabilities {
 84//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
 85//                 ..Default::default()
 86//             },
 87//             cx,
 88//         )
 89//         .await;
 90
 91//         cx.set_state(indoc! {"
 92//             fn teˇst() {
 93//                 do_work();
 94//             }
 95//         "});
 96//         let point = cx.display_point(indoc! {"
 97//             fn test() {
 98//                 do_wˇork();
 99//             }
100//         "});
101//         cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
102
103//         cx.assert_editor_state(indoc! {"
104//             fn test() {
105//                 do_wˇork();
106//             }
107//         "});
108//         cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
109//     }
110// }