mouse_context_menu.rs

  1use context_menu::ContextMenuItem;
  2use gpui::{
  3    elements::AnchorCorner, geometry::vector::Vector2F, impl_internal_actions, AppContext,
  4    ViewContext,
  5};
  6
  7use crate::{
  8    DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
  9    Rename, RevealInFinder, 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 AppContext) {
 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                ContextMenuItem::Separator,
 65                ContextMenuItem::item("Reveal in Finder", RevealInFinder),
 66            ],
 67            cx,
 68        );
 69    });
 70    cx.notify();
 71}
 72
 73#[cfg(test)]
 74mod tests {
 75    use crate::test::editor_lsp_test_context::EditorLspTestContext;
 76
 77    use super::*;
 78    use indoc::indoc;
 79
 80    #[gpui::test]
 81    async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
 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| {
102            deploy_context_menu(
103                editor,
104                &DeployMouseContextMenu {
105                    position: Default::default(),
106                    point,
107                },
108                cx,
109            )
110        });
111
112        cx.assert_editor_state(indoc! {"
113            fn test() {
114                do_wˇork();
115            }
116        "});
117        cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
118    }
119}