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)
})