Do not subtract gutter margin twice from the editor width (#34564)

Closes #33176

In auto-height layouts, horizontal autoscroll can occur right before
soft wrapping is triggered. This seems to be caused by gutter margin
being subtracted twice from the editor width.

If we subtract gutter width only once, the horizontal autoscroll
behavior goes away.

Before:


https://github.com/user-attachments/assets/03b6687e-3c0e-4b34-8e07-a228bcc6f798

After:


https://github.com/user-attachments/assets/84e54088-b5bd-4259-a193-d9fcf32cd3a7

Release Notes:

- Fixes issue with auto-height layouts where horizontal autoscroll is
triggered right before text wraps

---------

Co-authored-by: MrSubidubi <finn@zed.dev>
This commit is contained in:
Daniel Sauble 2025-07-24 17:00:59 -07:00 committed by GitHub
parent f78a112387
commit 4ee52433ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -7944,17 +7944,11 @@ impl Element for EditorElement {
right: right_margin,
};
// Offset the content_bounds from the text_bounds by the gutter margin (which
// is roughly half a character wide) to make hit testing work more like how we want.
let content_offset = point(editor_margins.gutter.margin, Pixels::ZERO);
let editor_content_width = editor_width - content_offset.x;
snapshot = self.editor.update(cx, |editor, cx| {
editor.last_bounds = Some(bounds);
editor.gutter_dimensions = gutter_dimensions;
editor.set_visible_line_count(bounds.size.height / line_height, window, cx);
editor.set_visible_column_count(editor_content_width / em_advance);
editor.set_visible_column_count(editor_width / em_advance);
if matches!(
editor.mode,
@ -7966,10 +7960,10 @@ impl Element for EditorElement {
let wrap_width = match editor.soft_wrap_mode(cx) {
SoftWrap::GitDiff => None,
SoftWrap::None => Some(wrap_width_for(MAX_LINE_LEN as u32 / 2)),
SoftWrap::EditorWidth => Some(editor_content_width),
SoftWrap::EditorWidth => Some(editor_width),
SoftWrap::Column(column) => Some(wrap_width_for(column)),
SoftWrap::Bounded(column) => {
Some(editor_content_width.min(wrap_width_for(column)))
Some(editor_width.min(wrap_width_for(column)))
}
};
@ -7994,13 +7988,12 @@ impl Element for EditorElement {
HitboxBehavior::Normal,
);
// Offset the content_bounds from the text_bounds by the gutter margin (which
// is roughly half a character wide) to make hit testing work more like how we want.
let content_offset = point(editor_margins.gutter.margin, Pixels::ZERO);
let content_origin = text_hitbox.origin + content_offset;
let editor_text_bounds =
Bounds::from_corners(content_origin, bounds.bottom_right());
let height_in_lines = editor_text_bounds.size.height / line_height;
let height_in_lines = bounds.size.height / line_height;
let max_row = snapshot.max_point().row().as_f32();
// The max scroll position for the top of the window
@ -8384,7 +8377,6 @@ impl Element for EditorElement {
glyph_grid_cell,
size(longest_line_width, max_row.as_f32() * line_height),
longest_line_blame_width,
editor_width,
EditorSettings::get_global(cx),
);
@ -8456,7 +8448,7 @@ impl Element for EditorElement {
MultiBufferRow(end_anchor.to_point(&snapshot.buffer_snapshot).row);
let scroll_max = point(
((scroll_width - editor_content_width) / em_advance).max(0.0),
((scroll_width - editor_width) / em_advance).max(0.0),
max_scroll_top,
);
@ -8468,7 +8460,7 @@ impl Element for EditorElement {
if needs_horizontal_autoscroll.0
&& let Some(new_scroll_position) = editor.autoscroll_horizontally(
start_row,
editor_content_width,
editor_width,
scroll_width,
em_advance,
&line_layouts,
@ -9049,7 +9041,6 @@ impl ScrollbarLayoutInformation {
glyph_grid_cell: Size<Pixels>,
document_size: Size<Pixels>,
longest_line_blame_width: Pixels,
editor_width: Pixels,
settings: &EditorSettings,
) -> Self {
let vertical_overscroll = match settings.scroll_beyond_last_line {
@ -9060,19 +9051,11 @@ impl ScrollbarLayoutInformation {
}
};
let right_margin = if document_size.width + longest_line_blame_width >= editor_width {
glyph_grid_cell.width
} else {
px(0.0)
};
let overscroll = size(right_margin + longest_line_blame_width, vertical_overscroll);
let scroll_range = document_size + overscroll;
let overscroll = size(longest_line_blame_width, vertical_overscroll);
ScrollbarLayoutInformation {
editor_bounds,
scroll_range,
scroll_range: document_size + overscroll,
glyph_grid_cell,
}
}
@ -9177,7 +9160,7 @@ struct EditorScrollbars {
impl EditorScrollbars {
pub fn from_scrollbar_axes(
settings_visibility: ScrollbarAxes,
show_scrollbar: ScrollbarAxes,
layout_information: &ScrollbarLayoutInformation,
content_offset: gpui::Point<Pixels>,
scroll_position: gpui::Point<f32>,
@ -9215,22 +9198,13 @@ impl EditorScrollbars {
};
let mut create_scrollbar_layout = |axis| {
settings_visibility
.along(axis)
let viewport_size = viewport_size.along(axis);
let scroll_range = scroll_range.along(axis);
// We always want a vertical scrollbar track for scrollbar diagnostic visibility.
(show_scrollbar.along(axis)
&& (axis == ScrollbarAxis::Vertical || scroll_range > viewport_size))
.then(|| {
(
viewport_size.along(axis) - content_offset.along(axis),
scroll_range.along(axis),
)
})
.filter(|(viewport_size, scroll_range)| {
// The scrollbar should only be rendered if the content does
// not entirely fit into the editor
// However, this only applies to the horizontal scrollbar, as information about the
// vertical scrollbar layout is always needed for scrollbar diagnostics.
axis != ScrollbarAxis::Horizontal || viewport_size < scroll_range
})
.map(|(viewport_size, scroll_range)| {
ScrollbarLayout::new(
window.insert_hitbox(scrollbar_bounds_for(axis), HitboxBehavior::Normal),
viewport_size,