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 /// 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 fn clone_state(&mut self, other: &Self) {
193 self.anchor = other.anchor;
194 self.ongoing = other.ongoing;
195 }
196
197 pub fn anchor(&self) -> ScrollAnchor {
198 self.anchor
199 }
200
201 pub 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 fn scrollbars_visible(&self) -> bool {
349 self.show_scrollbars
350 }
351
352 pub fn take_autoscroll_request(&mut self) -> Option<(Autoscroll, bool)> {
353 self.autoscroll_request.take()
354 }
355
356 pub 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 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 fn set_forbid_vertical_scroll(&mut self, forbid: bool) {
457 self.forbid_vertical_scroll = forbid;
458 }
459
460 pub fn forbid_vertical_scroll(&self) -> bool {
461 self.forbid_vertical_scroll
462 }
463}
464
465impl Editor {
466 pub 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 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 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 editor.colorize_brackets(false, cx);
504 })
505 .ok();
506 });
507 }
508 }
509
510 pub(crate) fn set_visible_column_count(&mut self, columns: f64) {
511 self.scroll_manager.visible_column_count = Some(columns);
512 }
513
514 pub fn apply_scroll_delta(
515 &mut self,
516 scroll_delta: gpui::Point<f32>,
517 window: &mut Window,
518 cx: &mut Context<Self>,
519 ) {
520 let mut delta = scroll_delta;
521 if self.scroll_manager.forbid_vertical_scroll {
522 delta.y = 0.0;
523 }
524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
525 let position =
526 self.scroll_manager.anchor.scroll_position(&display_map) + delta.map(f64::from);
527 self.set_scroll_position_taking_display_map(position, true, false, display_map, window, cx);
528 }
529
530 pub fn set_scroll_position(
531 &mut self,
532 scroll_position: gpui::Point<ScrollOffset>,
533 window: &mut Window,
534 cx: &mut Context<Self>,
535 ) -> WasScrolled {
536 let mut position = scroll_position;
537 if self.scroll_manager.forbid_vertical_scroll {
538 let current_position = self.scroll_position(cx);
539 position.y = current_position.y;
540 }
541 self.set_scroll_position_internal(position, true, false, window, cx)
542 }
543
544 /// Scrolls so that `row` is at the top of the editor view.
545 pub fn set_scroll_top_row(
546 &mut self,
547 row: DisplayRow,
548 window: &mut Window,
549 cx: &mut Context<Editor>,
550 ) {
551 let snapshot = self.snapshot(window, cx).display_snapshot;
552 let new_screen_top = DisplayPoint::new(row, 0);
553 let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
554 let new_anchor = snapshot.buffer_snapshot().anchor_before(new_screen_top);
555
556 self.set_scroll_anchor(
557 ScrollAnchor {
558 anchor: new_anchor,
559 offset: Default::default(),
560 },
561 window,
562 cx,
563 );
564 }
565
566 pub(crate) fn set_scroll_position_internal(
567 &mut self,
568 scroll_position: gpui::Point<ScrollOffset>,
569 local: bool,
570 autoscroll: bool,
571 window: &mut Window,
572 cx: &mut Context<Self>,
573 ) -> WasScrolled {
574 let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
575 self.set_scroll_position_taking_display_map(
576 scroll_position,
577 local,
578 autoscroll,
579 map,
580 window,
581 cx,
582 )
583 }
584
585 fn set_scroll_position_taking_display_map(
586 &mut self,
587 scroll_position: gpui::Point<ScrollOffset>,
588 local: bool,
589 autoscroll: bool,
590 display_map: DisplaySnapshot,
591 window: &mut Window,
592 cx: &mut Context<Self>,
593 ) -> WasScrolled {
594 hide_hover(self, cx);
595 let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
596
597 self.edit_prediction_preview
598 .set_previous_scroll_position(None);
599
600 let adjusted_position = if self.scroll_manager.forbid_vertical_scroll {
601 let current_position = self.scroll_manager.anchor.scroll_position(&display_map);
602 gpui::Point::new(scroll_position.x, current_position.y)
603 } else {
604 scroll_position
605 };
606
607 self.scroll_manager.set_scroll_position(
608 adjusted_position,
609 &display_map,
610 local,
611 autoscroll,
612 workspace_id,
613 window,
614 cx,
615 )
616 }
617
618 pub fn scroll_position(&self, cx: &mut Context<Self>) -> gpui::Point<ScrollOffset> {
619 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
620 self.scroll_manager.anchor.scroll_position(&display_map)
621 }
622
623 pub fn set_scroll_anchor(
624 &mut self,
625 scroll_anchor: ScrollAnchor,
626 window: &mut Window,
627 cx: &mut Context<Self>,
628 ) {
629 hide_hover(self, cx);
630 let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
631 let top_row = scroll_anchor
632 .anchor
633 .to_point(&self.buffer().read(cx).snapshot(cx))
634 .row;
635 self.scroll_manager.set_anchor(
636 scroll_anchor,
637 top_row,
638 true,
639 false,
640 workspace_id,
641 window,
642 cx,
643 );
644 }
645
646 pub(crate) fn set_scroll_anchor_remote(
647 &mut self,
648 scroll_anchor: ScrollAnchor,
649 window: &mut Window,
650 cx: &mut Context<Self>,
651 ) {
652 hide_hover(self, cx);
653 let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
654 let snapshot = &self.buffer().read(cx).snapshot(cx);
655 if !scroll_anchor.anchor.is_valid(snapshot) {
656 log::warn!("Invalid scroll anchor: {:?}", scroll_anchor);
657 return;
658 }
659 let top_row = scroll_anchor.anchor.to_point(snapshot).row;
660 self.scroll_manager.set_anchor(
661 scroll_anchor,
662 top_row,
663 false,
664 false,
665 workspace_id,
666 window,
667 cx,
668 );
669 }
670
671 pub fn scroll_screen(
672 &mut self,
673 amount: &ScrollAmount,
674 window: &mut Window,
675 cx: &mut Context<Self>,
676 ) {
677 if matches!(self.mode, EditorMode::SingleLine) {
678 cx.propagate();
679 return;
680 }
681
682 if self.take_rename(true, window, cx).is_some() {
683 return;
684 }
685
686 let mut current_position = self.scroll_position(cx);
687 let Some(visible_line_count) = self.visible_line_count() else {
688 return;
689 };
690 let Some(mut visible_column_count) = self.visible_column_count() else {
691 return;
692 };
693
694 // If the user has a preferred line length, and has the editor
695 // configured to wrap at the preferred line length, or bounded to it,
696 // use that value over the visible column count. This was mostly done so
697 // that tests could actually be written for vim's `z l`, `z h`, `z
698 // shift-l` and `z shift-h` commands, as there wasn't a good way to
699 // configure the editor to only display a certain number of columns. If
700 // that ever happens, this could probably be removed.
701 let settings = AllLanguageSettings::get_global(cx);
702 if matches!(
703 settings.defaults.soft_wrap,
704 SoftWrap::PreferredLineLength | SoftWrap::Bounded
705 ) && (settings.defaults.preferred_line_length as f64) < visible_column_count
706 {
707 visible_column_count = settings.defaults.preferred_line_length as f64;
708 }
709
710 // If the scroll position is currently at the left edge of the document
711 // (x == 0.0) and the intent is to scroll right, the gutter's margin
712 // should first be added to the current position, otherwise the cursor
713 // will end at the column position minus the margin, which looks off.
714 if current_position.x == 0.0
715 && amount.columns(visible_column_count) > 0.
716 && let Some(last_position_map) = &self.last_position_map
717 {
718 current_position.x +=
719 f64::from(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_before(Point::new(top_row, 0));
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}