@@ -1,8 +1,8 @@
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
- div, px, uniform_list, AnyElement, BackgroundExecutor, Div, FontWeight, ListSizingBehavior,
- Model, ScrollStrategy, SharedString, Size, StrikethroughStyle, StyledText,
- UniformListScrollHandle, ViewContext, WeakView,
+ div, pulsating_between, px, uniform_list, Animation, AnimationExt, AnyElement,
+ BackgroundExecutor, Div, FontWeight, ListSizingBehavior, Model, ScrollStrategy, SharedString,
+ Size, StrikethroughStyle, StyledText, UniformListScrollHandle, ViewContext, WeakView,
};
use language::Buffer;
use language::{CodeLabel, Documentation};
@@ -10,6 +10,8 @@ use lsp::LanguageServerId;
use multi_buffer::{Anchor, ExcerptId};
use ordered_float::OrderedFloat;
use project::{CodeAction, Completion, TaskSourceKind};
+use settings::Settings;
+use std::time::Duration;
use std::{
cell::RefCell,
cmp::{min, Reverse},
@@ -333,9 +335,6 @@ impl CompletionsMenu {
entries[0] = hint;
}
_ => {
- if self.selected_item != 0 {
- self.selected_item += 1;
- }
entries.insert(0, hint);
}
}
@@ -463,10 +462,7 @@ impl CompletionsMenu {
len
}
- CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint {
- provider_name,
- ..
- }) => provider_name.len(),
+ CompletionEntry::InlineCompletionHint(hint) => hint.label().chars().count(),
})
.map(|(ix, _)| ix);
drop(completions);
@@ -490,6 +486,12 @@ impl CompletionsMenu {
.enumerate()
.map(|(ix, mat)| {
let item_ix = start_ix + ix;
+ let buffer_font = theme::ThemeSettings::get_global(cx).buffer_font.clone();
+ let base_label = h_flex()
+ .gap_1()
+ .child(div().font(buffer_font.clone()).child("Zed AI"))
+ .child(div().px_0p5().child("/").opacity(0.2));
+
match mat {
CompletionEntry::Match(mat) => {
let candidate_id = mat.candidate_id;
@@ -573,20 +575,57 @@ impl CompletionsMenu {
.end_slot::<Label>(documentation_label),
)
}
- CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint {
- provider_name,
- ..
- }) => div().min_w(px(250.)).max_w(px(500.)).child(
+ CompletionEntry::InlineCompletionHint(
+ hint @ InlineCompletionMenuHint::None,
+ ) => div().min_w(px(250.)).max_w(px(500.)).child(
ListItem::new("inline-completion")
.inset(true)
.toggle_state(item_ix == selected_item)
.start_slot(Icon::new(IconName::ZedPredict))
.child(
- StyledText::new(format!(
- "{} Completion",
- SharedString::new_static(provider_name)
- ))
- .with_highlights(&style.text, None),
+ base_label.child(
+ StyledText::new(hint.label())
+ .with_highlights(&style.text, None),
+ ),
+ ),
+ ),
+ CompletionEntry::InlineCompletionHint(
+ hint @ InlineCompletionMenuHint::Loading,
+ ) => div().min_w(px(250.)).max_w(px(500.)).child(
+ ListItem::new("inline-completion")
+ .inset(true)
+ .toggle_state(item_ix == selected_item)
+ .start_slot(Icon::new(IconName::ZedPredict))
+ .child(base_label.child({
+ let text_style = style.text.clone();
+ StyledText::new(hint.label())
+ .with_highlights(&text_style, None)
+ .with_animation(
+ "pulsating-label",
+ Animation::new(Duration::from_secs(1))
+ .repeat()
+ .with_easing(pulsating_between(0.4, 0.8)),
+ move |text, delta| {
+ let mut text_style = text_style.clone();
+ text_style.color =
+ text_style.color.opacity(delta);
+ text.with_highlights(&text_style, None)
+ },
+ )
+ })),
+ ),
+ CompletionEntry::InlineCompletionHint(
+ hint @ InlineCompletionMenuHint::Loaded { .. },
+ ) => div().min_w(px(250.)).max_w(px(500.)).child(
+ ListItem::new("inline-completion")
+ .inset(true)
+ .toggle_state(item_ix == selected_item)
+ .start_slot(Icon::new(IconName::ZedPredict))
+ .child(
+ base_label.child(
+ StyledText::new(hint.label())
+ .with_highlights(&style.text, None),
+ ),
)
.on_click(cx.listener(move |editor, _event, cx| {
cx.stop_propagation();
@@ -643,19 +682,20 @@ impl CompletionsMenu {
Documentation::Undocumented => return None,
}
}
- CompletionEntry::InlineCompletionHint(hint) => match &hint.text {
- InlineCompletionText::Edit { text, highlights } => div()
- .mx_1()
- .rounded(px(6.))
- .bg(cx.theme().colors().editor_background)
- .border_1()
- .border_color(cx.theme().colors().border_variant)
- .child(
- gpui::StyledText::new(text.clone())
- .with_highlights(&style.text, highlights.clone()),
- ),
- InlineCompletionText::Move(text) => div().child(text.clone()),
- },
+ CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint::Loaded { text }) => {
+ match text {
+ InlineCompletionText::Edit { text, highlights } => div()
+ .mx_1()
+ .rounded_md()
+ .bg(cx.theme().colors().editor_background)
+ .child(
+ gpui::StyledText::new(text.clone())
+ .with_highlights(&style.text, highlights.clone()),
+ ),
+ InlineCompletionText::Move(text) => div().child(text.clone()),
+ }
+ }
+ CompletionEntry::InlineCompletionHint(_) => return None,
};
Some(
@@ -459,9 +459,21 @@ pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
type CompletionId = usize;
#[derive(Debug, Clone)]
-struct InlineCompletionMenuHint {
- provider_name: &'static str,
- text: InlineCompletionText,
+enum InlineCompletionMenuHint {
+ Loading,
+ Loaded { text: InlineCompletionText },
+ None,
+}
+
+impl InlineCompletionMenuHint {
+ pub fn label(&self) -> &'static str {
+ match self {
+ InlineCompletionMenuHint::Loading | InlineCompletionMenuHint::Loaded { .. } => {
+ "Edit Prediction"
+ }
+ InlineCompletionMenuHint::None => "No Prediction",
+ }
+ }
}
#[derive(Clone, Debug)]
@@ -3823,6 +3835,26 @@ impl Editor {
) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
use language::ToOffset as _;
+ {
+ let context_menu = self.context_menu.borrow();
+ if let CodeContextMenu::Completions(menu) = context_menu.as_ref()? {
+ let entries = menu.entries.borrow();
+ let entry = entries.get(item_ix.unwrap_or(menu.selected_item));
+ match entry {
+ Some(CompletionEntry::InlineCompletionHint(
+ InlineCompletionMenuHint::Loading,
+ )) => return Some(Task::ready(Ok(()))),
+ Some(CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint::None)) => {
+ drop(entries);
+ drop(context_menu);
+ self.context_menu_next(&Default::default(), cx);
+ return Some(Task::ready(Ok(())));
+ }
+ _ => {}
+ }
+ }
+ }
+
let completions_menu =
if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
menu
@@ -3833,7 +3865,7 @@ impl Editor {
let entries = completions_menu.entries.borrow();
let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
let mat = match mat {
- CompletionEntry::InlineCompletionHint { .. } => {
+ CompletionEntry::InlineCompletionHint(_) => {
self.accept_inline_completion(&AcceptInlineCompletion, cx);
cx.stop_propagation();
return Some(Task::ready(Ok(())));
@@ -4904,8 +4936,8 @@ impl Editor {
&mut self,
cx: &mut ViewContext<Self>,
) -> Option<InlineCompletionMenuHint> {
+ let provider = self.inline_completion_provider()?;
if self.has_active_inline_completion() {
- let provider_name = self.inline_completion_provider()?.display_name();
let editor_snapshot = self.snapshot(cx);
let text = match &self.active_inline_completion.as_ref()?.completion {
@@ -4922,12 +4954,11 @@ impl Editor {
}
};
- Some(InlineCompletionMenuHint {
- provider_name,
- text,
- })
+ Some(InlineCompletionMenuHint::Loaded { text })
+ } else if provider.is_refreshing(cx) {
+ Some(InlineCompletionMenuHint::Loading)
} else {
- None
+ Some(InlineCompletionMenuHint::None)
}
}