editor: Reapply fix for wrap guide positioning (#33776)

Reapplies the fix from #33514 which was removed in #33554. Wrap guides
are currently drifting again due to this on main.

Slightly changed the approach here so that we now actually only save the
wrap guides in the `EditorLayout` that will actually be painted. Also
ensures that we paint indent guides that were previously hidden behind
the vertical scrollbar once it auto-hides.

I wanted to add tests for this, however, I am rather sure this depends
on the work/fixes in #33590 and thus I'd prefer to add these later so we
can have this fix in the next release.

Release Notes:

- N/A
This commit is contained in:
Finn Evers 2025-07-03 10:44:17 +02:00 committed by GitHub
parent 5c88e9c66b
commit 06ddc74917
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2444,7 +2444,7 @@ impl EditorElement {
.git
.inline_blame
.and_then(|settings| settings.min_column)
.map(|col| self.column_pixels(col as usize, window, cx))
.map(|col| self.column_pixels(col as usize, window))
.unwrap_or(px(0.));
let min_start = content_origin.x - scroll_pixel_position.x + min_column_in_pixels;
@ -2629,7 +2629,7 @@ impl EditorElement {
.enumerate()
.filter_map(|(i, indent_guide)| {
let single_indent_width =
self.column_pixels(indent_guide.tab_size as usize, window, cx);
self.column_pixels(indent_guide.tab_size as usize, window);
let total_width = single_indent_width * indent_guide.depth as f32;
let start_x = content_origin.x + total_width - scroll_pixel_position.x;
if start_x >= text_origin.x {
@ -2657,6 +2657,39 @@ impl EditorElement {
)
}
fn layout_wrap_guides(
&self,
em_advance: Pixels,
scroll_position: gpui::Point<f32>,
content_origin: gpui::Point<Pixels>,
scrollbar_layout: Option<&EditorScrollbars>,
vertical_scrollbar_width: Pixels,
hitbox: &Hitbox,
window: &Window,
cx: &App,
) -> SmallVec<[(Pixels, bool); 2]> {
let scroll_left = scroll_position.x * em_advance;
let content_origin = content_origin.x;
let horizontal_offset = content_origin - scroll_left;
let vertical_scrollbar_width = scrollbar_layout
.and_then(|layout| layout.visible.then_some(vertical_scrollbar_width))
.unwrap_or_default();
self.editor
.read(cx)
.wrap_guides(cx)
.into_iter()
.flat_map(|(guide, active)| {
let wrap_position = self.column_pixels(guide, window);
let wrap_guide_x = wrap_position + horizontal_offset;
let display_wrap_guide = wrap_guide_x >= content_origin
&& wrap_guide_x <= hitbox.bounds.right() - vertical_scrollbar_width;
display_wrap_guide.then_some((wrap_guide_x, active))
})
.collect()
}
fn calculate_indent_guide_bounds(
row_range: Range<MultiBufferRow>,
line_height: Pixels,
@ -5240,26 +5273,7 @@ impl EditorElement {
paint_highlight(range.start, range.end, color, edges);
}
let scroll_left =
layout.position_map.snapshot.scroll_position().x * layout.position_map.em_width;
for (wrap_position, active) in layout.wrap_guides.iter() {
let x = (layout.position_map.text_hitbox.origin.x
+ *wrap_position
+ layout.position_map.em_width / 2.)
- scroll_left;
let show_scrollbars = layout
.scrollbars_layout
.as_ref()
.map_or(false, |layout| layout.visible);
if x < layout.position_map.text_hitbox.origin.x
|| (show_scrollbars && x > self.scrollbar_left(&layout.hitbox.bounds))
{
continue;
}
for (guide_x, active) in layout.wrap_guides.iter() {
let color = if *active {
cx.theme().colors().editor_active_wrap_guide
} else {
@ -5267,7 +5281,7 @@ impl EditorElement {
};
window.paint_quad(fill(
Bounds {
origin: point(x, layout.position_map.text_hitbox.origin.y),
origin: point(*guide_x, layout.position_map.text_hitbox.origin.y),
size: size(px(1.), layout.position_map.text_hitbox.size.height),
},
color,
@ -6678,7 +6692,7 @@ impl EditorElement {
let position_map: &PositionMap = &position_map;
let line_height = position_map.line_height;
let max_glyph_width = position_map.em_width;
let max_glyph_advance = position_map.em_advance;
let (delta, axis) = match delta {
gpui::ScrollDelta::Pixels(mut pixels) => {
//Trackpad
@ -6689,15 +6703,15 @@ impl EditorElement {
gpui::ScrollDelta::Lines(lines) => {
//Not trackpad
let pixels =
point(lines.x * max_glyph_width, lines.y * line_height);
point(lines.x * max_glyph_advance, lines.y * line_height);
(pixels, None)
}
};
let current_scroll_position = position_map.snapshot.scroll_position();
let x = (current_scroll_position.x * max_glyph_width
let x = (current_scroll_position.x * max_glyph_advance
- (delta.x * scroll_sensitivity))
/ max_glyph_width;
/ max_glyph_advance;
let y = (current_scroll_position.y * line_height
- (delta.y * scroll_sensitivity))
/ line_height;
@ -6858,11 +6872,7 @@ impl EditorElement {
});
}
fn scrollbar_left(&self, bounds: &Bounds<Pixels>) -> Pixels {
bounds.top_right().x - self.style.scrollbar_width
}
fn column_pixels(&self, column: usize, window: &mut Window, _: &mut App) -> Pixels {
fn column_pixels(&self, column: usize, window: &Window) -> Pixels {
let style = &self.style;
let font_size = style.text.font_size.to_pixels(window.rem_size());
let layout = window.text_system().shape_line(
@ -6881,14 +6891,9 @@ impl EditorElement {
layout.width
}
fn max_line_number_width(
&self,
snapshot: &EditorSnapshot,
window: &mut Window,
cx: &mut App,
) -> Pixels {
fn max_line_number_width(&self, snapshot: &EditorSnapshot, window: &mut Window) -> Pixels {
let digit_count = snapshot.widest_line_number().ilog10() + 1;
self.column_pixels(digit_count as usize, window, cx)
self.column_pixels(digit_count as usize, window)
}
fn shape_line_number(
@ -7789,7 +7794,7 @@ impl Element for EditorElement {
} => {
let editor_handle = cx.entity().clone();
let max_line_number_width =
self.max_line_number_width(&editor.snapshot(window, cx), window, cx);
self.max_line_number_width(&editor.snapshot(window, cx), window);
window.request_measured_layout(
Style::default(),
move |known_dimensions, available_space, window, cx| {
@ -7879,7 +7884,7 @@ impl Element for EditorElement {
.gutter_dimensions(
font_id,
font_size,
self.max_line_number_width(&snapshot, window, cx),
self.max_line_number_width(&snapshot, window),
cx,
)
.or_else(|| {
@ -7954,14 +7959,6 @@ impl Element for EditorElement {
}
});
let wrap_guides = self
.editor
.read(cx)
.wrap_guides(cx)
.iter()
.map(|(guide, active)| (self.column_pixels(*guide, window, cx), *active))
.collect::<SmallVec<[_; 2]>>();
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
let gutter_hitbox = window.insert_hitbox(
gutter_bounds(bounds, gutter_dimensions),
@ -8593,7 +8590,7 @@ impl Element for EditorElement {
start_row,
editor_content_width,
scroll_width,
em_width,
em_advance,
&line_layouts,
cx,
)
@ -8797,6 +8794,17 @@ impl Element for EditorElement {
self.prepaint_expand_toggles(&mut expand_toggles, window, cx)
});
let wrap_guides = self.layout_wrap_guides(
em_advance,
scroll_position,
content_origin,
scrollbars_layout.as_ref(),
vertical_scrollbar_width,
&hitbox,
window,
cx,
);
let minimap = window.with_element_namespace("minimap", |window| {
self.layout_minimap(
&snapshot,