719 lines · 21.3 KB · Rust
RawDownload
mod actions;pub(crate) mod autoscroll;pub(crate) mod scroll_amount; use crate::editor_settings::ScrollBeyondLastLine;use crate::{    Anchor, DisplayPoint, DisplayRow, Editor, EditorEvent, EditorMode, EditorSettings,    InlayHintRefreshReason, MultiBufferSnapshot, RowExt, ToPoint,    display_map::{DisplaySnapshot, ToDisplayPoint},    hover_popover::hide_hover,    persistence::DB,};pub use autoscroll::{Autoscroll, AutoscrollStrategy};use core::fmt::Debug;use gpui::{App, Axis, Context, Global, Pixels, Task, Window, point, px};use language::{Bias, Point};pub use scroll_amount::ScrollAmount;use settings::Settings;use std::{    cmp::Ordering,    time::{Duration, Instant},};use util::ResultExt;use workspace::{ItemId, WorkspaceId}; pub const SCROLL_EVENT_SEPARATION: Duration = Duration::from_millis(28);const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1); #[derive(Default)]pub struct ScrollbarAutoHide(pub bool); impl Global for ScrollbarAutoHide {} #[derive(Clone, Copy, Debug, PartialEq)]pub struct ScrollAnchor {    pub offset: gpui::Point<f32>,    pub anchor: Anchor,} impl ScrollAnchor {    pub(super) fn new() -> Self {        Self {            offset: gpui::Point::default(),            anchor: Anchor::min(),        }    }     pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {        let mut scroll_position = self.offset;        if self.anchor == Anchor::min() {            scroll_position.y = 0.;        } else {            let scroll_top = self.anchor.to_display_point(snapshot).row().as_f32();            scroll_position.y += scroll_top;        }        scroll_position    }     pub fn top_row(&self, buffer: &MultiBufferSnapshot) -> u32 {        self.anchor.to_point(buffer).row    }} #[derive(Clone, Copy, Debug)]pub struct OngoingScroll {    last_event: Instant,    axis: Option<Axis>,} impl OngoingScroll {    fn new() -> Self {        Self {            last_event: Instant::now() - SCROLL_EVENT_SEPARATION,            axis: None,        }    }     pub fn filter(&self, delta: &mut gpui::Point<Pixels>) -> Option<Axis> {        const UNLOCK_PERCENT: f32 = 1.9;        const UNLOCK_LOWER_BOUND: Pixels = px(6.);        let mut axis = self.axis;         let x = delta.x.abs();        let y = delta.y.abs();        let duration = Instant::now().duration_since(self.last_event);        if duration > SCROLL_EVENT_SEPARATION {            //New ongoing scroll will start, determine axis            axis = if x <= y {                Some(Axis::Vertical)            } else {                Some(Axis::Horizontal)            };        } else if x.max(y) >= UNLOCK_LOWER_BOUND {            //Check if the current ongoing will need to unlock            match axis {                Some(Axis::Vertical) => {                    if x > y && x >= y * UNLOCK_PERCENT {                        axis = None;                    }                }                 Some(Axis::Horizontal) => {                    if y > x && y >= x * UNLOCK_PERCENT {                        axis = None;                    }                }                 None => {}            }        }         match axis {            Some(Axis::Vertical) => {                *delta = point(px(0.), delta.y);            }            Some(Axis::Horizontal) => {                *delta = point(delta.x, px(0.));            }            None => {}        }         axis    }} #[derive(Copy, Clone, PartialEq, Eq)]pub enum ScrollbarThumbState {    Idle,    Hovered,    Dragging,} #[derive(PartialEq, Eq)]pub struct ActiveScrollbarState {    axis: Axis,    thumb_state: ScrollbarThumbState,} impl ActiveScrollbarState {    pub fn new(axis: Axis, thumb_state: ScrollbarThumbState) -> Self {        ActiveScrollbarState { axis, thumb_state }    }     pub fn thumb_state_for_axis(&self, axis: Axis) -> Option<ScrollbarThumbState> {        (self.axis == axis).then_some(self.thumb_state)    }} pub struct ScrollManager {    pub(crate) vertical_scroll_margin: f32,    anchor: ScrollAnchor,    ongoing: OngoingScroll,    autoscroll_request: Option<(Autoscroll, bool)>,    last_autoscroll: Option<(gpui::Point<f32>, f32, f32, AutoscrollStrategy)>,    show_scrollbars: bool,    hide_scrollbar_task: Option<Task<()>>,    active_scrollbar: Option<ActiveScrollbarState>,    visible_line_count: Option<f32>,    forbid_vertical_scroll: bool,    dragging_minimap: bool,    show_minimap_thumb: bool,} impl ScrollManager {    pub fn new(cx: &mut App) -> Self {        ScrollManager {            vertical_scroll_margin: EditorSettings::get_global(cx).vertical_scroll_margin,            anchor: ScrollAnchor::new(),            ongoing: OngoingScroll::new(),            autoscroll_request: None,            show_scrollbars: true,            hide_scrollbar_task: None,            active_scrollbar: None,            last_autoscroll: None,            visible_line_count: None,            forbid_vertical_scroll: false,            dragging_minimap: false,            show_minimap_thumb: false,        }    }     pub fn clone_state(&mut self, other: &Self) {        self.anchor = other.anchor;        self.ongoing = other.ongoing;    }     pub fn anchor(&self) -> ScrollAnchor {        self.anchor    }     pub fn ongoing_scroll(&self) -> OngoingScroll {        self.ongoing    }     pub fn update_ongoing_scroll(&mut self, axis: Option<Axis>) {        self.ongoing.last_event = Instant::now();        self.ongoing.axis = axis;    }     pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {        self.anchor.scroll_position(snapshot)    }     fn set_scroll_position(        &mut self,        scroll_position: gpui::Point<f32>,        map: &DisplaySnapshot,        local: bool,        autoscroll: bool,        workspace_id: Option<WorkspaceId>,        window: &mut Window,        cx: &mut Context<Editor>,    ) {        let (new_anchor, top_row) = if scroll_position.y <= 0. {            (                ScrollAnchor {                    anchor: Anchor::min(),                    offset: scroll_position.max(&gpui::Point::default()),                },                0,            )        } else {            let scroll_top = scroll_position.y;            let scroll_top = match EditorSettings::get_global(cx).scroll_beyond_last_line {                ScrollBeyondLastLine::OnePage => scroll_top,                ScrollBeyondLastLine::Off => {                    if let Some(height_in_lines) = self.visible_line_count {                        let max_row = map.max_point().row().0 as f32;                        scroll_top.min(max_row - height_in_lines + 1.).max(0.)                    } else {                        scroll_top                    }                }                ScrollBeyondLastLine::VerticalScrollMargin => {                    if let Some(height_in_lines) = self.visible_line_count {                        let max_row = map.max_point().row().0 as f32;                        scroll_top                            .min(max_row - height_in_lines + 1. + self.vertical_scroll_margin)                            .max(0.)                    } else {                        scroll_top                    }                }            };             let scroll_top_buffer_point =                DisplayPoint::new(DisplayRow(scroll_top as u32), 0).to_point(map);            let top_anchor = map                .buffer_snapshot                .anchor_at(scroll_top_buffer_point, Bias::Right);             (                ScrollAnchor {                    anchor: top_anchor,                    offset: point(                        scroll_position.x.max(0.),                        scroll_top - top_anchor.to_display_point(map).row().as_f32(),                    ),                },                scroll_top_buffer_point.row,            )        };         self.set_anchor(            new_anchor,            top_row,            local,            autoscroll,            workspace_id,            window,            cx,        );    }     fn set_anchor(        &mut self,        anchor: ScrollAnchor,        top_row: u32,        local: bool,        autoscroll: bool,        workspace_id: Option<WorkspaceId>,        window: &mut Window,        cx: &mut Context<Editor>,    ) {        let adjusted_anchor = if self.forbid_vertical_scroll {            ScrollAnchor {                offset: gpui::Point::new(anchor.offset.x, self.anchor.offset.y),                anchor: self.anchor.anchor,            }        } else {            anchor        };         self.anchor = adjusted_anchor;        cx.emit(EditorEvent::ScrollPositionChanged { local, autoscroll });        self.show_scrollbars(window, cx);        self.autoscroll_request.take();        if let Some(workspace_id) = workspace_id {            let item_id = cx.entity().entity_id().as_u64() as ItemId;             cx.foreground_executor()                .spawn(async move {                    log::debug!(                        "Saving scroll position for item {item_id:?} in workspace {workspace_id:?}"                    );                    DB.save_scroll_position(                        item_id,                        workspace_id,                        top_row,                        anchor.offset.x,                        anchor.offset.y,                    )                    .await                    .log_err()                })                .detach()        }        cx.notify();    }     pub fn show_scrollbars(&mut self, window: &mut Window, cx: &mut Context<Editor>) {        if !self.show_scrollbars {            self.show_scrollbars = true;            cx.notify();        }         if cx.default_global::<ScrollbarAutoHide>().0 {            self.hide_scrollbar_task = Some(cx.spawn_in(window, async move |editor, cx| {                cx.background_executor()                    .timer(SCROLLBAR_SHOW_INTERVAL)                    .await;                editor                    .update(cx, |editor, cx| {                        editor.scroll_manager.show_scrollbars = false;                        cx.notify();                    })                    .log_err();            }));        } else {            self.hide_scrollbar_task = None;        }    }     pub fn scrollbars_visible(&self) -> bool {        self.show_scrollbars    }     pub fn show_minimap_thumb(&mut self, cx: &mut Context<Editor>) {        if !self.show_minimap_thumb {            self.show_minimap_thumb = true;            cx.notify();        }    }     pub fn hide_minimap_thumb(&mut self, cx: &mut Context<Editor>) {        if self.show_minimap_thumb {            self.show_minimap_thumb = false;            cx.notify();        }    }     pub fn minimap_thumb_visible(&mut self) -> bool {        self.show_minimap_thumb    }     pub fn autoscroll_request(&self) -> Option<Autoscroll> {        self.autoscroll_request.map(|(autoscroll, _)| autoscroll)    }     pub fn active_scrollbar_state(&self) -> Option<&ActiveScrollbarState> {        self.active_scrollbar.as_ref()    }     pub fn dragging_scrollbar_axis(&self) -> Option<Axis> {        self.active_scrollbar            .as_ref()            .map(|scrollbar| scrollbar.axis)    }     pub fn any_scrollbar_dragged(&self) -> bool {        self.active_scrollbar            .as_ref()            .is_some_and(|scrollbar| scrollbar.thumb_state == ScrollbarThumbState::Dragging)    }     pub fn set_hovered_scroll_thumb_axis(&mut self, axis: Axis, cx: &mut Context<Editor>) {        self.update_active_scrollbar_state(            Some(ActiveScrollbarState::new(                axis,                ScrollbarThumbState::Hovered,            )),            cx,        );    }     pub fn set_dragged_scroll_thumb_axis(&mut self, axis: Axis, cx: &mut Context<Editor>) {        self.update_active_scrollbar_state(            Some(ActiveScrollbarState::new(                axis,                ScrollbarThumbState::Dragging,            )),            cx,        );    }     pub fn reset_scrollbar_state(&mut self, cx: &mut Context<Editor>) {        self.update_active_scrollbar_state(None, cx);    }     fn update_active_scrollbar_state(        &mut self,        new_state: Option<ActiveScrollbarState>,        cx: &mut Context<Editor>,    ) {        if self.active_scrollbar != new_state {            self.active_scrollbar = new_state;            cx.notify();        }    }     pub fn is_dragging_minimap(&self) -> bool {        self.dragging_minimap    }     pub fn set_is_dragging_minimap(&mut self, dragging: bool, cx: &mut Context<Editor>) {        self.dragging_minimap = dragging;        cx.notify();    }     pub fn clamp_scroll_left(&mut self, max: f32) -> bool {        if max < self.anchor.offset.x {            self.anchor.offset.x = max;            true        } else {            false        }    }     pub fn set_forbid_vertical_scroll(&mut self, forbid: bool) {        self.forbid_vertical_scroll = forbid;    }     pub fn forbid_vertical_scroll(&self) -> bool {        self.forbid_vertical_scroll    }} impl Editor {    pub fn vertical_scroll_margin(&self) -> usize {        self.scroll_manager.vertical_scroll_margin as usize    }     pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut Context<Self>) {        self.scroll_manager.vertical_scroll_margin = margin_rows as f32;        cx.notify();    }     pub fn visible_line_count(&self) -> Option<f32> {        self.scroll_manager.visible_line_count    }     pub fn visible_row_count(&self) -> Option<u32> {        self.visible_line_count()            .map(|line_count| line_count as u32 - 1)    }     pub(crate) fn set_visible_line_count(        &mut self,        lines: f32,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let opened_first_time = self.scroll_manager.visible_line_count.is_none();        self.scroll_manager.visible_line_count = Some(lines);        if opened_first_time {            cx.spawn_in(window, async move |editor, cx| {                editor                    .update(cx, |editor, cx| {                        editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)                    })                    .ok()            })            .detach()        }    }     pub fn apply_scroll_delta(        &mut self,        scroll_delta: gpui::Point<f32>,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let mut delta = scroll_delta;        if self.scroll_manager.forbid_vertical_scroll {            delta.y = 0.0;        }        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));        let position = self.scroll_manager.anchor.scroll_position(&display_map) + delta;        self.set_scroll_position_taking_display_map(position, true, false, display_map, window, cx);    }

Lines 1–500 of 719.