Polish edit predictions (#24732)
Release Notes: - N/A --------- Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: as-cii <as-cii@zed.dev> Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
parent
2b7d3726b4
commit
51092c4e31
6 changed files with 353 additions and 161 deletions
|
@ -5648,6 +5648,77 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_edit_prediction_accept_keybind(&self, window: &mut Window, cx: &App) -> Option<Div> {
|
||||
let accept_binding = self.accept_edit_prediction_keybind(window, cx);
|
||||
let accept_keystroke = accept_binding.keystroke()?;
|
||||
let colors = cx.theme().colors();
|
||||
let accent_color = colors.text_accent;
|
||||
let editor_bg_color = colors.editor_background;
|
||||
let bg_color = editor_bg_color.blend(accent_color.opacity(0.1));
|
||||
|
||||
h_flex()
|
||||
.px_0p5()
|
||||
.gap_1()
|
||||
.bg(bg_color)
|
||||
.font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
|
||||
.text_size(TextSize::XSmall.rems(cx))
|
||||
.when(!self.edit_prediction_preview_is_active(), |parent| {
|
||||
parent.children(ui::render_modifiers(
|
||||
&accept_keystroke.modifiers,
|
||||
PlatformStyle::platform(),
|
||||
Some(if accept_keystroke.modifiers == window.modifiers() {
|
||||
Color::Accent
|
||||
} else {
|
||||
Color::Muted
|
||||
}),
|
||||
Some(IconSize::XSmall.rems().into()),
|
||||
false,
|
||||
))
|
||||
})
|
||||
.child(accept_keystroke.key.clone())
|
||||
.into()
|
||||
}
|
||||
|
||||
fn render_edit_prediction_line_popover(
|
||||
&self,
|
||||
label: impl Into<SharedString>,
|
||||
icon: Option<IconName>,
|
||||
window: &mut Window,
|
||||
cx: &App,
|
||||
) -> Option<Div> {
|
||||
let bg_color = Self::edit_prediction_line_popover_bg_color(cx);
|
||||
|
||||
let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
|
||||
|
||||
let result = h_flex()
|
||||
.gap_1()
|
||||
.border_1()
|
||||
.rounded_lg()
|
||||
.shadow_sm()
|
||||
.bg(bg_color)
|
||||
.border_color(cx.theme().colors().text_accent.opacity(0.4))
|
||||
.py_0p5()
|
||||
.pl_1()
|
||||
.pr(padding_right)
|
||||
.children(self.render_edit_prediction_accept_keybind(window, cx))
|
||||
.child(Label::new(label).size(LabelSize::Small))
|
||||
.when_some(icon, |element, icon| {
|
||||
element.child(
|
||||
div()
|
||||
.mt(px(1.5))
|
||||
.child(Icon::new(icon).size(IconSize::Small)),
|
||||
)
|
||||
});
|
||||
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
|
||||
let accent_color = cx.theme().colors().text_accent;
|
||||
let editor_bg_color = cx.theme().colors().editor_background;
|
||||
editor_bg_color.blend(accent_color.opacity(0.1))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn render_edit_prediction_cursor_popover(
|
||||
&self,
|
||||
|
@ -5788,18 +5859,26 @@ impl Editor {
|
|||
.min_w(min_width)
|
||||
.max_w(max_width)
|
||||
.flex_1()
|
||||
.px_2()
|
||||
.elevation_2(cx)
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(div().py_1().overflow_hidden().child(completion))
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.py_1()
|
||||
.px_2()
|
||||
.overflow_hidden()
|
||||
.child(completion),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.h_full()
|
||||
.border_l_1()
|
||||
.rounded_r_lg()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.bg(Self::edit_prediction_line_popover_bg_color(cx))
|
||||
.gap_1()
|
||||
.py_1()
|
||||
.pl_2()
|
||||
.px_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
|
||||
|
@ -14548,6 +14627,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
self.hide_context_menu(window, cx);
|
||||
self.discard_inline_completion(false, cx);
|
||||
cx.emit(EditorEvent::Blurred);
|
||||
cx.notify();
|
||||
}
|
||||
|
|
|
@ -3583,14 +3583,14 @@ impl EditorElement {
|
|||
}
|
||||
|
||||
if target_display_point.row() < visible_row_range.start {
|
||||
let mut element = inline_completion_accept_indicator(
|
||||
"Scroll",
|
||||
Some(IconName::ArrowUp),
|
||||
editor,
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
let mut element = editor
|
||||
.render_edit_prediction_line_popover(
|
||||
"Scroll",
|
||||
Some(IconName::ArrowUp),
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
|
||||
element.layout_as_root(AvailableSpace::min_size(), window, cx);
|
||||
|
||||
|
@ -3608,14 +3608,14 @@ impl EditorElement {
|
|||
element.prepaint_at(origin, window, cx);
|
||||
return Some(element);
|
||||
} else if target_display_point.row() >= visible_row_range.end {
|
||||
let mut element = inline_completion_accept_indicator(
|
||||
"Scroll",
|
||||
Some(IconName::ArrowDown),
|
||||
editor,
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
let mut element = editor
|
||||
.render_edit_prediction_line_popover(
|
||||
"Scroll",
|
||||
Some(IconName::ArrowDown),
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
|
||||
let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
|
||||
|
||||
|
@ -3640,12 +3640,11 @@ impl EditorElement {
|
|||
|
||||
let mut element = v_flex()
|
||||
.child(
|
||||
inline_completion_accept_indicator(
|
||||
"Jump", None, editor, window, cx,
|
||||
)?
|
||||
.rounded_br(px(0.))
|
||||
.rounded_tr(px(0.))
|
||||
.border_r_2(),
|
||||
editor
|
||||
.render_edit_prediction_line_popover("Jump", None, window, cx)?
|
||||
.rounded_br(px(0.))
|
||||
.rounded_tr(px(0.))
|
||||
.border_r_2(),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
|
@ -3680,28 +3679,30 @@ impl EditorElement {
|
|||
}
|
||||
|
||||
if target_display_point.row().as_f32() < scroll_top {
|
||||
let mut element = inline_completion_accept_indicator(
|
||||
"Jump to Edit",
|
||||
Some(IconName::ArrowUp),
|
||||
editor,
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
let mut element = editor
|
||||
.render_edit_prediction_line_popover(
|
||||
"Jump to Edit",
|
||||
Some(IconName::ArrowUp),
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
|
||||
let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
|
||||
let offset = point((text_bounds.size.width - size.width) / 2., PADDING_Y);
|
||||
|
||||
element.prepaint_at(text_bounds.origin + offset, window, cx);
|
||||
Some(element)
|
||||
} else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
|
||||
let mut element = inline_completion_accept_indicator(
|
||||
"Jump to Edit",
|
||||
Some(IconName::ArrowDown),
|
||||
editor,
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
let mut element = editor
|
||||
.render_edit_prediction_line_popover(
|
||||
"Jump to Edit",
|
||||
Some(IconName::ArrowDown),
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
|
||||
let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
|
||||
let offset = point(
|
||||
(text_bounds.size.width - size.width) / 2.,
|
||||
|
@ -3711,14 +3712,9 @@ impl EditorElement {
|
|||
element.prepaint_at(text_bounds.origin + offset, window, cx);
|
||||
Some(element)
|
||||
} else {
|
||||
let mut element = inline_completion_accept_indicator(
|
||||
"Jump to Edit",
|
||||
None,
|
||||
editor,
|
||||
window,
|
||||
cx,
|
||||
)?
|
||||
.into_any();
|
||||
let mut element = editor
|
||||
.render_edit_prediction_line_popover("Jump to Edit", None, window, cx)?
|
||||
.into_any();
|
||||
let target_line_end = DisplayPoint::new(
|
||||
target_display_point.row(),
|
||||
editor_snapshot.line_len(target_display_point.row()),
|
||||
|
@ -3776,10 +3772,11 @@ impl EditorElement {
|
|||
);
|
||||
let (mut element, origin) = self.editor.update(cx, |editor, cx| {
|
||||
Some((
|
||||
inline_completion_accept_indicator(
|
||||
"Accept", None, editor, window, cx,
|
||||
)?
|
||||
.into_any(),
|
||||
editor
|
||||
.render_edit_prediction_line_popover(
|
||||
"Accept", None, window, cx,
|
||||
)?
|
||||
.into_any(),
|
||||
editor.display_to_pixel_point(
|
||||
target_line_end,
|
||||
editor_snapshot,
|
||||
|
@ -3808,6 +3805,37 @@ impl EditorElement {
|
|||
cx,
|
||||
);
|
||||
|
||||
let styled_text = highlighted_edits.to_styled_text(&style.text);
|
||||
|
||||
const ACCEPT_INDICATOR_HEIGHT: Pixels = px(24.);
|
||||
|
||||
let mut element = v_flex()
|
||||
.items_end()
|
||||
.shadow_sm()
|
||||
.child(
|
||||
h_flex()
|
||||
.h(ACCEPT_INDICATOR_HEIGHT)
|
||||
.mb(px(-1.))
|
||||
.px_1p5()
|
||||
.gap_1()
|
||||
.bg(Editor::edit_prediction_line_popover_bg_color(cx))
|
||||
.border_1()
|
||||
.border_b_0()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_t_lg()
|
||||
.children(editor.render_edit_prediction_accept_keybind(window, cx)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_lg()
|
||||
.rounded_tr(Pixels::ZERO)
|
||||
.child(styled_text),
|
||||
)
|
||||
.into_any();
|
||||
|
||||
let line_count = highlighted_edits.text.lines().count();
|
||||
|
||||
let longest_row =
|
||||
|
@ -3827,16 +3855,6 @@ impl EditorElement {
|
|||
.width
|
||||
};
|
||||
|
||||
let styled_text = highlighted_edits.to_styled_text(&style.text);
|
||||
|
||||
let mut element = div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_md()
|
||||
.child(styled_text)
|
||||
.into_any();
|
||||
|
||||
let viewport_bounds = Bounds::new(Default::default(), window.viewport_size())
|
||||
.extend(Edges {
|
||||
right: -Self::SCROLLBAR_WIDTH,
|
||||
|
@ -3853,7 +3871,7 @@ impl EditorElement {
|
|||
let is_fully_visible = x_after_longest < text_bounds.right()
|
||||
&& x_after_longest + element_bounds.width < viewport_bounds.right();
|
||||
|
||||
let origin = if is_fully_visible {
|
||||
let mut origin = if is_fully_visible {
|
||||
point(
|
||||
x_after_longest,
|
||||
text_bounds.origin.y + edit_start.row().as_f32() * line_height
|
||||
|
@ -3898,6 +3916,8 @@ impl EditorElement {
|
|||
)
|
||||
};
|
||||
|
||||
origin.y -= ACCEPT_INDICATOR_HEIGHT;
|
||||
|
||||
window.defer_draw(element, origin, 1);
|
||||
|
||||
// Do not return an element, since it will already be drawn due to defer_draw.
|
||||
|
@ -5796,63 +5816,6 @@ fn header_jump_data(
|
|||
}
|
||||
}
|
||||
|
||||
fn inline_completion_accept_indicator(
|
||||
label: impl Into<SharedString>,
|
||||
icon: Option<IconName>,
|
||||
editor: &Editor,
|
||||
window: &mut Window,
|
||||
cx: &App,
|
||||
) -> Option<Div> {
|
||||
let accept_binding = editor.accept_edit_prediction_keybind(window, cx);
|
||||
let accept_keystroke = accept_binding.keystroke()?;
|
||||
|
||||
let accept_key = h_flex()
|
||||
.px_0p5()
|
||||
.font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
|
||||
.text_size(TextSize::XSmall.rems(cx))
|
||||
.text_color(cx.theme().colors().text)
|
||||
.gap_1()
|
||||
.when(!editor.edit_prediction_preview_is_active(), |parent| {
|
||||
parent.children(ui::render_modifiers(
|
||||
&accept_keystroke.modifiers,
|
||||
PlatformStyle::platform(),
|
||||
Some(Color::Default),
|
||||
None,
|
||||
false,
|
||||
))
|
||||
})
|
||||
.child(accept_keystroke.key.clone());
|
||||
|
||||
let colors = cx.theme().colors();
|
||||
|
||||
let accent_color = colors.text_accent;
|
||||
let editor_bg_color = colors.editor_background;
|
||||
let bg_color = editor_bg_color.blend(accent_color.opacity(0.2));
|
||||
let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
|
||||
|
||||
let result = h_flex()
|
||||
.gap_1()
|
||||
.border_1()
|
||||
.rounded_md()
|
||||
.shadow_sm()
|
||||
.bg(bg_color)
|
||||
.border_color(colors.text_accent.opacity(0.8))
|
||||
.py_0p5()
|
||||
.pl_1()
|
||||
.pr(padding_right)
|
||||
.child(accept_key)
|
||||
.child(Label::new(label).size(LabelSize::Small))
|
||||
.when_some(icon, |element, icon| {
|
||||
element.child(
|
||||
div()
|
||||
.mt(px(1.5))
|
||||
.child(Icon::new(icon).size(IconSize::Small)),
|
||||
)
|
||||
});
|
||||
|
||||
Some(result)
|
||||
}
|
||||
|
||||
pub struct AcceptEditPredictionBinding(pub(crate) Option<gpui::KeyBinding>);
|
||||
|
||||
impl AcceptEditPredictionBinding {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue