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