Show loading state for predictions (#23172)
Release Notes: - N/A --------- Co-authored-by: Thorsten <thorsten@zed.dev> Co-authored-by: Danilo Leal <daniloleal09@gmail.com> Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
parent
bf75b33464
commit
da8e65b3e5
2 changed files with 113 additions and 40 deletions
|
@ -1,8 +1,8 @@
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, uniform_list, AnyElement, BackgroundExecutor, Div, FontWeight, ListSizingBehavior,
|
div, pulsating_between, px, uniform_list, Animation, AnimationExt, AnyElement,
|
||||||
Model, ScrollStrategy, SharedString, Size, StrikethroughStyle, StyledText,
|
BackgroundExecutor, Div, FontWeight, ListSizingBehavior, Model, ScrollStrategy, SharedString,
|
||||||
UniformListScrollHandle, ViewContext, WeakView,
|
Size, StrikethroughStyle, StyledText, UniformListScrollHandle, ViewContext, WeakView,
|
||||||
};
|
};
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use language::{CodeLabel, Documentation};
|
use language::{CodeLabel, Documentation};
|
||||||
|
@ -10,6 +10,8 @@ use lsp::LanguageServerId;
|
||||||
use multi_buffer::{Anchor, ExcerptId};
|
use multi_buffer::{Anchor, ExcerptId};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use project::{CodeAction, Completion, TaskSourceKind};
|
use project::{CodeAction, Completion, TaskSourceKind};
|
||||||
|
use settings::Settings;
|
||||||
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
cmp::{min, Reverse},
|
cmp::{min, Reverse},
|
||||||
|
@ -333,7 +335,6 @@ impl CompletionsMenu {
|
||||||
entries[0] = hint;
|
entries[0] = hint;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.selected_item += 1;
|
|
||||||
entries.insert(0, hint);
|
entries.insert(0, hint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,10 +462,7 @@ impl CompletionsMenu {
|
||||||
|
|
||||||
len
|
len
|
||||||
}
|
}
|
||||||
CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint {
|
CompletionEntry::InlineCompletionHint(hint) => hint.label().chars().count(),
|
||||||
provider_name,
|
|
||||||
..
|
|
||||||
}) => provider_name.len(),
|
|
||||||
})
|
})
|
||||||
.map(|(ix, _)| ix);
|
.map(|(ix, _)| ix);
|
||||||
drop(completions);
|
drop(completions);
|
||||||
|
@ -488,6 +486,12 @@ impl CompletionsMenu {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(ix, mat)| {
|
.map(|(ix, mat)| {
|
||||||
let item_ix = start_ix + ix;
|
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 {
|
match mat {
|
||||||
CompletionEntry::Match(mat) => {
|
CompletionEntry::Match(mat) => {
|
||||||
let candidate_id = mat.candidate_id;
|
let candidate_id = mat.candidate_id;
|
||||||
|
@ -571,20 +575,57 @@ impl CompletionsMenu {
|
||||||
.end_slot::<Label>(documentation_label),
|
.end_slot::<Label>(documentation_label),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint {
|
CompletionEntry::InlineCompletionHint(
|
||||||
provider_name,
|
hint @ InlineCompletionMenuHint::None,
|
||||||
..
|
) => div().min_w(px(250.)).max_w(px(500.)).child(
|
||||||
}) => div().min_w(px(250.)).max_w(px(500.)).child(
|
|
||||||
ListItem::new("inline-completion")
|
ListItem::new("inline-completion")
|
||||||
.inset(true)
|
.inset(true)
|
||||||
.toggle_state(item_ix == selected_item)
|
.toggle_state(item_ix == selected_item)
|
||||||
.start_slot(Icon::new(IconName::ZedPredict))
|
.start_slot(Icon::new(IconName::ZedPredict))
|
||||||
.child(
|
.child(
|
||||||
StyledText::new(format!(
|
base_label.child(
|
||||||
"{} Completion",
|
StyledText::new(hint.label())
|
||||||
SharedString::new_static(provider_name)
|
.with_highlights(&style.text, None),
|
||||||
))
|
),
|
||||||
.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| {
|
.on_click(cx.listener(move |editor, _event, cx| {
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
|
@ -641,19 +682,20 @@ impl CompletionsMenu {
|
||||||
Documentation::Undocumented => return None,
|
Documentation::Undocumented => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CompletionEntry::InlineCompletionHint(hint) => match &hint.text {
|
CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint::Loaded { text }) => {
|
||||||
InlineCompletionText::Edit { text, highlights } => div()
|
match text {
|
||||||
.mx_1()
|
InlineCompletionText::Edit { text, highlights } => div()
|
||||||
.rounded(px(6.))
|
.mx_1()
|
||||||
.bg(cx.theme().colors().editor_background)
|
.rounded_md()
|
||||||
.border_1()
|
.bg(cx.theme().colors().editor_background)
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.child(
|
||||||
.child(
|
gpui::StyledText::new(text.clone())
|
||||||
gpui::StyledText::new(text.clone())
|
.with_highlights(&style.text, highlights.clone()),
|
||||||
.with_highlights(&style.text, highlights.clone()),
|
),
|
||||||
),
|
InlineCompletionText::Move(text) => div().child(text.clone()),
|
||||||
InlineCompletionText::Move(text) => div().child(text.clone()),
|
}
|
||||||
},
|
}
|
||||||
|
CompletionEntry::InlineCompletionHint(_) => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
|
|
|
@ -459,9 +459,21 @@ pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
|
||||||
type CompletionId = usize;
|
type CompletionId = usize;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct InlineCompletionMenuHint {
|
enum InlineCompletionMenuHint {
|
||||||
provider_name: &'static str,
|
Loading,
|
||||||
text: InlineCompletionText,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -3828,6 +3840,26 @@ impl Editor {
|
||||||
) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
|
) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
|
||||||
use language::ToOffset as _;
|
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 =
|
let completions_menu =
|
||||||
if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
|
if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
|
||||||
menu
|
menu
|
||||||
|
@ -3838,7 +3870,7 @@ impl Editor {
|
||||||
let entries = completions_menu.entries.borrow();
|
let entries = completions_menu.entries.borrow();
|
||||||
let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
|
let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
|
||||||
let mat = match mat {
|
let mat = match mat {
|
||||||
CompletionEntry::InlineCompletionHint { .. } => {
|
CompletionEntry::InlineCompletionHint(_) => {
|
||||||
self.accept_inline_completion(&AcceptInlineCompletion, cx);
|
self.accept_inline_completion(&AcceptInlineCompletion, cx);
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
return Some(Task::ready(Ok(())));
|
return Some(Task::ready(Ok(())));
|
||||||
|
@ -4912,8 +4944,8 @@ impl Editor {
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<InlineCompletionMenuHint> {
|
) -> Option<InlineCompletionMenuHint> {
|
||||||
|
let provider = self.inline_completion_provider()?;
|
||||||
if self.has_active_inline_completion() {
|
if self.has_active_inline_completion() {
|
||||||
let provider_name = self.inline_completion_provider()?.display_name();
|
|
||||||
let editor_snapshot = self.snapshot(cx);
|
let editor_snapshot = self.snapshot(cx);
|
||||||
|
|
||||||
let text = match &self.active_inline_completion.as_ref()?.completion {
|
let text = match &self.active_inline_completion.as_ref()?.completion {
|
||||||
|
@ -4930,12 +4962,11 @@ impl Editor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(InlineCompletionMenuHint {
|
Some(InlineCompletionMenuHint::Loaded { text })
|
||||||
provider_name,
|
} else if provider.is_refreshing(cx) {
|
||||||
text,
|
Some(InlineCompletionMenuHint::Loading)
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
Some(InlineCompletionMenuHint::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue