mouse_context_menu.rs

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