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