From c4bcff1e87a3b4851beac254042ce37d218036e3 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Fri, 7 Feb 2025 18:01:39 -0300 Subject: [PATCH] edit predictions: Add binding to the prediction toggle (#24468) This PR primary goal is to add a keybinding to the (ephemeral) prediction toggle. In doing that, we also standardized the keybinding to open the status bar menu with it. Release Notes: - N/A --------- Co-authored-by: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com> --- assets/icons/lock_outlined.svg | 6 + assets/keymaps/default-linux.json | 9 +- assets/keymaps/default-macos.json | 13 +- .../src/inline_completion_button.rs | 63 +++- crates/ui/src/components/context_menu.rs | 3 +- crates/ui/src/components/icon.rs | 1 + crates/zed/src/zed/quick_action_bar.rs | 282 +++++++++--------- 7 files changed, 215 insertions(+), 162 deletions(-) create mode 100644 assets/icons/lock_outlined.svg diff --git a/assets/icons/lock_outlined.svg b/assets/icons/lock_outlined.svg new file mode 100644 index 0000000000..0bfd2fdc82 --- /dev/null +++ b/assets/icons/lock_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 42c879a534..48eebbeaef 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -122,7 +122,8 @@ "ctrl-i": "editor::ShowSignatureHelp", "alt-g b": "editor::ToggleGitBlame", "menu": "editor::OpenContextMenu", - "shift-f10": "editor::OpenContextMenu" + "shift-f10": "editor::OpenContextMenu", + "ctrl-shift-e": "editor::ToggleEditPrediction" } }, { @@ -535,8 +536,7 @@ { "bindings": { "ctrl-alt-shift-f": "workspace::FollowNextCollaborator", - "ctrl-alt-i": "zed::DebugElements", - "ctrl-:": "editor::ToggleInlayHints" + "ctrl-alt-i": "zed::DebugElements" } }, { @@ -554,7 +554,8 @@ "ctrl-shift-e": "pane::RevealInProjectPanel", "ctrl-f8": "editor::GoToHunk", "ctrl-shift-f8": "editor::GoToPrevHunk", - "ctrl-enter": "assistant::InlineAssist" + "ctrl-enter": "assistant::InlineAssist", + "ctrl-:": "editor::ToggleInlayHints" } }, { diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 3f67997224..5fa14b940c 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -39,8 +39,8 @@ "cmd-m": "zed::Minimize", "fn-f": "zed::ToggleFullScreen", "ctrl-cmd-f": "zed::ToggleFullScreen", - "ctrl-shift-z": "zeta::RateCompletions", - "ctrl-shift-i": "edit_prediction::ToggleMenu" + "ctrl-cmd-z": "zeta::RateCompletions", + "ctrl-cmd-i": "edit_prediction::ToggleMenu" } }, { @@ -132,7 +132,8 @@ "cmd-alt-g b": "editor::ToggleGitBlame", "cmd-i": "editor::ShowSignatureHelp", "ctrl-f12": "editor::GoToDeclaration", - "alt-ctrl-f12": "editor::GoToDeclarationSplit" + "alt-ctrl-f12": "editor::GoToDeclarationSplit", + "ctrl-cmd-e": "editor::ToggleEditPrediction" } }, { @@ -619,8 +620,7 @@ "ctrl-alt-cmd-f": "workspace::FollowNextCollaborator", // TODO: Move this to a dock open action "cmd-shift-c": "collab_panel::ToggleFocus", - "cmd-alt-i": "zed::DebugElements", - "ctrl-:": "editor::ToggleInlayHints" + "cmd-alt-i": "zed::DebugElements" } }, { @@ -633,7 +633,8 @@ "cmd-shift-e": "pane::RevealInProjectPanel", "cmd-f8": "editor::GoToHunk", "cmd-shift-f8": "editor::GoToPrevHunk", - "ctrl-enter": "assistant::InlineAssist" + "ctrl-enter": "assistant::InlineAssist", + "ctrl-:": "editor::ToggleInlayHints" } }, { diff --git a/crates/inline_completion_button/src/inline_completion_button.rs b/crates/inline_completion_button/src/inline_completion_button.rs index 1864e0c266..ce1b7bcd83 100644 --- a/crates/inline_completion_button/src/inline_completion_button.rs +++ b/crates/inline_completion_button/src/inline_completion_button.rs @@ -1,7 +1,11 @@ use anyhow::Result; use client::UserStore; use copilot::{Copilot, Status}; -use editor::{actions::ShowEditPrediction, scroll::Autoscroll, Editor}; +use editor::{ + actions::{ShowEditPrediction, ToggleEditPrediction}, + scroll::Autoscroll, + Editor, +}; use feature_flags::{ FeatureFlagAppExt, PredictEditsFeatureFlag, PredictEditsRateCompletionsFeatureFlag, }; @@ -44,6 +48,7 @@ struct CopilotErrorToast; pub struct InlineCompletionButton { editor_subscription: Option<(Subscription, usize)>, editor_enabled: Option, + editor_show_predictions: bool, editor_focus_handle: Option, language: Option>, file: Option>, @@ -275,15 +280,29 @@ impl Render for InlineCompletionButton { ); } + let show_editor_predictions = self.editor_show_predictions; + let icon_button = IconButton::new("zed-predict-pending-button", zeta_icon) .shape(IconButtonShape::Square) + .when(enabled && !show_editor_predictions, |this| { + this.indicator(Indicator::dot().color(Color::Muted)) + .indicator_border_color(Some(cx.theme().colors().status_bar_background)) + }) .when(!self.popover_menu_handle.is_deployed(), |element| { - if enabled { - element.tooltip(|window, cx| { - Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx) - }) - } else { - element.tooltip(|window, cx| { + element.tooltip(move |window, cx| { + if enabled { + if show_editor_predictions { + Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx) + } else { + Tooltip::with_meta( + "Edit Prediction", + Some(&ToggleMenu), + "Hidden For This File", + window, + cx, + ) + } + } else { Tooltip::with_meta( "Edit Prediction", Some(&ToggleMenu), @@ -291,8 +310,8 @@ impl Render for InlineCompletionButton { window, cx, ) - }) - } + } + }) }); let this = cx.entity().clone(); @@ -347,6 +366,7 @@ impl InlineCompletionButton { Self { editor_subscription: None, editor_enabled: None, + editor_show_predictions: true, editor_focus_handle: None, language: None, file: None, @@ -384,6 +404,21 @@ impl InlineCompletionButton { menu = menu.header("Show Edit Predictions For"); + if let Some(editor_focus_handle) = self.editor_focus_handle.clone() { + menu = menu.toggleable_entry( + "This File", + self.editor_show_predictions, + IconPosition::Start, + Some(Box::new(ToggleEditPrediction)), + { + let editor_focus_handle = editor_focus_handle.clone(); + move |window, cx| { + editor_focus_handle.dispatch_action(&ToggleEditPrediction, window, cx); + } + }, + ); + } + if let Some(language) = self.language.clone() { let fs = fs.clone(); let language_enabled = @@ -393,7 +428,7 @@ impl InlineCompletionButton { menu = menu.toggleable_entry( language.name(), language_enabled, - IconPosition::End, + IconPosition::Start, None, move |_, cx| { toggle_show_inline_completions_for_language(language.clone(), fs.clone(), cx) @@ -406,7 +441,7 @@ impl InlineCompletionButton { menu = menu.toggleable_entry( "All Files", globally_enabled, - IconPosition::End, + IconPosition::Start, None, move |_, cx| toggle_inline_completions_globally(fs.clone(), cx), ); @@ -422,7 +457,7 @@ impl InlineCompletionButton { // TODO: We want to add something later that communicates whether // the current project is open-source. ContextMenuEntry::new("Share Training Data") - .toggleable(IconPosition::End, data_collection.is_enabled()) + .toggleable(IconPosition::Start, data_collection.is_enabled()) .documentation_aside(|_| { Label::new(indoc!{" Help us improve our open model by sharing data from open source repositories. \ @@ -450,6 +485,8 @@ impl InlineCompletionButton { menu = menu.item( ContextMenuEntry::new("Configure Excluded Files") + .icon(IconName::LockOutlined) + .icon_color(Color::Muted) .documentation_aside(|_| { Label::new(indoc!{" Open your settings to add sensitive paths for which Zed will never predict edits."}).into_any_element() @@ -486,7 +523,6 @@ impl InlineCompletionButton { Some(Box::new(ShowEditPrediction)), { let editor_focus_handle = editor_focus_handle.clone(); - move |window, cx| { editor_focus_handle.dispatch_action(&ShowEditPrediction, window, cx); } @@ -571,6 +607,7 @@ impl InlineCompletionButton { .unwrap_or(true), ) }; + self.editor_show_predictions = editor.should_show_inline_completions(cx); self.edit_prediction_provider = editor.edit_prediction_provider(); self.language = language.cloned(); self.file = file; diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index db9632d4ff..fe00c733f0 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -674,7 +674,8 @@ impl Render for ContextMenu { let contents = if toggled { v_flex().flex_none().child( Icon::new(IconName::Check) - .color(Color::Accent), + .color(Color::Accent) + .size(*icon_size) ) } else { v_flex().flex_none().size( diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 12346026e8..4ea5ca9c54 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -234,6 +234,7 @@ pub enum IconName { Link, ListTree, ListX, + LockOutlined, MagnifyingGlass, MailOpen, Maximize, diff --git a/crates/zed/src/zed/quick_action_bar.rs b/crates/zed/src/zed/quick_action_bar.rs index 67161de75f..bc523be4fd 100644 --- a/crates/zed/src/zed/quick_action_bar.rs +++ b/crates/zed/src/zed/quick_action_bar.rs @@ -213,6 +213,7 @@ impl Render for QuickActionBar { }) }); + let editor_focus_handle = editor.focus_handle(cx); let editor = editor.downgrade(); let editor_settings_dropdown = { let vim_mode_enabled = VimModeSetting::get_global(cx).0; @@ -231,20 +232,67 @@ impl Render for QuickActionBar { .anchor(Corner::TopRight) .with_handle(self.toggle_settings_handle.clone()) .menu(move |window, cx| { - let menu = ContextMenu::build(window, cx, |mut menu, _, _| { - if supports_inlay_hints { + let menu = ContextMenu::build(window, cx, { + let focus_handle = editor_focus_handle.clone(); + |mut menu, _, _| { + menu = menu.context(focus_handle); + + if supports_inlay_hints { + menu = menu.toggleable_entry( + "Inlay Hints", + inlay_hints_enabled, + IconPosition::Start, + Some(editor::actions::ToggleInlayHints.boxed_clone()), + { + let editor = editor.clone(); + move |window, cx| { + editor + .update(cx, |editor, cx| { + editor.toggle_inlay_hints( + &editor::actions::ToggleInlayHints, + window, + cx, + ); + }) + .ok(); + } + }, + ); + } + menu = menu.toggleable_entry( - "Inlay Hints", - inlay_hints_enabled, + "Selection Menu", + selection_menu_enabled, IconPosition::Start, - Some(editor::actions::ToggleInlayHints.boxed_clone()), + Some(editor::actions::ToggleSelectionMenu.boxed_clone()), { let editor = editor.clone(); move |window, cx| { editor .update(cx, |editor, cx| { - editor.toggle_inlay_hints( - &editor::actions::ToggleInlayHints, + editor.toggle_selection_menu( + &editor::actions::ToggleSelectionMenu, + window, + cx, + ) + }) + .ok(); + } + }, + ); + + menu = menu.toggleable_entry( + "Auto Signature Help", + auto_signature_help_enabled, + IconPosition::Start, + Some(editor::actions::ToggleAutoSignatureHelp.boxed_clone()), + { + let editor = editor.clone(); + move |window, cx| { + editor + .update(cx, |editor, cx| { + editor.toggle_auto_signature_help_menu( + &editor::actions::ToggleAutoSignatureHelp, window, cx, ); @@ -253,138 +301,96 @@ impl Render for QuickActionBar { } }, ); + + let mut inline_completion_entry = ContextMenuEntry::new("Edit Predictions") + .toggleable(IconPosition::Start, inline_completion_enabled && show_inline_completions) + .disabled(!inline_completion_enabled) + .action(Some( + editor::actions::ToggleEditPrediction.boxed_clone(), + )).handler({ + let editor = editor.clone(); + move |window, cx| { + editor + .update(cx, |editor, cx| { + editor.toggle_inline_completions( + &editor::actions::ToggleEditPrediction, + window, + cx, + ); + }) + .ok(); + } + }); + if !inline_completion_enabled { + inline_completion_entry = inline_completion_entry.documentation_aside(|_| { + Label::new("You can't toggle edit predictions for this file as it is within the excluded files list.").into_any_element() + }); + } + + menu = menu.item(inline_completion_entry); + + menu = menu.separator(); + + menu = menu.toggleable_entry( + "Inline Git Blame", + git_blame_inline_enabled, + IconPosition::Start, + Some(editor::actions::ToggleGitBlameInline.boxed_clone()), + { + let editor = editor.clone(); + move |window, cx| { + editor + .update(cx, |editor, cx| { + editor.toggle_git_blame_inline( + &editor::actions::ToggleGitBlameInline, + window, + cx, + ) + }) + .ok(); + } + }, + ); + + menu = menu.toggleable_entry( + "Column Git Blame", + show_git_blame_gutter, + IconPosition::Start, + Some(editor::actions::ToggleGitBlame.boxed_clone()), + { + let editor = editor.clone(); + move |window, cx| { + editor + .update(cx, |editor, cx| { + editor.toggle_git_blame( + &editor::actions::ToggleGitBlame, + window, + cx, + ) + }) + .ok(); + } + }, + ); + + menu = menu.separator(); + + menu = menu.toggleable_entry( + "Vim Mode", + vim_mode_enabled, + IconPosition::Start, + None, + { + move |window, cx| { + let new_value = !vim_mode_enabled; + VimModeSetting::override_global(VimModeSetting(new_value), cx); + window.refresh(); + } + }, + ); + + menu } - - menu = menu.toggleable_entry( - "Selection Menu", - selection_menu_enabled, - IconPosition::Start, - Some(editor::actions::ToggleSelectionMenu.boxed_clone()), - { - let editor = editor.clone(); - move |window, cx| { - editor - .update(cx, |editor, cx| { - editor.toggle_selection_menu( - &editor::actions::ToggleSelectionMenu, - window, - cx, - ) - }) - .ok(); - } - }, - ); - - menu = menu.toggleable_entry( - "Auto Signature Help", - auto_signature_help_enabled, - IconPosition::Start, - Some(editor::actions::ToggleAutoSignatureHelp.boxed_clone()), - { - let editor = editor.clone(); - move |window, cx| { - editor - .update(cx, |editor, cx| { - editor.toggle_auto_signature_help_menu( - &editor::actions::ToggleAutoSignatureHelp, - window, - cx, - ); - }) - .ok(); - } - }, - ); - - let mut inline_completion_entry = ContextMenuEntry::new("Edit Predictions") - .toggleable(IconPosition::Start, inline_completion_enabled && show_inline_completions) - .disabled(!inline_completion_enabled) - .action(Some( - editor::actions::ToggleEditPrediction.boxed_clone(), - )).handler({ - let editor = editor.clone(); - move |window, cx| { - editor - .update(cx, |editor, cx| { - editor.toggle_inline_completions( - &editor::actions::ToggleEditPrediction, - window, - cx, - ); - }) - .ok(); - } - }); - if !inline_completion_enabled { - inline_completion_entry = inline_completion_entry.documentation_aside(|_| { - Label::new("You can't toggle edit predictions for this file as it is within the excluded files list.").into_any_element() - }); - } - - menu = menu.item(inline_completion_entry); - - menu = menu.separator(); - - menu = menu.toggleable_entry( - "Inline Git Blame", - git_blame_inline_enabled, - IconPosition::Start, - Some(editor::actions::ToggleGitBlameInline.boxed_clone()), - { - let editor = editor.clone(); - move |window, cx| { - editor - .update(cx, |editor, cx| { - editor.toggle_git_blame_inline( - &editor::actions::ToggleGitBlameInline, - window, - cx, - ) - }) - .ok(); - } - }, - ); - - menu = menu.toggleable_entry( - "Column Git Blame", - show_git_blame_gutter, - IconPosition::Start, - Some(editor::actions::ToggleGitBlame.boxed_clone()), - { - let editor = editor.clone(); - move |window, cx| { - editor - .update(cx, |editor, cx| { - editor.toggle_git_blame( - &editor::actions::ToggleGitBlame, - window, - cx, - ) - }) - .ok(); - } - }, - ); - - menu = menu.separator(); - - menu = menu.toggleable_entry( - "Vim Mode", - vim_mode_enabled, - IconPosition::Start, - None, - { - move |window, cx| { - let new_value = !vim_mode_enabled; - VimModeSetting::override_global(VimModeSetting(new_value), cx); - window.refresh(); - } - }, - ); - - menu }); Some(menu) })