From ba588161d9e0f36efeffec313ad31172235ad4cb Mon Sep 17 00:00:00 2001 From: Smit Barmase Date: Thu, 17 Apr 2025 23:48:51 +0530 Subject: [PATCH] editor: Revert flattening of code actions in mouse context menu (#28988) In light of making context not move dynamically, reverting back these changes. - Doing it async will lead to a loading state, which moves the context menu. - Doing it sync introduces noticeable lag in opening the context menu. Future idea is to introduce fixed code actions like refactor, rewrite, etc depending on code action kind [(see more)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeActionKind) which will use submenus. Release Notes: - N/A --- crates/collab/src/tests/editor_tests.rs | 10 +- crates/editor/src/actions.rs | 3 - crates/editor/src/code_context_menus.rs | 12 +- crates/editor/src/editor.rs | 238 ++++++++----------- crates/editor/src/mouse_context_menu.rs | 300 +++++------------------- 5 files changed, 170 insertions(+), 393 deletions(-) diff --git a/crates/collab/src/tests/editor_tests.rs b/crates/collab/src/tests/editor_tests.rs index 8a039da882..719b8643f2 100644 --- a/crates/collab/src/tests/editor_tests.rs +++ b/crates/collab/src/tests/editor_tests.rs @@ -694,15 +694,7 @@ async fn test_collaborating_with_code_actions( // Confirming the code action will trigger a resolve request. let confirm_action = editor_b .update_in(cx_b, |editor, window, cx| { - Editor::confirm_code_action( - editor, - &ConfirmCodeAction { - item_ix: Some(0), - from_mouse_context_menu: false, - }, - window, - cx, - ) + Editor::confirm_code_action(editor, &ConfirmCodeAction { item_ix: Some(0) }, window, cx) }) .unwrap(); fake_language_server.set_request_handler::( diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 2adeb6442a..9726740f17 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -99,9 +99,6 @@ pub struct ComposeCompletion { pub struct ConfirmCodeAction { #[serde(default)] pub item_ix: Option, - #[serde(default)] - #[serde(skip)] - pub from_mouse_context_menu: bool, } #[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)] diff --git a/crates/editor/src/code_context_menus.rs b/crates/editor/src/code_context_menus.rs index 86af09309c..6637fe43c4 100644 --- a/crates/editor/src/code_context_menus.rs +++ b/crates/editor/src/code_context_menus.rs @@ -775,7 +775,7 @@ pub struct AvailableCodeAction { pub provider: Rc, } -#[derive(Clone, Default)] +#[derive(Clone)] pub struct CodeActionContents { pub tasks: Option>, pub actions: Option>, @@ -791,7 +791,7 @@ impl CodeActionContents { } } - pub fn is_empty(&self) -> bool { + fn is_empty(&self) -> bool { match (&self.tasks, &self.actions) { (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(), (Some(tasks), None) => tasks.templates.is_empty(), @@ -800,7 +800,7 @@ impl CodeActionContents { } } - pub fn iter(&self) -> impl Iterator + '_ { + fn iter(&self) -> impl Iterator + '_ { self.tasks .iter() .flat_map(|tasks| { @@ -868,14 +868,14 @@ pub enum CodeActionsItem { } impl CodeActionsItem { - pub fn as_task(&self) -> Option<&ResolvedTask> { + fn as_task(&self) -> Option<&ResolvedTask> { let Self::Task(_, task) = self else { return None; }; Some(task) } - pub fn as_code_action(&self) -> Option<&CodeAction> { + fn as_code_action(&self) -> Option<&CodeAction> { let Self::CodeAction { action, .. } = self else { return None; }; @@ -1015,7 +1015,6 @@ impl CodeActionsMenu { if let Some(task) = editor.confirm_code_action( &ConfirmCodeAction { item_ix: Some(item_ix), - from_mouse_context_menu: false, }, window, cx, @@ -1041,7 +1040,6 @@ impl CodeActionsMenu { if let Some(task) = editor.confirm_code_action( &ConfirmCodeAction { item_ix: Some(item_ix), - from_mouse_context_menu: false, }, window, cx, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4e8b6e1e45..55f9ad2620 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1790,7 +1790,6 @@ impl Editor { self, crate::mouse_context_menu::MenuPosition::PinnedToScreen(position), context_menu, - None, window, cx, )); @@ -4933,89 +4932,6 @@ impl Editor { })) } - fn prepare_code_actions_task( - &mut self, - action: &ToggleCodeActions, - window: &mut Window, - cx: &mut Context, - ) -> Task, CodeActionContents)>> { - let snapshot = self.snapshot(window, cx); - let multibuffer_point = action - .deployed_from_indicator - .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot)) - .unwrap_or_else(|| self.selections.newest::(cx).head()); - - let Some((buffer, buffer_row)) = snapshot - .buffer_snapshot - .buffer_line_for_row(MultiBufferRow(multibuffer_point.row)) - .and_then(|(buffer_snapshot, range)| { - self.buffer - .read(cx) - .buffer(buffer_snapshot.remote_id()) - .map(|buffer| (buffer, range.start.row)) - }) - else { - return Task::ready(None); - }; - - let (_, code_actions) = self - .available_code_actions - .clone() - .and_then(|(location, code_actions)| { - let snapshot = location.buffer.read(cx).snapshot(); - let point_range = location.range.to_point(&snapshot); - let point_range = point_range.start.row..=point_range.end.row; - if point_range.contains(&buffer_row) { - Some((location, code_actions)) - } else { - None - } - }) - .unzip(); - - let buffer_id = buffer.read(cx).remote_id(); - let tasks = self - .tasks - .get(&(buffer_id, buffer_row)) - .map(|t| Arc::new(t.to_owned())); - - if tasks.is_none() && code_actions.is_none() { - return Task::ready(None); - } - - self.completion_tasks.clear(); - self.discard_inline_completion(false, cx); - - let task_context = tasks - .as_ref() - .zip(self.project.clone()) - .map(|(tasks, project)| { - Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx) - }); - - cx.spawn_in(window, async move |_, _| { - let task_context = match task_context { - Some(task_context) => task_context.await, - None => None, - }; - let resolved_tasks = tasks.zip(task_context).map(|(tasks, task_context)| { - Rc::new(ResolvedTasks { - templates: tasks.resolve(&task_context).collect(), - position: snapshot - .buffer_snapshot - .anchor_before(Point::new(multibuffer_point.row, tasks.column)), - }) - }); - Some(( - buffer, - CodeActionContents { - actions: code_actions, - tasks: resolved_tasks, - }, - )) - }) - } - pub fn toggle_code_actions( &mut self, action: &ToggleCodeActions, @@ -5036,58 +4952,113 @@ impl Editor { } } drop(context_menu); - + let snapshot = self.snapshot(window, cx); let deployed_from_indicator = action.deployed_from_indicator; let mut task = self.code_actions_task.take(); let action = action.clone(); - cx.spawn_in(window, async move |editor, cx| { while let Some(prev_task) = task { prev_task.await.log_err(); task = editor.update(cx, |this, _| this.code_actions_task.take())?; } - let context_menu_task = editor.update_in(cx, |editor, window, cx| { - if !editor.focus_handle.is_focused(window) { - return Some(Task::ready(Ok(()))); - } - let debugger_flag = cx.has_flag::(); - let code_actions_task = editor.prepare_code_actions_task(&action, window, cx); - Some(cx.spawn_in(window, async move |editor, cx| { - if let Some((buffer, code_action_contents)) = code_actions_task.await { - let spawn_straight_away = - code_action_contents.tasks.as_ref().map_or(false, |tasks| { - tasks - .templates - .iter() - .filter(|task| { - if matches!(task.1.task_type(), task::TaskType::Debug(_)) { - debugger_flag - } else { - true - } - }) - .count() - == 1 - }) && code_action_contents - .actions - .as_ref() - .map_or(true, |actions| actions.is_empty()); + let spawned_test_task = editor.update_in(cx, |editor, window, cx| { + if editor.focus_handle.is_focused(window) { + let multibuffer_point = action + .deployed_from_indicator + .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot)) + .unwrap_or_else(|| editor.selections.newest::(cx).head()); + let (buffer, buffer_row) = snapshot + .buffer_snapshot + .buffer_line_for_row(MultiBufferRow(multibuffer_point.row)) + .and_then(|(buffer_snapshot, range)| { + editor + .buffer + .read(cx) + .buffer(buffer_snapshot.remote_id()) + .map(|buffer| (buffer, range.start.row)) + })?; + let (_, code_actions) = editor + .available_code_actions + .clone() + .and_then(|(location, code_actions)| { + let snapshot = location.buffer.read(cx).snapshot(); + let point_range = location.range.to_point(&snapshot); + let point_range = point_range.start.row..=point_range.end.row; + if point_range.contains(&buffer_row) { + Some((location, code_actions)) + } else { + None + } + }) + .unzip(); + let buffer_id = buffer.read(cx).remote_id(); + let tasks = editor + .tasks + .get(&(buffer_id, buffer_row)) + .map(|t| Arc::new(t.to_owned())); + if tasks.is_none() && code_actions.is_none() { + return None; + } + + editor.completion_tasks.clear(); + editor.discard_inline_completion(false, cx); + let task_context = + tasks + .as_ref() + .zip(editor.project.clone()) + .map(|(tasks, project)| { + Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx) + }); + + let debugger_flag = cx.has_flag::(); + + Some(cx.spawn_in(window, async move |editor, cx| { + let task_context = match task_context { + Some(task_context) => task_context.await, + None => None, + }; + let resolved_tasks = + tasks.zip(task_context).map(|(tasks, task_context)| { + Rc::new(ResolvedTasks { + templates: tasks.resolve(&task_context).collect(), + position: snapshot.buffer_snapshot.anchor_before(Point::new( + multibuffer_point.row, + tasks.column, + )), + }) + }); + let spawn_straight_away = resolved_tasks.as_ref().map_or(false, |tasks| { + tasks + .templates + .iter() + .filter(|task| { + if matches!(task.1.task_type(), task::TaskType::Debug(_)) { + debugger_flag + } else { + true + } + }) + .count() + == 1 + }) && code_actions + .as_ref() + .map_or(true, |actions| actions.is_empty()); if let Ok(task) = editor.update_in(cx, |editor, window, cx| { *editor.context_menu.borrow_mut() = Some(CodeContextMenu::CodeActions(CodeActionsMenu { buffer, - actions: code_action_contents, + actions: CodeActionContents { + tasks: resolved_tasks, + actions: code_actions, + }, selected_item: Default::default(), scroll_handle: UniformListScrollHandle::default(), deployed_from_indicator, })); if spawn_straight_away { if let Some(task) = editor.confirm_code_action( - &ConfirmCodeAction { - item_ix: Some(0), - from_mouse_context_menu: false, - }, + &ConfirmCodeAction { item_ix: Some(0) }, window, cx, ) { @@ -5102,12 +5073,12 @@ impl Editor { } else { Ok(()) } - } else { - Ok(()) - } - })) + })) + } else { + Some(Task::ready(Ok(()))) + } })?; - if let Some(task) = context_menu_task { + if let Some(task) = spawned_test_task { task.await?; } @@ -5124,27 +5095,17 @@ impl Editor { ) -> Option>> { self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction); - let (action, buffer) = if action.from_mouse_context_menu { - if let Some(menu) = self.mouse_context_menu.take() { - let code_action = menu.code_action?; - let index = action.item_ix?; - let action = code_action.actions.get(index)?; - (action, code_action.buffer) - } else { - return None; - } - } else { + let actions_menu = if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? { - let action_ix = action.item_ix.unwrap_or(menu.selected_item); - let action = menu.actions.get(action_ix)?; - let buffer = menu.buffer; - (action, buffer) + menu } else { return None; - } - }; + }; + let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item); + let action = actions_menu.actions.get(action_ix)?; let title = action.label(); + let buffer = actions_menu.buffer; let workspace = self.workspace()?; match action { @@ -8941,7 +8902,6 @@ impl Editor { self, source, clicked_point, - None, context_menu, window, cx, diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index e7453f59bd..9b0017b7ce 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -1,22 +1,15 @@ use crate::{ - ConfirmCodeAction, Copy, CopyAndTrim, CopyPermalinkToLine, Cut, DebuggerEvaluateSelectedText, - DisplayPoint, DisplaySnapshot, Editor, FindAllReferences, GoToDeclaration, GoToDefinition, + Copy, CopyAndTrim, CopyPermalinkToLine, Cut, DebuggerEvaluateSelectedText, DisplayPoint, + DisplaySnapshot, Editor, FindAllReferences, GoToDeclaration, GoToDefinition, GoToImplementation, GoToTypeDefinition, Paste, Rename, RevealInFileManager, SelectMode, SelectionExt, ToDisplayPoint, ToggleCodeActions, actions::{Format, FormatSelections}, - code_context_menus::CodeActionContents, selections_collection::SelectionsCollection, }; -use feature_flags::{Debugger, FeatureFlagAppExt as _}; use gpui::prelude::FluentBuilder; -use gpui::{ - Context, DismissEvent, Entity, FocusHandle, Focusable as _, Pixels, Point, Subscription, Task, - Window, -}; +use gpui::{Context, DismissEvent, Entity, Focusable as _, Pixels, Point, Subscription, Window}; use std::ops::Range; use text::PointUtf16; -use ui::ContextMenu; -use util::ResultExt; use workspace::OpenInTerminal; #[derive(Debug)] @@ -32,24 +25,13 @@ pub enum MenuPosition { }, } -pub struct MouseCodeAction { - pub actions: CodeActionContents, - pub buffer: Entity, -} - pub struct MouseContextMenu { pub(crate) position: MenuPosition, pub(crate) context_menu: Entity, - pub(crate) code_action: Option, _dismiss_subscription: Subscription, _cursor_move_subscription: Subscription, } -enum CodeActionLoadState { - Loading, - Loaded(CodeActionContents), -} - impl std::fmt::Debug for MouseContextMenu { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("MouseContextMenu") @@ -64,7 +46,6 @@ impl MouseContextMenu { editor: &mut Editor, source: multi_buffer::Anchor, position: Point, - code_action: Option, context_menu: Entity, window: &mut Window, cx: &mut Context, @@ -84,7 +65,6 @@ impl MouseContextMenu { editor, menu_position, context_menu, - code_action, window, cx, )); @@ -94,7 +74,6 @@ impl MouseContextMenu { editor: &Editor, position: MenuPosition, context_menu: Entity, - code_action: Option, window: &mut Window, cx: &mut Context, ) -> Self { @@ -141,7 +120,6 @@ impl MouseContextMenu { Self { position, context_menu, - code_action, _dismiss_subscription, _cursor_move_subscription, } @@ -181,13 +159,13 @@ pub fn deploy_context_menu( let display_map = editor.selections.display_map(cx); let source_anchor = display_map.display_point_to_anchor(point, text::Bias::Right); - if let Some(custom) = editor.custom_context_menu.take() { + let context_menu = if let Some(custom) = editor.custom_context_menu.take() { let menu = custom(editor, point, window, cx); editor.custom_context_menu = Some(custom); let Some(menu) = menu else { return; }; - set_context_menu(editor, menu, source_anchor, position, None, window, cx); + menu } else { // Don't show the context menu if there isn't a project associated with this editor let Some(project) = editor.project.clone() else { @@ -226,222 +204,75 @@ pub fn deploy_context_menu( !filter.is_hidden(&DebuggerEvaluateSelectedText) }); - let menu = build_context_menu( - focus, - has_selections, - has_reveal_target, - has_git_repo, - evaluate_selection, - Some(CodeActionLoadState::Loading), - window, - cx, - ); - - set_context_menu(editor, menu, source_anchor, position, None, window, cx); - - let mut actions_task = editor.code_actions_task.take(); - cx.spawn_in(window, async move |editor, cx| { - while let Some(prev_task) = actions_task { - prev_task.await.log_err(); - actions_task = editor.update(cx, |this, _| this.code_actions_task.take())?; - } - let action = ToggleCodeActions { - deployed_from_indicator: Some(point.row()), - }; - let context_menu_task = editor.update_in(cx, |editor, window, cx| { - let code_actions_task = editor.prepare_code_actions_task(&action, window, cx); - Some(cx.spawn_in(window, async move |editor, cx| { - let code_action_result = code_actions_task.await; - if let Ok(editor_task) = editor.update_in(cx, |editor, window, cx| { - let Some(mouse_context_menu) = editor.mouse_context_menu.take() else { - return Task::ready(Ok::<_, anyhow::Error>(())); - }; - if mouse_context_menu - .context_menu - .focus_handle(cx) - .contains_focused(window, cx) - { - window.focus(&editor.focus_handle(cx)); - } - drop(mouse_context_menu); - let (state, code_action) = - if let Some((buffer, actions)) = code_action_result { - ( - CodeActionLoadState::Loaded(actions.clone()), - Some(MouseCodeAction { actions, buffer }), - ) - } else { - ( - CodeActionLoadState::Loaded(CodeActionContents::default()), - None, - ) - }; - let menu = build_context_menu( - window.focused(cx), - has_selections, - has_reveal_target, - has_git_repo, - evaluate_selection, - Some(state), - window, - cx, - ); - set_context_menu( - editor, - menu, - source_anchor, - position, - code_action, - window, - cx, - ); - Task::ready(Ok(())) - }) { - editor_task.await + ui::ContextMenu::build(window, cx, |menu, _window, _cx| { + let builder = menu + .on_blur_subscription(Subscription::new(|| {})) + .action( + "Show Code Actions", + Box::new(ToggleCodeActions { + deployed_from_indicator: None, + }), + ) + .separator() + .when(evaluate_selection && has_selections, |builder| { + builder + .action("Evaluate Selection", Box::new(DebuggerEvaluateSelectedText)) + .separator() + }) + .action("Go to Definition", Box::new(GoToDefinition)) + .action("Go to Declaration", Box::new(GoToDeclaration)) + .action("Go to Type Definition", Box::new(GoToTypeDefinition)) + .action("Go to Implementation", Box::new(GoToImplementation)) + .action("Find All References", Box::new(FindAllReferences)) + .separator() + .action("Rename Symbol", Box::new(Rename)) + .action("Format Buffer", Box::new(Format)) + .when(has_selections, |cx| { + cx.action("Format Selections", Box::new(FormatSelections)) + }) + .separator() + .action("Cut", Box::new(Cut)) + .action("Copy", Box::new(Copy)) + .action("Copy and trim", Box::new(CopyAndTrim)) + .action("Paste", Box::new(Paste)) + .separator() + .map(|builder| { + let reveal_in_finder_label = if cfg!(target_os = "macos") { + "Reveal in Finder" } else { - Ok(()) - } - })) - })?; - if let Some(task) = context_menu_task { - task.await?; - } - Ok::<_, anyhow::Error>(()) - }) - .detach_and_log_err(cx); - }; -} - -fn build_context_menu( - focus: Option, - has_selections: bool, - has_reveal_target: bool, - has_git_repo: bool, - evaluate_selection: bool, - code_action_load_state: Option, - window: &mut Window, - cx: &mut Context, -) -> Entity { - ui::ContextMenu::build(window, cx, |menu, _window, cx| { - let menu = menu - .on_blur_subscription(Subscription::new(|| {})) - .when(evaluate_selection && has_selections, |builder| { - builder - .action("Evaluate Selection", Box::new(DebuggerEvaluateSelectedText)) - .separator() - }) - .action("Go to Definition", Box::new(GoToDefinition)) - .action("Go to Declaration", Box::new(GoToDeclaration)) - .action("Go to Type Definition", Box::new(GoToTypeDefinition)) - .action("Go to Implementation", Box::new(GoToImplementation)) - .action("Find All References", Box::new(FindAllReferences)) - .separator() - .action("Rename Symbol", Box::new(Rename)) - .action("Format Buffer", Box::new(Format)) - .when(has_selections, |cx| { - cx.action("Format Selections", Box::new(FormatSelections)) - }) - .separator() - .action("Cut", Box::new(Cut)) - .action("Copy", Box::new(Copy)) - .action("Copy and trim", Box::new(CopyAndTrim)) - .action("Paste", Box::new(Paste)) - .separator() - .map(|builder| { - let reveal_in_finder_label = if cfg!(target_os = "macos") { - "Reveal in Finder" - } else { - "Reveal in File Manager" - }; - const OPEN_IN_TERMINAL_LABEL: &str = "Open in Terminal"; - if has_reveal_target { - builder - .action(reveal_in_finder_label, Box::new(RevealInFileManager)) - .action(OPEN_IN_TERMINAL_LABEL, Box::new(OpenInTerminal)) - } else { - builder - .disabled_action(reveal_in_finder_label, Box::new(RevealInFileManager)) - .disabled_action(OPEN_IN_TERMINAL_LABEL, Box::new(OpenInTerminal)) - } - }) - .map(|builder| { - const COPY_PERMALINK_LABEL: &str = "Copy Permalink"; - if has_git_repo { - builder.action(COPY_PERMALINK_LABEL, Box::new(CopyPermalinkToLine)) - } else { - builder.disabled_action(COPY_PERMALINK_LABEL, Box::new(CopyPermalinkToLine)) - } - }) - .when_some(code_action_load_state, |menu, state| { - menu.separator().map(|menu| match state { - CodeActionLoadState::Loading => menu.disabled_action( - "Loading code actions...", - Box::new(ConfirmCodeAction { - item_ix: None, - from_mouse_context_menu: true, - }), - ), - CodeActionLoadState::Loaded(actions) => { - if actions.is_empty() { - menu.disabled_action( - "No code actions available", - Box::new(ConfirmCodeAction { - item_ix: None, - from_mouse_context_menu: true, - }), - ) - } else { - actions - .iter() - .filter(|action| { - if action - .as_task() - .map(|task| { - matches!(task.task_type(), task::TaskType::Debug(_)) - }) - .unwrap_or(false) - { - cx.has_flag::() - } else { - true - } - }) - .enumerate() - .fold(menu, |menu, (ix, action)| { - menu.action( - action.label(), - Box::new(ConfirmCodeAction { - item_ix: Some(ix), - from_mouse_context_menu: true, - }), - ) - }) - } + "Reveal in File Manager" + }; + const OPEN_IN_TERMINAL_LABEL: &str = "Open in Terminal"; + if has_reveal_target { + builder + .action(reveal_in_finder_label, Box::new(RevealInFileManager)) + .action(OPEN_IN_TERMINAL_LABEL, Box::new(OpenInTerminal)) + } else { + builder + .disabled_action(reveal_in_finder_label, Box::new(RevealInFileManager)) + .disabled_action(OPEN_IN_TERMINAL_LABEL, Box::new(OpenInTerminal)) } }) - }); - match focus { - Some(focus) => menu.context(focus), - None => menu, - } - }) -} + .map(|builder| { + const COPY_PERMALINK_LABEL: &str = "Copy Permalink"; + if has_git_repo { + builder.action(COPY_PERMALINK_LABEL, Box::new(CopyPermalinkToLine)) + } else { + builder.disabled_action(COPY_PERMALINK_LABEL, Box::new(CopyPermalinkToLine)) + } + }); + match focus { + Some(focus) => builder.context(focus), + None => builder, + } + }) + }; -fn set_context_menu( - editor: &mut Editor, - context_menu: Entity, - source_anchor: multi_buffer::Anchor, - position: Option>, - code_action: Option, - window: &mut Window, - cx: &mut Context, -) { editor.mouse_context_menu = match position { Some(position) => MouseContextMenu::pinned_to_editor( editor, source_anchor, position, - code_action, context_menu, window, cx, @@ -456,7 +287,6 @@ fn set_context_menu( editor, menu_position, context_menu, - code_action, window, cx, ))