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