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 super::*;
61 use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
62 use indoc::indoc;
63
64 #[gpui::test]
65 async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
66 init_test(cx, |_| {});
67
68 let mut cx = EditorLspTestContext::new_rust(
69 lsp::ServerCapabilities {
70 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
71 ..Default::default()
72 },
73 cx,
74 )
75 .await;
76
77 cx.set_state(indoc! {"
78 fn teˇst() {
79 do_work();
80 }
81 "});
82 let point = cx.display_point(indoc! {"
83 fn test() {
84 do_wˇork();
85 }
86 "});
87 cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
88
89 cx.assert_editor_state(indoc! {"
90 fn test() {
91 do_wˇork();
92 }
93 "});
94 cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
95 }
96}