Position prediction popover adjacent to edit if possible (#24750)

Also, place accept key binding indicator on right edge of popover to
avoid obscuring content above.

Release Notes:

- N/A

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: rtfeldman <oss@rtfeldman.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
Max Brunsfeld
2025-02-12 12:11:49 -08:00
committed by GitHub
co-authored by Danilo Leal rtfeldman Agus Zubiaga
parent eea6b526dc
commit 316b97d6e3
+48 -54
View File
@@ -3808,39 +3808,38 @@ impl EditorElement {
);
let styled_text = highlighted_edits.to_styled_text(&style.text);
let line_count = highlighted_edits.text.lines().count();
const ACCEPT_INDICATOR_HEIGHT: Pixels = px(24.);
const BORDER_WIDTH: Pixels = px(1.);
let mut element = v_flex()
.items_end()
let mut element = h_flex()
.items_start()
.child(
div()
.bg(cx.theme().colors().editor_background)
.border(BORDER_WIDTH)
.shadow_sm()
.border_color(cx.theme().colors().border)
.rounded_l_lg()
.when(line_count > 1, |el| el.rounded_br_lg())
.pr_1()
.child(styled_text),
)
.child(
h_flex()
.h(ACCEPT_INDICATOR_HEIGHT)
.mb(px(-1.))
.h(line_height + BORDER_WIDTH * px(2.))
.px_1p5()
.gap_1()
.shadow_sm()
.bg(Editor::edit_prediction_line_popover_bg_color(cx))
.border_1()
.border_b_0()
.border(BORDER_WIDTH)
.border_l_0()
.border_color(cx.theme().colors().border)
.rounded_t_lg()
.rounded_r_lg()
.children(editor.render_edit_prediction_accept_keybind(window, cx)),
)
.child(
div()
.bg(cx.theme().colors().editor_background)
.border_1()
.shadow_sm()
.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 =
editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
let longest_line_width = if visible_row_range.contains(&longest_row) {
@@ -3871,55 +3870,50 @@ impl EditorElement {
// Fully visible if it can be displayed within the window (allow overlapping other
// panes). However, this is only allowed if the popover starts within text_bounds.
let is_fully_visible = x_after_longest < text_bounds.right()
let can_position_to_the_right = x_after_longest < text_bounds.right()
&& x_after_longest + element_bounds.width < viewport_bounds.right();
let mut origin = if is_fully_visible {
let mut origin = if can_position_to_the_right {
point(
x_after_longest,
text_bounds.origin.y + edit_start.row().as_f32() * line_height
- scroll_pixel_position.y,
)
} else {
// Avoid overlapping both the edited rows and the user's cursor.
let target_above = DisplayRow(
edit_start
.row()
.0
.min(
newest_selection_head
.map_or(u32::MAX, |cursor_row| cursor_row.row().0),
)
.saturating_sub(line_count as u32),
);
let mut row_target;
if visible_row_range.contains(&DisplayRow(target_above.0.saturating_sub(1))) {
row_target = target_above;
} else {
row_target = DisplayRow(
edit_end.row().0.max(
newest_selection_head.map_or(0, |cursor_row| cursor_row.row().0),
) + 1,
);
if !visible_row_range.contains(&row_target) {
// Not visible, so fallback on displaying immediately below the cursor.
if let Some(cursor) = newest_selection_head {
row_target = DisplayRow(cursor.row().0 + 1);
} else {
// Not visible and no cursor visible, so fallback on displaying at the top of the editor.
row_target = DisplayRow(0);
}
}
};
let cursor_row = newest_selection_head.map(|head| head.row());
let above_edit = edit_start
.row()
.0
.checked_sub(line_count as u32)
.map(DisplayRow);
let below_edit = Some(edit_end.row() + 1);
let above_cursor = cursor_row
.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
text_bounds.origin
// Place the edit popover adjacent to the edit if there is a location
// available that is onscreen and does not obscure the cursor. Otherwise,
// place it adjacent to the cursor.
let row_target = [above_edit, below_edit, above_cursor, below_cursor]
.into_iter()
.flatten()
.find(|&start_row| {
let end_row = start_row + line_count as u32;
visible_row_range.contains(&start_row)
&& visible_row_range.contains(&end_row)
&& cursor_row.map_or(true, |cursor_row| {
!((start_row..end_row).contains(&cursor_row))
})
})?;
content_origin
+ point(
-scroll_pixel_position.x,
row_target.as_f32() * line_height - scroll_pixel_position.y,
)
};
origin.y -= ACCEPT_INDICATOR_HEIGHT;
origin.x -= BORDER_WIDTH;
window.defer_draw(element, origin, 1);