Revise "Hide/Show Inline Completions" menu (#23808)

> **Note:** https://github.com/zed-industries/zed/pull/23813 should be
merged first!

@nathansobo and I paired on revising this menu, including adding the
"Predict Edits at Cursor" menu item (to make the keyboard shortcut more
discoverable; clicking it makes the inline edits show up, as shown in
the second screenshot) and switching from "Hide/Show" language to
checkboxes.

## Before
<img width="282" alt="Screenshot 2025-01-28 at 4 51 37 PM"
src="https://github.com/user-attachments/assets/309c82c1-8fb5-44db-950e-1a8789a63993"
/>

## After
<img width="1138" alt="Screenshot 2025-01-28 at 4 50 05 PM"
src="https://github.com/user-attachments/assets/302a126c-9389-42a4-bb7d-2896bce859e7"
/>

We also switched to use `SharedString` in more places, where it made
more sense.

@danilo-leal This isn't necessarily *exactly* what we want, but we were
pairing and decided to get it in a state where we can actually try it
out and tweak from here.

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
Richard Feldman 2025-01-29 15:45:28 -05:00 committed by GitHub
parent 4cef772364
commit ee0d2a8d94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 24 deletions

View file

@ -1,14 +1,15 @@
use anyhow::Result; use anyhow::Result;
use client::UserStore; use client::UserStore;
use copilot::{Copilot, Status}; use copilot::{Copilot, Status};
use editor::{scroll::Autoscroll, Editor}; use editor::{actions::ShowInlineCompletion, scroll::Autoscroll, Editor};
use feature_flags::{ use feature_flags::{
FeatureFlagAppExt, PredictEditsFeatureFlag, PredictEditsRateCompletionsFeatureFlag, FeatureFlagAppExt, PredictEditsFeatureFlag, PredictEditsRateCompletionsFeatureFlag,
}; };
use fs::Fs; use fs::Fs;
use gpui::{ use gpui::{
actions, div, pulsating_between, Action, Animation, AnimationExt, App, AsyncWindowContext, actions, div, pulsating_between, Action, Animation, AnimationExt, App, AsyncWindowContext,
Corner, Entity, IntoElement, ParentElement, Render, Subscription, WeakEntity, Corner, Entity, FocusHandle, Focusable, IntoElement, ParentElement, Render, Subscription,
WeakEntity,
}; };
use language::{ use language::{
language_settings::{ language_settings::{
@ -43,6 +44,7 @@ struct CopilotErrorToast;
pub struct InlineCompletionButton { pub struct InlineCompletionButton {
editor_subscription: Option<(Subscription, usize)>, editor_subscription: Option<(Subscription, usize)>,
editor_enabled: Option<bool>, editor_enabled: Option<bool>,
editor_focus_handle: Option<FocusHandle>,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
file: Option<Arc<dyn File>>, file: Option<Arc<dyn File>>,
inline_completion_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>, inline_completion_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>,
@ -329,6 +331,7 @@ impl InlineCompletionButton {
Self { Self {
editor_subscription: None, editor_subscription: None,
editor_enabled: None, editor_enabled: None,
editor_focus_handle: None,
language: None, language: None,
file: None, file: None,
inline_completion_provider: None, inline_completion_provider: None,
@ -364,21 +367,26 @@ impl InlineCompletionButton {
}) })
} }
// Predict Edits at Cursor alt-tab
// Automatically Predict:
// ✓ PATH
// ✓ Rust
// ✓ All Files
pub fn build_language_settings_menu(&self, mut menu: ContextMenu, cx: &mut App) -> ContextMenu { pub fn build_language_settings_menu(&self, mut menu: ContextMenu, cx: &mut App) -> ContextMenu {
let fs = self.fs.clone(); let fs = self.fs.clone();
menu = menu.header("Predict Edits For:");
if let Some(language) = self.language.clone() { if let Some(language) = self.language.clone() {
let fs = fs.clone(); let fs = fs.clone();
let language_enabled = let language_enabled =
language_settings::language_settings(Some(language.name()), None, cx) language_settings::language_settings(Some(language.name()), None, cx)
.show_inline_completions; .show_inline_completions;
menu = menu.entry( menu = menu.toggleable_entry(
format!( language.name(),
"{} Inline Completions for {}", language_enabled,
if language_enabled { "Hide" } else { "Show" }, IconPosition::Start,
language.name()
),
None, None,
move |_, cx| { move |_, cx| {
toggle_inline_completions_for_language(language.clone(), fs.clone(), cx) toggle_inline_completions_for_language(language.clone(), fs.clone(), cx)
@ -387,16 +395,14 @@ impl InlineCompletionButton {
} }
let settings = AllLanguageSettings::get_global(cx); let settings = AllLanguageSettings::get_global(cx);
if let Some(file) = &self.file { if let Some(file) = &self.file {
let path = file.path().clone(); let path = file.path().clone();
let path_enabled = settings.inline_completions_enabled_for_path(&path); let path_enabled = settings.inline_completions_enabled_for_path(&path);
menu = menu.entry( menu = menu.toggleable_entry(
format!( "This File",
"{} Inline Completions for This Path", path_enabled,
if path_enabled { "Hide" } else { "Show" } IconPosition::Start,
),
None, None,
move |window, cx| { move |window, cx| {
if let Some(workspace) = window.root().flatten() { if let Some(workspace) = window.root().flatten() {
@ -416,15 +422,32 @@ impl InlineCompletionButton {
} }
let globally_enabled = settings.inline_completions_enabled(None, None, cx); let globally_enabled = settings.inline_completions_enabled(None, None, cx);
menu.entry( menu = menu.toggleable_entry(
if globally_enabled { "All Files",
"Hide Inline Completions for All Files" globally_enabled,
} else { IconPosition::Start,
"Show Inline Completions for All Files"
},
None, None,
move |_, cx| toggle_inline_completions_globally(fs.clone(), cx), move |_, cx| toggle_inline_completions_globally(fs.clone(), cx),
) );
if let Some(editor_focus_handle) = self.editor_focus_handle.clone() {
menu = menu
.separator()
.entry(
"Predict Edit at Cursor",
Some(Box::new(ShowInlineCompletion)),
{
let editor_focus_handle = editor_focus_handle.clone();
move |window, cx| {
editor_focus_handle.dispatch_action(&ShowInlineCompletion, window, cx);
}
},
)
.context(editor_focus_handle);
}
menu
} }
fn build_copilot_context_menu( fn build_copilot_context_menu(
@ -468,7 +491,7 @@ impl InlineCompletionButton {
self.build_language_settings_menu(menu, cx).when( self.build_language_settings_menu(menu, cx).when(
cx.has_flag::<PredictEditsRateCompletionsFeatureFlag>(), cx.has_flag::<PredictEditsRateCompletionsFeatureFlag>(),
|this| { |this| {
this.separator().entry( this.entry(
"Rate Completions", "Rate Completions",
Some(RateCompletions.boxed_clone()), Some(RateCompletions.boxed_clone()),
move |window, cx| { move |window, cx| {
@ -504,6 +527,7 @@ impl InlineCompletionButton {
self.inline_completion_provider = editor.inline_completion_provider(); self.inline_completion_provider = editor.inline_completion_provider();
self.language = language.cloned(); self.language = language.cloned();
self.file = file; self.file = file;
self.editor_focus_handle = Some(editor.focus_handle(cx));
cx.notify(); cx.notify();
} }

View file

@ -49,7 +49,7 @@ impl RenderOnce for ListSubHeader {
.px(DynamicSpacing::Base02.rems(cx)) .px(DynamicSpacing::Base02.rems(cx))
.child( .child(
div() div()
.h_6() .h_5()
.when(self.inset, |this| this.px_2()) .when(self.inset, |this| this.px_2())
.when(self.selected, |this| { .when(self.selected, |this| {
this.bg(cx.theme().colors().ghost_element_selected) this.bg(cx.theme().colors().ghost_element_selected)
@ -70,7 +70,11 @@ impl RenderOnce for ListSubHeader {
Icon::new(i).color(Color::Muted).size(IconSize::Small) Icon::new(i).color(Color::Muted).size(IconSize::Small)
}), }),
) )
.child(Label::new(self.label.clone()).color(Color::Muted)), .child(
Label::new(self.label.clone())
.color(Color::Muted)
.size(LabelSize::Small),
),
), ),
) )
} }