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 crate::test::editor_lsp_test_context::EditorLspTestContext;
 74
 75    use super::*;
 76    use indoc::indoc;
 77
 78    #[gpui::test]
 79    async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
 80        let mut cx = EditorLspTestContext::new_rust(
 81            lsp::ServerCapabilities {
 82                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
 83                ..Default::default()
 84            },
 85            cx,
 86        )
 87        .await;
 88
 89        cx.set_state(indoc! {"
 90            fn teˇst() {
 91                do_work();
 92            }
 93        "});
 94        let point = cx.display_point(indoc! {"
 95            fn test() {
 96                do_wˇork();
 97            }
 98        "});
 99        cx.update_editor(|editor, cx| {
100            deploy_context_menu(
101                editor,
102                &DeployMouseContextMenu {
103                    position: Default::default(),
104                    point,
105                },
106                cx,
107            )
108        });
109
110        cx.assert_editor_state(indoc! {"
111            fn test() {
112                do_wˇork();
113            }
114        "});
115        cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
116    }
117}