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