editor: Show hints for using AI features on empty lines (#20824)

Co-Authored-by: Thorsten <thorsten@zed.dev>
Co-Authored-by: Antonio <antonio@zed.dev>

Screenshot:

![screenshot-2024-11-18-17 11
08@2x](https://github.com/user-attachments/assets/610fd7db-7476-4b9b-9465-a3d55df12340)

TODO:
- [x] docs

Release Notes:

- Added inline hints that guide users on how to invoke the inline
assistant and open the assistant panel. (These hints can be disabled by
setting `{"assistant": {"show_hints": false}}`.)

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
This commit is contained in:
Bennet Bo Fenner 2024-11-19 09:41:44 +01:00 committed by GitHub
parent a35b73e63e
commit aae39071ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 283 additions and 83 deletions

View file

@ -66,7 +66,7 @@ use zed::{
OpenRequest,
};
use crate::zed::inline_completion_registry;
use crate::zed::{assistant_hints, inline_completion_registry};
#[cfg(feature = "mimalloc")]
#[global_allocator]
@ -401,6 +401,7 @@ fn main() {
stdout_is_a_pty(),
cx,
);
assistant_hints::init(cx);
repl::init(
app_state.fs.clone(),
app_state.client.telemetry().clone(),

View file

@ -1,4 +1,5 @@
mod app_menus;
pub mod assistant_hints;
pub mod inline_completion_registry;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub(crate) mod linux_prompts;

View file

@ -0,0 +1,115 @@
use assistant::assistant_settings::AssistantSettings;
use collections::HashMap;
use editor::{ActiveLineTrailerProvider, Editor, EditorMode};
use gpui::{AnyWindowHandle, AppContext, ViewContext, WeakView, WindowContext};
use settings::{Settings, SettingsStore};
use std::{cell::RefCell, rc::Rc};
use theme::ActiveTheme;
use ui::prelude::*;
use workspace::Workspace;
pub fn init(cx: &mut AppContext) {
let editors: Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>> = Rc::default();
cx.observe_new_views({
let editors = editors.clone();
move |_: &mut Workspace, cx: &mut ViewContext<Workspace>| {
let workspace_handle = cx.view().clone();
cx.subscribe(&workspace_handle, {
let editors = editors.clone();
move |_, _, event, cx| match event {
workspace::Event::ItemAdded { item } => {
if let Some(editor) = item.act_as::<Editor>(cx) {
if editor.read(cx).mode() != EditorMode::Full {
return;
}
cx.on_release({
let editor_handle = editor.downgrade();
let editors = editors.clone();
move |_, _, _| {
editors.borrow_mut().remove(&editor_handle);
}
})
.detach();
editors
.borrow_mut()
.insert(editor.downgrade(), cx.window_handle());
let show_hints = should_show_hints(cx);
editor.update(cx, |editor, cx| {
assign_active_line_trailer_provider(editor, show_hints, cx)
})
}
}
_ => {}
}
})
.detach();
}
})
.detach();
let mut show_hints = AssistantSettings::get_global(cx).show_hints;
cx.observe_global::<SettingsStore>(move |cx| {
let new_show_hints = should_show_hints(cx);
if new_show_hints != show_hints {
show_hints = new_show_hints;
for (editor, window) in editors.borrow().iter() {
_ = window.update(cx, |_window, cx| {
_ = editor.update(cx, |editor, cx| {
assign_active_line_trailer_provider(editor, show_hints, cx);
})
});
}
}
})
.detach();
}
struct AssistantHintsProvider;
impl ActiveLineTrailerProvider for AssistantHintsProvider {
fn render_active_line_trailer(
&mut self,
style: &editor::EditorStyle,
focus_handle: &gpui::FocusHandle,
cx: &mut WindowContext,
) -> Option<gpui::AnyElement> {
if !focus_handle.is_focused(cx) {
return None;
}
let chat_keybinding =
cx.keystroke_text_for_action_in(&assistant::ToggleFocus, focus_handle);
let generate_keybinding =
cx.keystroke_text_for_action_in(&zed_actions::InlineAssist::default(), focus_handle);
Some(
h_flex()
.id("inline-assistant-instructions")
.w_full()
.font_family(style.text.font().family)
.text_color(cx.theme().status().hint)
.line_height(style.text.line_height)
.child(format!(
"{chat_keybinding} to chat, {generate_keybinding} to generate"
))
.into_any(),
)
}
}
fn assign_active_line_trailer_provider(
editor: &mut Editor,
show_hints: bool,
cx: &mut ViewContext<Editor>,
) {
let provider = show_hints.then_some(AssistantHintsProvider);
editor.set_active_line_trailer_provider(provider, cx);
}
fn should_show_hints(cx: &AppContext) -> bool {
let assistant_settings = AssistantSettings::get_global(cx);
assistant_settings.enabled && assistant_settings.show_hints
}