mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 11:01:54 +00:00
Show "tab Accept" indicator for single line insertions/deletions (#23411)
https://github.com/user-attachments/assets/655f20a9-f22f-4f91-870e-d40b20c8c994 Release Notes: - N/A Co-authored-by: Danilo <danilo@zed.dev>
This commit is contained in:
parent
14cd178ab0
commit
86ff88ae1d
1 changed files with 106 additions and 62 deletions
|
@ -1592,6 +1592,7 @@ impl EditorElement {
|
||||||
&self,
|
&self,
|
||||||
display_row: DisplayRow,
|
display_row: DisplayRow,
|
||||||
display_snapshot: &DisplaySnapshot,
|
display_snapshot: &DisplaySnapshot,
|
||||||
|
buffer_snapshot: &MultiBufferSnapshot,
|
||||||
line_layout: &LineWithInvisibles,
|
line_layout: &LineWithInvisibles,
|
||||||
crease_trailer: Option<&CreaseTrailerLayout>,
|
crease_trailer: Option<&CreaseTrailerLayout>,
|
||||||
em_width: Pixels,
|
em_width: Pixels,
|
||||||
|
@ -1617,7 +1618,28 @@ impl EditorElement {
|
||||||
let display_point = DisplayPoint::new(display_row, 0);
|
let display_point = DisplayPoint::new(display_row, 0);
|
||||||
let buffer_row = MultiBufferRow(display_point.to_point(display_snapshot).row);
|
let buffer_row = MultiBufferRow(display_point.to_point(display_snapshot).row);
|
||||||
|
|
||||||
let blame = self.editor.read(cx).blame.clone()?;
|
let editor = self.editor.read(cx);
|
||||||
|
let blame = editor.blame.clone()?;
|
||||||
|
let padding = {
|
||||||
|
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 6.;
|
||||||
|
const INLINE_ACCEPT_SUGGESTION_EM_WIDTHS: f32 = 14.;
|
||||||
|
|
||||||
|
let mut padding = INLINE_BLAME_PADDING_EM_WIDTHS;
|
||||||
|
|
||||||
|
if let Some(inline_completion) = editor.active_inline_completion.as_ref() {
|
||||||
|
match &inline_completion.completion {
|
||||||
|
InlineCompletion::Edit(edits)
|
||||||
|
if single_line_edit(&edits, buffer_snapshot).is_some() =>
|
||||||
|
{
|
||||||
|
padding += INLINE_ACCEPT_SUGGESTION_EM_WIDTHS
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
padding * em_width
|
||||||
|
};
|
||||||
|
|
||||||
let blame_entry = blame
|
let blame_entry = blame
|
||||||
.update(cx, |blame, cx| {
|
.update(cx, |blame, cx| {
|
||||||
blame.blame_for_rows([Some(buffer_row)], cx).next()
|
blame.blame_for_rows([Some(buffer_row)], cx).next()
|
||||||
|
@ -1631,14 +1653,13 @@ impl EditorElement {
|
||||||
+ line_height * (display_row.as_f32() - scroll_pixel_position.y / line_height);
|
+ line_height * (display_row.as_f32() - scroll_pixel_position.y / line_height);
|
||||||
|
|
||||||
let start_x = {
|
let start_x = {
|
||||||
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 6.;
|
|
||||||
|
|
||||||
let line_end = if let Some(crease_trailer) = crease_trailer {
|
let line_end = if let Some(crease_trailer) = crease_trailer {
|
||||||
crease_trailer.bounds.right()
|
crease_trailer.bounds.right()
|
||||||
} else {
|
} else {
|
||||||
content_origin.x - scroll_pixel_position.x + line_layout.width
|
content_origin.x - scroll_pixel_position.x + line_layout.width
|
||||||
};
|
};
|
||||||
let padded_line_end = line_end + em_width * INLINE_BLAME_PADDING_EM_WIDTHS;
|
|
||||||
|
let padded_line_end = line_end + padding;
|
||||||
|
|
||||||
let min_column_in_pixels = ProjectSettings::get_global(cx)
|
let min_column_in_pixels = ProjectSettings::get_global(cx)
|
||||||
.git
|
.git
|
||||||
|
@ -3326,49 +3347,23 @@ impl EditorElement {
|
||||||
|
|
||||||
match &active_inline_completion.completion {
|
match &active_inline_completion.completion {
|
||||||
InlineCompletion::Move(target_position) => {
|
InlineCompletion::Move(target_position) => {
|
||||||
let tab_kbd = 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)
|
|
||||||
.child("tab");
|
|
||||||
|
|
||||||
let icon_container = div().mt(px(2.5)); // For optical alignment
|
|
||||||
|
|
||||||
let container_element = h_flex()
|
|
||||||
.items_center()
|
|
||||||
.py_0p5()
|
|
||||||
.px_1()
|
|
||||||
.gap_1()
|
|
||||||
.bg(cx.theme().colors().text_accent.opacity(0.15))
|
|
||||||
.border_1()
|
|
||||||
.border_color(cx.theme().colors().text_accent.opacity(0.8))
|
|
||||||
.rounded_md()
|
|
||||||
.shadow_sm();
|
|
||||||
|
|
||||||
let target_display_point = target_position.to_display_point(editor_snapshot);
|
let target_display_point = target_position.to_display_point(editor_snapshot);
|
||||||
if target_display_point.row().as_f32() < scroll_top {
|
if target_display_point.row().as_f32() < scroll_top {
|
||||||
let mut element = container_element
|
let mut element = inline_completion_tab_indicator(
|
||||||
.child(tab_kbd)
|
"Jump to Edit",
|
||||||
.child(Label::new("Jump to Edit").size(LabelSize::Small))
|
Some(IconName::ArrowUp),
|
||||||
.child(
|
cx,
|
||||||
icon_container
|
);
|
||||||
.child(Icon::new(IconName::ArrowUp).size(IconSize::Small)),
|
|
||||||
)
|
|
||||||
.into_any();
|
|
||||||
let size = element.layout_as_root(AvailableSpace::min_size(), cx);
|
let size = element.layout_as_root(AvailableSpace::min_size(), cx);
|
||||||
let offset = point((text_bounds.size.width - size.width) / 2., PADDING_Y);
|
let offset = point((text_bounds.size.width - size.width) / 2., PADDING_Y);
|
||||||
element.prepaint_at(text_bounds.origin + offset, cx);
|
element.prepaint_at(text_bounds.origin + offset, cx);
|
||||||
Some(element)
|
Some(element)
|
||||||
} else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
|
} else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
|
||||||
let mut element = container_element
|
let mut element = inline_completion_tab_indicator(
|
||||||
.child(tab_kbd)
|
"Jump to Edit",
|
||||||
.child(Label::new("Jump to Edit").size(LabelSize::Small))
|
Some(IconName::ArrowDown),
|
||||||
.child(
|
cx,
|
||||||
icon_container
|
);
|
||||||
.child(Icon::new(IconName::ArrowDown).size(IconSize::Small)),
|
|
||||||
)
|
|
||||||
.into_any();
|
|
||||||
let size = element.layout_as_root(AvailableSpace::min_size(), cx);
|
let size = element.layout_as_root(AvailableSpace::min_size(), cx);
|
||||||
let offset = point(
|
let offset = point(
|
||||||
(text_bounds.size.width - size.width) / 2.,
|
(text_bounds.size.width - size.width) / 2.,
|
||||||
|
@ -3377,10 +3372,7 @@ impl EditorElement {
|
||||||
element.prepaint_at(text_bounds.origin + offset, cx);
|
element.prepaint_at(text_bounds.origin + offset, cx);
|
||||||
Some(element)
|
Some(element)
|
||||||
} else {
|
} else {
|
||||||
let mut element = container_element
|
let mut element = inline_completion_tab_indicator("Jump to Edit", None, cx);
|
||||||
.child(tab_kbd)
|
|
||||||
.child(Label::new("Jump to Edit").size(LabelSize::Small))
|
|
||||||
.into_any();
|
|
||||||
|
|
||||||
let target_line_end = DisplayPoint::new(
|
let target_line_end = DisplayPoint::new(
|
||||||
target_display_point.row(),
|
target_display_point.row(),
|
||||||
|
@ -3421,9 +3413,28 @@ impl EditorElement {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hard_to_spot_single_edit(&edits, &editor_snapshot.buffer_snapshot)
|
if let Some(range) = single_line_edit(&edits, &editor_snapshot.buffer_snapshot) {
|
||||||
&& all_edits_insertions_or_deletions(edits, &editor_snapshot.buffer_snapshot)
|
let mut element = inline_completion_tab_indicator("Accept", None, cx);
|
||||||
{
|
|
||||||
|
let target_display_point = range.end.to_display_point(editor_snapshot);
|
||||||
|
let target_line_end = DisplayPoint::new(
|
||||||
|
target_display_point.row(),
|
||||||
|
editor_snapshot.line_len(target_display_point.row()),
|
||||||
|
);
|
||||||
|
let origin = self.editor.update(cx, |editor, cx| {
|
||||||
|
editor.display_to_pixel_point(target_line_end, editor_snapshot, cx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
element.prepaint_as_root(
|
||||||
|
text_bounds.origin + origin + point(PADDING_X, px(0.)),
|
||||||
|
AvailableSpace::min_size(),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Some(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
if all_edits_insertions_or_deletions(edits, &editor_snapshot.buffer_snapshot) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5203,23 +5214,55 @@ fn header_jump_data(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if there's a single edit, that's either a single character
|
fn inline_completion_tab_indicator(
|
||||||
/// insertion or a single line deletion.
|
label: impl Into<SharedString>,
|
||||||
fn hard_to_spot_single_edit(
|
icon: Option<IconName>,
|
||||||
edits: &[(Range<Anchor>, String)],
|
cx: &WindowContext,
|
||||||
|
) -> AnyElement {
|
||||||
|
let tab_kbd = 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)
|
||||||
|
.child("tab");
|
||||||
|
|
||||||
|
let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.py_0p5()
|
||||||
|
.pl_1()
|
||||||
|
.pr(padding_right)
|
||||||
|
.gap_1()
|
||||||
|
.bg(cx.theme().colors().text_accent.opacity(0.15))
|
||||||
|
.border_1()
|
||||||
|
.border_color(cx.theme().colors().text_accent.opacity(0.8))
|
||||||
|
.rounded_md()
|
||||||
|
.shadow_sm()
|
||||||
|
.child(tab_kbd)
|
||||||
|
.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)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn single_line_edit<'a>(
|
||||||
|
edits: &'a [(Range<Anchor>, String)],
|
||||||
snapshot: &MultiBufferSnapshot,
|
snapshot: &MultiBufferSnapshot,
|
||||||
) -> bool {
|
) -> Option<&'a Range<Anchor>> {
|
||||||
if let [(range, new_text)] = edits {
|
let [(range, _)] = edits else {
|
||||||
match new_text.len() {
|
return None;
|
||||||
0 => {
|
};
|
||||||
let range = range.to_point(&snapshot);
|
|
||||||
range.start.row == range.end.row
|
let point_range = range.to_point(&snapshot);
|
||||||
}
|
if point_range.start.row == point_range.end.row {
|
||||||
1 => true,
|
Some(range)
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
false
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6507,6 +6550,7 @@ impl Element for EditorElement {
|
||||||
inline_blame = self.layout_inline_blame(
|
inline_blame = self.layout_inline_blame(
|
||||||
display_row,
|
display_row,
|
||||||
&snapshot.display_snapshot,
|
&snapshot.display_snapshot,
|
||||||
|
&snapshot.buffer_snapshot,
|
||||||
line_layout,
|
line_layout,
|
||||||
crease_trailer_layout,
|
crease_trailer_layout,
|
||||||
em_width,
|
em_width,
|
||||||
|
|
Loading…
Reference in a new issue