editor: Represent scroll offset with more precision (#39367)
Closes #5355 Release Notes: - Fixed rendering glitches with files with more than 16 million lines (that occured due to floating number rounding errors). --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This commit is contained in:
co-authored by
Smit Barmase
parent
4c35274b6e
commit
d359a814f8
@@ -2,7 +2,7 @@ use super::Axis;
|
||||
use crate::{
|
||||
Autoscroll, Editor, EditorMode, NextScreen, NextScrollCursorCenterTopBottom,
|
||||
SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT, ScrollCursorBottom, ScrollCursorCenter,
|
||||
ScrollCursorCenterTopBottom, ScrollCursorTop, display_map::DisplayRow,
|
||||
ScrollCursorCenterTopBottom, ScrollCursorTop, display_map::DisplayRow, scroll::ScrollOffset,
|
||||
};
|
||||
use gpui::{Context, Point, Window};
|
||||
|
||||
@@ -25,7 +25,7 @@ impl Editor {
|
||||
|
||||
pub fn scroll(
|
||||
&mut self,
|
||||
scroll_position: Point<f32>,
|
||||
scroll_position: Point<ScrollOffset>,
|
||||
axis: Option<Axis>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::{
|
||||
DisplayRow, Editor, EditorMode, LineWithInvisibles, RowExt, SelectionEffects,
|
||||
display_map::ToDisplayPoint, scroll::WasScrolled,
|
||||
display_map::ToDisplayPoint,
|
||||
scroll::{ScrollOffset, WasScrolled},
|
||||
};
|
||||
use gpui::{Bounds, Context, Pixels, Window, px};
|
||||
use gpui::{Bounds, Context, Pixels, Window};
|
||||
use language::Point;
|
||||
use multi_buffer::Anchor;
|
||||
use std::{cmp, f32};
|
||||
use std::cmp;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Autoscroll {
|
||||
@@ -106,20 +107,21 @@ impl Editor {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
line_height: Pixels,
|
||||
max_scroll_top: f32,
|
||||
max_scroll_top: ScrollOffset,
|
||||
autoscroll_request: Option<(Autoscroll, bool)>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) -> (NeedsHorizontalAutoscroll, WasScrolled) {
|
||||
let viewport_height = bounds.size.height;
|
||||
let visible_lines = viewport_height / line_height;
|
||||
let visible_lines = ScrollOffset::from(viewport_height / line_height);
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
|
||||
let original_y = scroll_position.y;
|
||||
if let Some(last_bounds) = self.expect_bounds_change.take()
|
||||
&& scroll_position.y != 0.
|
||||
{
|
||||
scroll_position.y += (bounds.top() - last_bounds.top()) / line_height;
|
||||
scroll_position.y +=
|
||||
ScrollOffset::from((bounds.top() - last_bounds.top()) / line_height);
|
||||
if scroll_position.y < 0. {
|
||||
scroll_position.y = 0.;
|
||||
}
|
||||
@@ -143,7 +145,7 @@ impl Editor {
|
||||
if let Some(first_highlighted_row) =
|
||||
self.highlighted_display_row_for_autoscroll(&display_map)
|
||||
{
|
||||
target_top = first_highlighted_row.as_f32();
|
||||
target_top = first_highlighted_row.as_f64();
|
||||
target_bottom = target_top + 1.;
|
||||
} else {
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
@@ -154,7 +156,7 @@ impl Editor {
|
||||
.head()
|
||||
.to_display_point(&display_map)
|
||||
.row()
|
||||
.as_f32();
|
||||
.as_f64();
|
||||
target_bottom = selections
|
||||
.last()
|
||||
.unwrap()
|
||||
@@ -162,7 +164,7 @@ impl Editor {
|
||||
.to_display_point(&display_map)
|
||||
.row()
|
||||
.next_row()
|
||||
.as_f32();
|
||||
.as_f64();
|
||||
|
||||
let selections_fit = target_bottom - target_top <= visible_lines;
|
||||
if matches!(
|
||||
@@ -178,7 +180,7 @@ impl Editor {
|
||||
.head()
|
||||
.to_display_point(&display_map)
|
||||
.row()
|
||||
.as_f32();
|
||||
.as_f64();
|
||||
target_top = newest_selection_top;
|
||||
target_bottom = newest_selection_top + 1.;
|
||||
}
|
||||
@@ -209,7 +211,7 @@ impl Editor {
|
||||
}
|
||||
};
|
||||
if let Autoscroll::Strategy(_, Some(anchor)) = autoscroll {
|
||||
target_top = anchor.to_display_point(&display_map).row().as_f32();
|
||||
target_top = anchor.to_display_point(&display_map).row().as_f64();
|
||||
target_bottom = target_top + 1.;
|
||||
}
|
||||
|
||||
@@ -254,11 +256,11 @@ impl Editor {
|
||||
self.set_scroll_position_internal(scroll_position, local, true, window, cx)
|
||||
}
|
||||
AutoscrollStrategy::TopRelative(lines) => {
|
||||
scroll_position.y = target_top - lines as f32;
|
||||
scroll_position.y = target_top - lines as ScrollOffset;
|
||||
self.set_scroll_position_internal(scroll_position, local, true, window, cx)
|
||||
}
|
||||
AutoscrollStrategy::BottomRelative(lines) => {
|
||||
scroll_position.y = target_bottom + lines as f32;
|
||||
scroll_position.y = target_bottom + lines as ScrollOffset;
|
||||
self.set_scroll_position_internal(scroll_position, local, true, window, cx)
|
||||
}
|
||||
};
|
||||
@@ -284,22 +286,25 @@ impl Editor {
|
||||
autoscroll_request: Option<(Autoscroll, bool)>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<gpui::Point<f32>> {
|
||||
) -> Option<gpui::Point<ScrollOffset>> {
|
||||
let (_, local) = autoscroll_request?;
|
||||
let em_advance = ScrollOffset::from(em_advance);
|
||||
let viewport_width = ScrollOffset::from(viewport_width);
|
||||
let scroll_width = ScrollOffset::from(scroll_width);
|
||||
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
|
||||
|
||||
let mut target_left;
|
||||
let mut target_right;
|
||||
let mut target_right: f64;
|
||||
|
||||
if self
|
||||
.highlighted_display_row_for_autoscroll(&display_map)
|
||||
.is_none()
|
||||
{
|
||||
target_left = px(f32::INFINITY);
|
||||
target_right = px(0.);
|
||||
target_left = f64::INFINITY;
|
||||
target_right = 0.;
|
||||
for selection in selections {
|
||||
let head = selection.head().to_display_point(&display_map);
|
||||
if head.row() >= start_row
|
||||
@@ -307,21 +312,22 @@ impl Editor {
|
||||
{
|
||||
let start_column = head.column();
|
||||
let end_column = cmp::min(display_map.line_len(head.row()), head.column());
|
||||
target_left = target_left.min(
|
||||
target_left = target_left.min(ScrollOffset::from(
|
||||
layouts[head.row().minus(start_row) as usize]
|
||||
.x_for_index(start_column as usize)
|
||||
+ self.gutter_dimensions.margin,
|
||||
);
|
||||
));
|
||||
target_right = target_right.max(
|
||||
layouts[head.row().minus(start_row) as usize]
|
||||
.x_for_index(end_column as usize)
|
||||
+ em_advance,
|
||||
ScrollOffset::from(
|
||||
layouts[head.row().minus(start_row) as usize]
|
||||
.x_for_index(end_column as usize),
|
||||
) + em_advance,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
target_left = px(0.);
|
||||
target_right = px(0.);
|
||||
target_left = 0.;
|
||||
target_right = 0.;
|
||||
}
|
||||
|
||||
target_right = target_right.min(scroll_width);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use ui::{Pixels, px};
|
||||
use ui::Pixels;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScrollDirection {
|
||||
@@ -28,41 +28,41 @@ pub enum ScrollAmount {
|
||||
}
|
||||
|
||||
impl ScrollAmount {
|
||||
pub fn lines(&self, mut visible_line_count: f32) -> f32 {
|
||||
pub fn lines(&self, mut visible_line_count: f64) -> f64 {
|
||||
match self {
|
||||
Self::Line(count) => *count,
|
||||
Self::Line(count) => *count as f64,
|
||||
Self::Page(count) => {
|
||||
// for full pages subtract one to leave an anchor line
|
||||
if self.is_full_page() {
|
||||
visible_line_count -= 1.0
|
||||
}
|
||||
(visible_line_count * count).trunc()
|
||||
(visible_line_count * (*count as f64)).trunc()
|
||||
}
|
||||
Self::Column(_count) => 0.0,
|
||||
Self::PageWidth(_count) => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn columns(&self, visible_column_count: f32) -> f32 {
|
||||
pub fn columns(&self, visible_column_count: f64) -> f64 {
|
||||
match self {
|
||||
Self::Line(_count) => 0.0,
|
||||
Self::Page(_count) => 0.0,
|
||||
Self::Column(count) => *count,
|
||||
Self::PageWidth(count) => (visible_column_count * count).trunc(),
|
||||
Self::Column(count) => *count as f64,
|
||||
Self::PageWidth(count) => (visible_column_count * *count as f64).trunc(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pixels(&self, line_height: Pixels, height: Pixels) -> Pixels {
|
||||
match self {
|
||||
ScrollAmount::Line(x) => px(line_height.0 * x),
|
||||
ScrollAmount::Page(x) => px(height.0 * x),
|
||||
ScrollAmount::Line(x) => line_height * *x,
|
||||
ScrollAmount::Page(x) => height * *x,
|
||||
// This function seems to only be leveraged by the popover that is
|
||||
// displayed by the editor when, for example, viewing a function's
|
||||
// documentation. Right now that only supports vertical scrolling,
|
||||
// so I'm leaving this at 0.0 for now to try and make it clear that
|
||||
// this should not have an impact on that?
|
||||
ScrollAmount::Column(_) => px(0.0),
|
||||
ScrollAmount::PageWidth(_) => px(0.0),
|
||||
ScrollAmount::Column(_) => Pixels::ZERO,
|
||||
ScrollAmount::PageWidth(_) => Pixels::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user