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