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