mouse_context_menu.rs

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