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