scroll.rs

  1mod actions;
  2pub(crate) mod autoscroll;
  3pub(crate) mod scroll_amount;
  4
  5use crate::editor_settings::ScrollBeyondLastLine;
  6use crate::{
  7    Anchor, DisplayPoint, DisplayRow, Editor, EditorEvent, EditorMode, EditorSettings,
  8    InlayHintRefreshReason, MultiBufferSnapshot, RowExt, ToPoint,
  9    display_map::{DisplaySnapshot, ToDisplayPoint},
 10    hover_popover::hide_hover,
 11    persistence::DB,
 12};
 13pub use autoscroll::{Autoscroll, AutoscrollStrategy};
 14use core::fmt::Debug;
 15use gpui::{Along, App, Axis, Context, Pixels, Task, Window, point, px};
 16use language::language_settings::{AllLanguageSettings, SoftWrap};
 17use language::{Bias, Point};
 18pub use scroll_amount::ScrollAmount;
 19use settings::Settings;
 20use std::{
 21    cmp::Ordering,
 22    time::{Duration, Instant},
 23};
 24use ui::scrollbars::ScrollbarAutoHide;
 25use util::ResultExt;
 26use workspace::{ItemId, WorkspaceId};
 27
 28pub const SCROLL_EVENT_SEPARATION: Duration = Duration::from_millis(28);
 29const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
 30
 31pub struct WasScrolled(pub(crate) bool);
 32
 33#[derive(Clone, Copy, Debug, PartialEq)]
 34pub struct ScrollAnchor {
 35    pub offset: gpui::Point<f32>,
 36    pub anchor: Anchor,
 37}
 38
 39impl ScrollAnchor {
 40    pub(super) fn new() -> Self {
 41        Self {
 42            offset: gpui::Point::default(),
 43            anchor: Anchor::min(),
 44        }
 45    }
 46
 47    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
 48        self.offset.apply_along(Axis::Vertical, |offset| {
 49            if self.anchor == Anchor::min() {
 50                0.
 51            } else {
 52                let scroll_top = self.anchor.to_display_point(snapshot).row().as_f32();
 53                (offset + scroll_top).max(0.)
 54            }
 55        })
 56    }
 57
 58    pub fn top_row(&self, buffer: &MultiBufferSnapshot) -> u32 {
 59        self.anchor.to_point(buffer).row
 60    }
 61}
 62
 63#[derive(Clone, Copy, Debug)]
 64pub struct OngoingScroll {
 65    last_event: Instant,
 66    axis: Option<Axis>,
 67}
 68
 69impl OngoingScroll {
 70    fn new() -> Self {
 71        Self {
 72            last_event: Instant::now() - SCROLL_EVENT_SEPARATION,
 73            axis: None,
 74        }
 75    }
 76
 77    pub fn filter(&self, delta: &mut gpui::Point<Pixels>) -> Option<Axis> {
 78        const UNLOCK_PERCENT: f32 = 1.9;
 79        const UNLOCK_LOWER_BOUND: Pixels = px(6.);
 80        let mut axis = self.axis;
 81
 82        let x = delta.x.abs();
 83        let y = delta.y.abs();
 84        let duration = Instant::now().duration_since(self.last_event);
 85        if duration > SCROLL_EVENT_SEPARATION {
 86            //New ongoing scroll will start, determine axis
 87            axis = if x <= y {
 88                Some(Axis::Vertical)
 89            } else {
 90                Some(Axis::Horizontal)
 91            };
 92        } else if x.max(y) >= UNLOCK_LOWER_BOUND {
 93            //Check if the current ongoing will need to unlock
 94            match axis {
 95                Some(Axis::Vertical) => {
 96                    if x > y && x >= y * UNLOCK_PERCENT {
 97                        axis = None;
 98                    }
 99                }
100
101                Some(Axis::Horizontal) => {
102                    if y > x && y >= x * UNLOCK_PERCENT {
103                        axis = None;
104                    }
105                }
106
107                None => {}
108            }
109        }
110
111        match axis {
112            Some(Axis::Vertical) => {
113                *delta = point(px(0.), delta.y);
114            }
115            Some(Axis::Horizontal) => {
116                *delta = point(delta.x, px(0.));
117            }
118            None => {}
119        }
120
121        axis
122    }
123}
124
125#[derive(Copy, Clone, Default, PartialEq, Eq)]
126pub enum ScrollbarThumbState {
127    #[default]
128    Idle,
129    Hovered,
130    Dragging,
131}
132
133#[derive(PartialEq, Eq)]
134pub struct ActiveScrollbarState {
135    axis: Axis,
136    thumb_state: ScrollbarThumbState,
137}
138
139impl ActiveScrollbarState {
140    pub fn new(axis: Axis, thumb_state: ScrollbarThumbState) -> Self {
141        ActiveScrollbarState { axis, thumb_state }
142    }
143
144    pub fn thumb_state_for_axis(&self, axis: Axis) -> Option<ScrollbarThumbState> {
145        (self.axis == axis).then_some(self.thumb_state)
146    }
147}
148
149pub struct ScrollManager {
150    pub(crate) vertical_scroll_margin: f32,
151    anchor: ScrollAnchor,
152    ongoing: OngoingScroll,
153    /// The second element indicates whether the autoscroll request is local
154    /// (true) or remote (false). Local requests are initiated by user actions,
155    /// while remote requests come from external sources.
156    autoscroll_request: Option<(Autoscroll, bool)>,
157    last_autoscroll: Option<(gpui::Point<f32>, f32, f32, AutoscrollStrategy)>,
158    show_scrollbars: bool,
159    hide_scrollbar_task: Option<Task<()>>,
160    active_scrollbar: Option<ActiveScrollbarState>,
161    visible_line_count: Option<f32>,
162    visible_column_count: Option<f32>,
163    forbid_vertical_scroll: bool,
164    minimap_thumb_state: Option<ScrollbarThumbState>,
165}
166
167impl ScrollManager {
168    pub fn new(cx: &mut App) -> Self {
169        ScrollManager {
170            vertical_scroll_margin: EditorSettings::get_global(cx).vertical_scroll_margin,
171            anchor: ScrollAnchor::new(),
172            ongoing: OngoingScroll::new(),
173            autoscroll_request: None,
174            show_scrollbars: true,
175            hide_scrollbar_task: None,
176            active_scrollbar: None,
177            last_autoscroll: None,
178            visible_line_count: None,
179            visible_column_count: None,
180            forbid_vertical_scroll: false,
181            minimap_thumb_state: None,
182        }
183    }
184
185    pub fn clone_state(&mut self, other: &Self) {
186        self.anchor = other.anchor;
187        self.ongoing = other.ongoing;
188    }
189
190    pub fn anchor(&self) -> ScrollAnchor {
191        self.anchor
192    }
193
194    pub fn ongoing_scroll(&self) -> OngoingScroll {
195        self.ongoing
196    }
197
198    pub fn update_ongoing_scroll(&mut self, axis: Option<Axis>) {
199        self.ongoing.last_event = Instant::now();
200        self.ongoing.axis = axis;
201    }
202
203    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
204        self.anchor.scroll_position(snapshot)
205    }
206
207    fn set_scroll_position(
208        &mut self,
209        scroll_position: gpui::Point<f32>,
210        map: &DisplaySnapshot,
211        local: bool,
212        autoscroll: bool,
213        workspace_id: Option<WorkspaceId>,
214        window: &mut Window,
215        cx: &mut Context<Editor>,
216    ) -> WasScrolled {
217        let scroll_top = scroll_position.y.max(0.);
218        let scroll_top = match EditorSettings::get_global(cx).scroll_beyond_last_line {
219            ScrollBeyondLastLine::OnePage => scroll_top,
220            ScrollBeyondLastLine::Off => {
221                if let Some(height_in_lines) = self.visible_line_count {
222                    let max_row = map.max_point().row().0 as f32;
223                    scroll_top.min(max_row - height_in_lines + 1.).max(0.)
224                } else {
225                    scroll_top
226                }
227            }
228            ScrollBeyondLastLine::VerticalScrollMargin => {
229                if let Some(height_in_lines) = self.visible_line_count {
230                    let max_row = map.max_point().row().0 as f32;
231                    scroll_top
232                        .min(max_row - height_in_lines + 1. + self.vertical_scroll_margin)
233                        .max(0.)
234                } else {
235                    scroll_top
236                }
237            }
238        };
239
240        let scroll_top_row = DisplayRow(scroll_top as u32);
241        let scroll_top_buffer_point = map
242            .clip_point(
243                DisplayPoint::new(scroll_top_row, scroll_position.x as u32),
244                Bias::Left,
245            )
246            .to_point(map);
247        let top_anchor = map
248            .buffer_snapshot
249            .anchor_at(scroll_top_buffer_point, Bias::Right);
250
251        self.set_anchor(
252            ScrollAnchor {
253                anchor: top_anchor,
254                offset: point(
255                    scroll_position.x.max(0.),
256                    scroll_top - top_anchor.to_display_point(map).row().as_f32(),
257                ),
258            },
259            scroll_top_buffer_point.row,
260            local,
261            autoscroll,
262            workspace_id,
263            window,
264            cx,
265        )
266    }
267
268    fn set_anchor(
269        &mut self,
270        anchor: ScrollAnchor,
271        top_row: u32,
272        local: bool,
273        autoscroll: bool,
274        workspace_id: Option<WorkspaceId>,
275        window: &mut Window,
276        cx: &mut Context<Editor>,
277    ) -> WasScrolled {
278        let adjusted_anchor = if self.forbid_vertical_scroll {
279            ScrollAnchor {
280                offset: gpui::Point::new(anchor.offset.x, self.anchor.offset.y),
281                anchor: self.anchor.anchor,
282            }
283        } else {
284            anchor
285        };
286
287        self.autoscroll_request.take();
288        if self.anchor == adjusted_anchor {
289            return WasScrolled(false);
290        }
291
292        self.anchor = adjusted_anchor;
293        cx.emit(EditorEvent::ScrollPositionChanged { local, autoscroll });
294        self.show_scrollbars(window, cx);
295        if let Some(workspace_id) = workspace_id {
296            let item_id = cx.entity().entity_id().as_u64() as ItemId;
297
298            cx.foreground_executor()
299                .spawn(async move {
300                    log::debug!(
301                        "Saving scroll position for item {item_id:?} in workspace {workspace_id:?}"
302                    );
303                    DB.save_scroll_position(
304                        item_id,
305                        workspace_id,
306                        top_row,
307                        anchor.offset.x,
308                        anchor.offset.y,
309                    )
310                    .await
311                    .log_err()
312                })
313                .detach()
314        }
315        cx.notify();
316
317        WasScrolled(true)
318    }
319
320    pub fn show_scrollbars(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
321        if !self.show_scrollbars {
322            self.show_scrollbars = true;
323            cx.notify();
324        }
325
326        if cx.default_global::<ScrollbarAutoHide>().should_hide() {
327            self.hide_scrollbar_task = Some(cx.spawn_in(window, async move |editor, cx| {
328                cx.background_executor()
329                    .timer(SCROLLBAR_SHOW_INTERVAL)
330                    .await;
331                editor
332                    .update(cx, |editor, cx| {
333                        editor.scroll_manager.show_scrollbars = false;
334                        cx.notify();
335                    })
336                    .log_err();
337            }));
338        } else {
339            self.hide_scrollbar_task = None;
340        }
341    }
342
343    pub fn scrollbars_visible(&self) -> bool {
344        self.show_scrollbars
345    }
346
347    pub fn take_autoscroll_request(&mut self) -> Option<(Autoscroll, bool)> {
348        self.autoscroll_request.take()
349    }
350
351    pub fn active_scrollbar_state(&self) -> Option<&ActiveScrollbarState> {
352        self.active_scrollbar.as_ref()
353    }
354
355    pub fn dragging_scrollbar_axis(&self) -> Option<Axis> {
356        self.active_scrollbar
357            .as_ref()
358            .filter(|scrollbar| scrollbar.thumb_state == ScrollbarThumbState::Dragging)
359            .map(|scrollbar| scrollbar.axis)
360    }
361
362    pub fn any_scrollbar_dragged(&self) -> bool {
363        self.active_scrollbar
364            .as_ref()
365            .is_some_and(|scrollbar| scrollbar.thumb_state == ScrollbarThumbState::Dragging)
366    }
367
368    pub fn set_hovered_scroll_thumb_axis(&mut self, axis: Axis, cx: &mut Context<Editor>) {
369        self.update_active_scrollbar_state(
370            Some(ActiveScrollbarState::new(
371                axis,
372                ScrollbarThumbState::Hovered,
373            )),
374            cx,
375        );
376    }
377
378    pub fn set_dragged_scroll_thumb_axis(&mut self, axis: Axis, cx: &mut Context<Editor>) {
379        self.update_active_scrollbar_state(
380            Some(ActiveScrollbarState::new(
381                axis,
382                ScrollbarThumbState::Dragging,
383            )),
384            cx,
385        );
386    }
387
388    pub fn reset_scrollbar_state(&mut self, cx: &mut Context<Editor>) {
389        self.update_active_scrollbar_state(None, cx);
390    }
391
392    fn update_active_scrollbar_state(
393        &mut self,
394        new_state: Option<ActiveScrollbarState>,
395        cx: &mut Context<Editor>,
396    ) {
397        if self.active_scrollbar != new_state {
398            self.active_scrollbar = new_state;
399            cx.notify();
400        }
401    }
402
403    pub fn set_is_hovering_minimap_thumb(&mut self, hovered: bool, cx: &mut Context<Editor>) {
404        self.update_minimap_thumb_state(
405            Some(if hovered {
406                ScrollbarThumbState::Hovered
407            } else {
408                ScrollbarThumbState::Idle
409            }),
410            cx,
411        );
412    }
413
414    pub fn set_is_dragging_minimap(&mut self, cx: &mut Context<Editor>) {
415        self.update_minimap_thumb_state(Some(ScrollbarThumbState::Dragging), cx);
416    }
417
418    pub fn hide_minimap_thumb(&mut self, cx: &mut Context<Editor>) {
419        self.update_minimap_thumb_state(None, cx);
420    }
421
422    pub fn is_dragging_minimap(&self) -> bool {
423        self.minimap_thumb_state
424            .is_some_and(|state| state == ScrollbarThumbState::Dragging)
425    }
426
427    fn update_minimap_thumb_state(
428        &mut self,
429        thumb_state: Option<ScrollbarThumbState>,
430        cx: &mut Context<Editor>,
431    ) {
432        if self.minimap_thumb_state != thumb_state {
433            self.minimap_thumb_state = thumb_state;
434            cx.notify();
435        }
436    }
437
438    pub fn minimap_thumb_state(&self) -> Option<ScrollbarThumbState> {
439        self.minimap_thumb_state
440    }
441
442    pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
443        if max < self.anchor.offset.x {
444            self.anchor.offset.x = max;
445            true
446        } else {
447            false
448        }
449    }
450
451    pub fn set_forbid_vertical_scroll(&mut self, forbid: bool) {
452        self.forbid_vertical_scroll = forbid;
453    }
454
455    pub fn forbid_vertical_scroll(&self) -> bool {
456        self.forbid_vertical_scroll
457    }
458}
459
460impl Editor {
461    pub fn vertical_scroll_margin(&self) -> usize {
462        self.scroll_manager.vertical_scroll_margin as usize
463    }
464
465    pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut Context<Self>) {
466        self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
467        cx.notify();
468    }
469
470    pub fn visible_line_count(&self) -> Option<f32> {
471        self.scroll_manager.visible_line_count
472    }
473
474    pub fn visible_row_count(&self) -> Option<u32> {
475        self.visible_line_count()
476            .map(|line_count| line_count as u32 - 1)
477    }
478
479    pub fn visible_column_count(&self) -> Option<f32> {
480        self.scroll_manager.visible_column_count
481    }
482
483    pub(crate) fn set_visible_line_count(
484        &mut self,
485        lines: f32,
486        window: &mut Window,
487        cx: &mut Context<Self>,
488    ) {
489        let opened_first_time = self.scroll_manager.visible_line_count.is_none();
490        self.scroll_manager.visible_line_count = Some(lines);
491        if opened_first_time {
492            cx.spawn_in(window, async move |editor, cx| {
493                editor
494                    .update_in(cx, |editor, window, cx| {
495                        editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
496                        editor.refresh_colors(false, None, window, cx);
497                    })
498                    .ok()
499            })
500            .detach()
501        }
502    }
503
504    pub(crate) fn set_visible_column_count(&mut self, columns: f32) {
505        self.scroll_manager.visible_column_count = Some(columns);
506    }
507
508    pub fn apply_scroll_delta(
509        &mut self,
510        scroll_delta: gpui::Point<f32>,
511        window: &mut Window,
512        cx: &mut Context<Self>,
513    ) {
514        let mut delta = scroll_delta;
515        if self.scroll_manager.forbid_vertical_scroll {
516            delta.y = 0.0;
517        }
518        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
519        let position = self.scroll_manager.anchor.scroll_position(&display_map) + delta;
520        self.set_scroll_position_taking_display_map(position, true, false, display_map, window, cx);
521    }
522
523    pub fn set_scroll_position(
524        &mut self,
525        scroll_position: gpui::Point<f32>,
526        window: &mut Window,
527        cx: &mut Context<Self>,
528    ) -> WasScrolled {
529        let mut position = scroll_position;
530        if self.scroll_manager.forbid_vertical_scroll {
531            let current_position = self.scroll_position(cx);
532            position.y = current_position.y;
533        }
534        self.set_scroll_position_internal(position, true, false, window, cx)
535    }
536
537    /// Scrolls so that `row` is at the top of the editor view.
538    pub fn set_scroll_top_row(
539        &mut self,
540        row: DisplayRow,
541        window: &mut Window,
542        cx: &mut Context<Editor>,
543    ) {
544        let snapshot = self.snapshot(window, cx).display_snapshot;
545        let new_screen_top = DisplayPoint::new(row, 0);
546        let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
547        let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
548
549        self.set_scroll_anchor(
550            ScrollAnchor {
551                anchor: new_anchor,
552                offset: Default::default(),
553            },
554            window,
555            cx,
556        );
557    }
558
559    pub(crate) fn set_scroll_position_internal(
560        &mut self,
561        scroll_position: gpui::Point<f32>,
562        local: bool,
563        autoscroll: bool,
564        window: &mut Window,
565        cx: &mut Context<Self>,
566    ) -> WasScrolled {
567        let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
568        self.set_scroll_position_taking_display_map(
569            scroll_position,
570            local,
571            autoscroll,
572            map,
573            window,
574            cx,
575        )
576    }
577
578    fn set_scroll_position_taking_display_map(
579        &mut self,
580        scroll_position: gpui::Point<f32>,
581        local: bool,
582        autoscroll: bool,
583        display_map: DisplaySnapshot,
584        window: &mut Window,
585        cx: &mut Context<Self>,
586    ) -> WasScrolled {
587        hide_hover(self, cx);
588        let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
589
590        self.edit_prediction_preview
591            .set_previous_scroll_position(None);
592
593        let adjusted_position = if self.scroll_manager.forbid_vertical_scroll {
594            let current_position = self.scroll_manager.anchor.scroll_position(&display_map);
595            gpui::Point::new(scroll_position.x, current_position.y)
596        } else {
597            scroll_position
598        };
599
600        let editor_was_scrolled = self.scroll_manager.set_scroll_position(
601            adjusted_position,
602            &display_map,
603            local,
604            autoscroll,
605            workspace_id,
606            window,
607            cx,
608        );
609
610        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
611        self.refresh_colors(false, None, window, cx);
612        editor_was_scrolled
613    }
614
615    pub fn scroll_position(&self, cx: &mut Context<Self>) -> gpui::Point<f32> {
616        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
617        self.scroll_manager.anchor.scroll_position(&display_map)
618    }
619
620    pub fn set_scroll_anchor(
621        &mut self,
622        scroll_anchor: ScrollAnchor,
623        window: &mut Window,
624        cx: &mut Context<Self>,
625    ) {
626        hide_hover(self, cx);
627        let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
628        let top_row = scroll_anchor
629            .anchor
630            .to_point(&self.buffer().read(cx).snapshot(cx))
631            .row;
632        self.scroll_manager.set_anchor(
633            scroll_anchor,
634            top_row,
635            true,
636            false,
637            workspace_id,
638            window,
639            cx,
640        );
641    }
642
643    pub(crate) fn set_scroll_anchor_remote(
644        &mut self,
645        scroll_anchor: ScrollAnchor,
646        window: &mut Window,
647        cx: &mut Context<Self>,
648    ) {
649        hide_hover(self, cx);
650        let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
651        let snapshot = &self.buffer().read(cx).snapshot(cx);
652        if !scroll_anchor.anchor.is_valid(snapshot) {
653            log::warn!("Invalid scroll anchor: {:?}", scroll_anchor);
654            return;
655        }
656        let top_row = scroll_anchor.anchor.to_point(snapshot).row;
657        self.scroll_manager.set_anchor(
658            scroll_anchor,
659            top_row,
660            false,
661            false,
662            workspace_id,
663            window,
664            cx,
665        );
666    }
667
668    pub fn scroll_screen(
669        &mut self,
670        amount: &ScrollAmount,
671        window: &mut Window,
672        cx: &mut Context<Self>,
673    ) {
674        if matches!(self.mode, EditorMode::SingleLine) {
675            cx.propagate();
676            return;
677        }
678
679        if self.take_rename(true, window, cx).is_some() {
680            return;
681        }
682
683        let mut current_position = self.scroll_position(cx);
684        let Some(visible_line_count) = self.visible_line_count() else {
685            return;
686        };
687        let Some(mut visible_column_count) = self.visible_column_count() else {
688            return;
689        };
690
691        // If the user has a preferred line length, and has the editor
692        // configured to wrap at the preferred line length, or bounded to it,
693        // use that value over the visible column count. This was mostly done so
694        // that tests could actually be written for vim's `z l`, `z h`, `z
695        // shift-l` and `z shift-h` commands, as there wasn't a good way to
696        // configure the editor to only display a certain number of columns. If
697        // that ever happens, this could probably be removed.
698        let settings = AllLanguageSettings::get_global(cx);
699        if matches!(
700            settings.defaults.soft_wrap,
701            SoftWrap::PreferredLineLength | SoftWrap::Bounded
702        ) && (settings.defaults.preferred_line_length as f32) < visible_column_count
703        {
704            visible_column_count = settings.defaults.preferred_line_length as f32;
705        }
706
707        // If the scroll position is currently at the left edge of the document
708        // (x == 0.0) and the intent is to scroll right, the gutter's margin
709        // should first be added to the current position, otherwise the cursor
710        // will end at the column position minus the margin, which looks off.
711        if current_position.x == 0.0
712            && amount.columns(visible_column_count) > 0.
713            && let Some(last_position_map) = &self.last_position_map
714        {
715            current_position.x += self.gutter_dimensions.margin / last_position_map.em_advance;
716        }
717        let new_position = current_position
718            + point(
719                amount.columns(visible_column_count),
720                amount.lines(visible_line_count),
721            );
722        self.set_scroll_position(new_position, window, cx);
723    }
724
725    /// Returns an ordering. The newest selection is:
726    ///     Ordering::Equal => on screen
727    ///     Ordering::Less => above or to the left of the screen
728    ///     Ordering::Greater => below or to the right of the screen
729    pub fn newest_selection_on_screen(&self, cx: &mut App) -> Ordering {
730        let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
731        let newest_head = self
732            .selections
733            .newest_anchor()
734            .head()
735            .to_display_point(&snapshot);
736        let screen_top = self
737            .scroll_manager
738            .anchor
739            .anchor
740            .to_display_point(&snapshot);
741
742        if screen_top > newest_head {
743            return Ordering::Less;
744        }
745
746        if let (Some(visible_lines), Some(visible_columns)) =
747            (self.visible_line_count(), self.visible_column_count())
748            && newest_head.row() <= DisplayRow(screen_top.row().0 + visible_lines as u32)
749            && newest_head.column() <= screen_top.column() + visible_columns as u32
750        {
751            return Ordering::Equal;
752        }
753
754        Ordering::Greater
755    }
756
757    pub fn read_scroll_position_from_db(
758        &mut self,
759        item_id: u64,
760        workspace_id: WorkspaceId,
761        window: &mut Window,
762        cx: &mut Context<Editor>,
763    ) {
764        let scroll_position = DB.get_scroll_position(item_id, workspace_id);
765        if let Ok(Some((top_row, x, y))) = scroll_position {
766            let top_anchor = self
767                .buffer()
768                .read(cx)
769                .snapshot(cx)
770                .anchor_at(Point::new(top_row, 0), Bias::Left);
771            let scroll_anchor = ScrollAnchor {
772                offset: gpui::Point::new(x, y),
773                anchor: top_anchor,
774            };
775            self.set_scroll_anchor(scroll_anchor, window, cx);
776        }
777    }
778}