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