1191 lines · 46.1 KB · Rust
RawDownload
use std::ops::Range;use std::time::{Duration, Instant}; use collections::HashMap;use feature_flags::{DiffReviewFeatureFlag, FeatureFlagAppExt as _};use gpui::{    AnyElement, App, AvailableSpace, ClickEvent, Context, DefiniteLength, DispatchPhase, Element,    MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent,    ParentElement, Pixels, PressureStage, ScrollDelta, ScrollWheelEvent, TextStyleRefinement,    Window, anchored, deferred, point, px,};use multi_buffer::MultiBufferRow;use project::DisableAiSettings;use settings::Settings;use sum_tree::Bias;use text::SelectionGoal;use theme_settings::BufferLineHeight;use util::{RangeExt, debug_panic, post_inc}; use super::{EditorElement, EditorLayout, LineNumberLayout, PositionMap, SplitSide};use crate::{    CURSORS_VISIBLE_FOR, ColumnarMode, DisplayDiffHunk, DisplayPoint, DisplayRow, Editor,    EditorSettings, EditorSnapshot, GutterHoverButton, HoveredCursor, JumpData,    PhantomDiffReviewIndicator, SelectPhase, Selection, SelectionDragState,    display_map::ToDisplayPoint, editor_settings::DoubleClickInMultibuffer,    hover_popover::hover_at, mouse_context_menu, scroll::ScrollPixelOffset,}; impl EditorElement {    pub(crate) fn mouse_moved(        editor: &mut Editor,        event: &MouseMoveEvent,        position_map: &PositionMap,        split_side: Option<SplitSide>,        window: &mut Window,        cx: &mut Context<Editor>,    ) {        let text_hitbox = &position_map.text_hitbox;        let gutter_hitbox = &position_map.gutter_hitbox;        let modifiers = event.modifiers;        let text_hovered = text_hitbox.is_hovered(window);        let gutter_hovered = gutter_hitbox.is_hovered(window);        editor.set_gutter_hovered(gutter_hovered, cx);         let point_for_position = position_map.point_for_position(event.position);        let valid_point = point_for_position.nearest_valid;         // Update diff review drag state if we're dragging        if editor.diff_review_drag_state.is_some() {            editor.update_diff_review_drag(valid_point.row(), window, cx);        }         let hovered_diff_control = position_map            .diff_hunk_control_bounds            .iter()            .find(|(_, bounds)| bounds.contains(&event.position))            .map(|(row, _)| *row);         let hovered_diff_hunk_row = if let Some(control_row) = hovered_diff_control {            Some(control_row)        } else if text_hovered {            let current_row = valid_point.row();            position_map.display_hunks.iter().find_map(|(hunk, _)| {                if let DisplayDiffHunk::Unfolded {                    display_row_range, ..                } = hunk                {                    if display_row_range.contains(&current_row) {                        Some(display_row_range.start)                    } else {                        None                    }                } else {                    None                }            })        } else {            None        };         if hovered_diff_hunk_row != editor.hovered_diff_hunk_row {            editor.hovered_diff_hunk_row = hovered_diff_hunk_row;            cx.notify();        }         if text_hovered            && let Some((bounds, buffer_id, blame_entry)) = &position_map.inline_blame_bounds        {            let mouse_over_inline_blame = bounds.contains(&event.position);            let mouse_over_popover = editor                .inline_blame_popover                .as_ref()                .and_then(|state| state.popover_bounds)                .is_some_and(|bounds| bounds.contains(&event.position));            let keyboard_grace = editor                .inline_blame_popover                .as_ref()                .is_some_and(|state| state.keyboard_grace);             if mouse_over_inline_blame || mouse_over_popover {                editor.show_blame_popover(*buffer_id, blame_entry, event.position, false, cx);            } else if !keyboard_grace {                editor.hide_blame_popover(false, cx);            }        } else {            let keyboard_grace = editor                .inline_blame_popover                .as_ref()                .is_some_and(|state| state.keyboard_grace);            if !keyboard_grace {                editor.hide_blame_popover(false, cx);            }        }         // Handle diff review indicator when gutter is hovered in diff mode with AI enabled        let show_diff_review = editor.show_diff_review_button()            && cx.has_flag::<DiffReviewFeatureFlag>()            && !DisableAiSettings::is_ai_disabled_for_buffer(                editor.buffer.read(cx).as_singleton().as_ref(),                cx,            );         let diff_review_indicator = if gutter_hovered && show_diff_review {            let is_visible = editor                .gutter_diff_review_indicator                .0                .is_some_and(|indicator| indicator.is_active);             if !is_visible {                editor                    .gutter_diff_review_indicator                    .1                    .get_or_insert_with(|| {                        cx.spawn(async move |this, cx| {                            cx.background_executor()                                .timer(Duration::from_millis(200))                                .await;                             this.update(cx, |this, cx| {                                if let Some(indicator) =                                    this.gutter_diff_review_indicator.0.as_mut()                                {                                    indicator.is_active = true;                                    cx.notify();                                }                            })                            .ok();                        })                    });            }             let anchor = position_map                .snapshot                .display_point_to_anchor(valid_point, Bias::Left);            Some(PhantomDiffReviewIndicator {                start: anchor,                end: anchor,                is_active: is_visible,            })        } else {            editor.gutter_diff_review_indicator.1 = None;            None        };         if diff_review_indicator != editor.gutter_diff_review_indicator.0 {            editor.gutter_diff_review_indicator.0 = diff_review_indicator;            cx.notify();        }         // Don't show breakpoint indicator when diff review indicator is active on this row        let is_on_diff_review_button_row = diff_review_indicator.is_some_and(|indicator| {            let start_row = indicator                .start                .to_display_point(&position_map.snapshot.display_snapshot)                .row();            indicator.is_active && start_row == valid_point.row()        });         let gutter_hover_button = if gutter_hovered            && !is_on_diff_review_button_row            && split_side != Some(SplitSide::Left)        {            let buffer_anchor = position_map                .snapshot                .display_point_to_anchor(valid_point, Bias::Left);             if position_map                .snapshot                .buffer_snapshot()                .anchor_to_buffer_anchor(buffer_anchor)                .is_some()            {                let is_visible = editor                    .gutter_hover_button                    .0                    .is_some_and(|indicator| indicator.is_active);                 if !is_visible {                    editor.gutter_hover_button.1.get_or_insert_with(|| {                        cx.spawn(async move |this, cx| {                            cx.background_executor()                                .timer(Duration::from_millis(200))                                .await;                             this.update(cx, |this, cx| {                                if let Some(indicator) = this.gutter_hover_button.0.as_mut() {                                    indicator.is_active = true;                                    cx.notify();                                }                            })                            .ok();                        })                    });                }                 Some(GutterHoverButton {                    display_row: valid_point.row(),                    is_active: is_visible,                })            } else {                editor.gutter_hover_button.1 = None;                None            }        } else if editor.has_mouse_context_menu() {            editor.gutter_hover_button.1 = None;            editor.gutter_hover_button.0        } else {            editor.gutter_hover_button.1 = None;            None        };         if &gutter_hover_button != &editor.gutter_hover_button.0 {            editor.gutter_hover_button.0 = gutter_hover_button;            cx.notify();        }         // Don't trigger hover popover if mouse is hovering over context menu        if text_hovered {            editor.update_hovered_link(                point_for_position,                Some(event.position),                &position_map.snapshot,                modifiers,                window,                cx,            );             if let Some(point) = point_for_position.as_valid() {                let anchor = position_map                    .snapshot                    .buffer_snapshot()                    .anchor_before(point.to_offset(&position_map.snapshot, Bias::Left));                hover_at(editor, Some(anchor), Some(event.position), window, cx);                Self::update_visible_cursor(editor, point, position_map, window, cx);            } else {                editor.update_inlay_link_and_hover_points(                    &position_map.snapshot,                    point_for_position,                    Some(event.position),                    modifiers.secondary(),                    modifiers.shift,                    window,                    cx,                );            }        } else {            editor.hide_hovered_link(cx);            hover_at(editor, None, Some(event.position), window, cx);        }    }     pub(super) fn layout_mouse_context_menu(        &self,        editor_snapshot: &EditorSnapshot,        visible_range: Range<DisplayRow>,        content_origin: gpui::Point<Pixels>,        window: &mut Window,        cx: &mut App,    ) -> Option<AnyElement> {        let position = self.editor.update(cx, |editor, cx| {            let visible_start_point = editor.display_to_pixel_point(                DisplayPoint::new(visible_range.start, 0),                editor_snapshot,                window,                cx,            )?;            let visible_end_point = editor.display_to_pixel_point(                DisplayPoint::new(visible_range.end, 0),                editor_snapshot,                window,                cx,            )?;             let mouse_context_menu = editor.mouse_context_menu.as_ref()?;            let (source_display_point, position) = match mouse_context_menu.position {                mouse_context_menu::MenuPosition::PinnedToScreen(point) => (None, point),                mouse_context_menu::MenuPosition::PinnedToEditor { source, offset } => {                    let source_display_point = source.to_display_point(editor_snapshot);                    let source_point =                        editor.to_pixel_point(source, editor_snapshot, window, cx)?;                    let position = content_origin + source_point + offset;                    (Some(source_display_point), position)                }            };             let source_included = source_display_point.is_none_or(|source_display_point| {                visible_range                    .to_inclusive()                    .contains(&source_display_point.row())            });            let position_included =                visible_start_point.y <= position.y && position.y <= visible_end_point.y;            if !source_included && !position_included {                None            } else {                Some(position)            }        })?;         let text_style = TextStyleRefinement {            line_height: Some(DefiniteLength::Fraction(                BufferLineHeight::Comfortable.value(),            )),            ..Default::default()        };        window.with_text_style(Some(text_style), |window| {            let mut element = self.editor.read_with(cx, |editor, _| {                let mouse_context_menu = editor.mouse_context_menu.as_ref()?;                let context_menu = mouse_context_menu.context_menu.clone();                 Some(                    deferred(                        anchored()                            .position(position)                            .child(context_menu)                            .anchor(gpui::Anchor::TopLeft)                            .snap_to_window_with_margin(px(8.)),                    )                    .with_priority(1)                    .into_any(),                )            })?;             element.prepaint_as_root(position, AvailableSpace::min_size(), window, cx);            Some(element)        })    }     pub(super) fn paint_mouse_listeners(        &mut self,        layout: &EditorLayout,        window: &mut Window,        cx: &mut App,    ) {        if layout.mode.is_minimap() {            return;        }         self.paint_scroll_wheel_listener(layout, window, cx);         window.on_mouse_event({            let position_map = layout.position_map.clone();            let editor = self.editor.clone();            let line_numbers = layout.line_numbers.clone();             move |event: &MouseDownEvent, phase, window, cx| {                if phase == DispatchPhase::Bubble {                    match event.button {                        MouseButton::Left => editor.update(cx, |editor, cx| {                            let pending_mouse_down = editor                                .pending_mouse_down                                .get_or_insert_with(Default::default)                                .clone();                             *pending_mouse_down.borrow_mut() = Some(event.clone());                             Self::mouse_left_down(                                editor,                                event,                                &position_map,                                line_numbers.as_ref(),                                window,                                cx,                            );                        }),                        MouseButton::Right => editor.update(cx, |editor, cx| {                            Self::mouse_right_down(editor, event, &position_map, window, cx);                        }),                        MouseButton::Middle => editor.update(cx, |editor, cx| {                            Self::mouse_middle_down(editor, event, &position_map, window, cx);                        }),                        _ => {}                    };                }            }        });         window.on_mouse_event({            let editor = self.editor.clone();            let position_map = layout.position_map.clone();             move |event: &MouseUpEvent, phase, window, cx| {                if phase == DispatchPhase::Bubble {                    editor.update(cx, |editor, cx| {                        Self::mouse_up(editor, event, &position_map, window, cx)                    });                }            }        });         window.on_mouse_event({            let editor = self.editor.clone();            let position_map = layout.position_map.clone();            let mut captured_mouse_down = None;             move |event: &MouseUpEvent, phase, window, cx| match phase {                // Clear the pending mouse down during the capture phase,                // so that it happens even if another event handler stops                // propagation.                DispatchPhase::Capture => editor.update(cx, |editor, _cx| {                    let pending_mouse_down = editor                        .pending_mouse_down                        .get_or_insert_with(Default::default)                        .clone();                     let mut pending_mouse_down = pending_mouse_down.borrow_mut();                    if pending_mouse_down.is_some() && position_map.text_hitbox.is_hovered(window) {                        captured_mouse_down = pending_mouse_down.take();                        window.refresh();                    }                }),                // Fire click handlers during the bubble phase.                DispatchPhase::Bubble => editor.update(cx, |editor, cx| {                    if let Some(mouse_down) = captured_mouse_down.take() {                        let event = ClickEvent::Mouse(MouseClickEvent {                            down: mouse_down,                            up: event.clone(),                        });                        Self::click(editor, &event, &position_map, window, cx);                    }                }),            }        });         window.on_mouse_event({            let position_map = layout.position_map.clone();            let editor = self.editor.clone();             move |event: &MousePressureEvent, phase, window, cx| {                if phase == DispatchPhase::Bubble {                    editor.update(cx, |editor, cx| {                        Self::pressure_click(editor, &event, &position_map, window, cx);                    })                }            }        });         window.on_mouse_event({            let position_map = layout.position_map.clone();            let editor = self.editor.clone();            let split_side = self.split_side;             move |event: &MouseMoveEvent, phase, window, cx| {                if phase == DispatchPhase::Bubble {                    editor.update(cx, |editor, cx| {                        if editor.hover_state.focused(window, cx) {                            return;                        }                        if event.pressed_button == Some(MouseButton::Left)                            || event.pressed_button == Some(MouseButton::Middle)                        {                            Self::mouse_dragged(editor, event, &position_map, window, cx)                        }                         Self::mouse_moved(editor, event, &position_map, split_side, window, cx)                    });                }            }        });    }     fn paint_scroll_wheel_listener(        &mut self,        layout: &EditorLayout,        window: &mut Window,        cx: &mut App,    ) {        window.on_mouse_event({            let position_map = layout.position_map.clone();            let editor = self.editor.clone();            let hitbox = layout.hitbox.clone();            let mut delta = ScrollDelta::default();             // Set a minimum scroll_sensitivity of 0.01 to make sure the user doesn't            // accidentally turn off their scrolling.            let base_scroll_sensitivity =                EditorSettings::get_global(cx).scroll_sensitivity.max(0.01);             // Use a minimum fast_scroll_sensitivity for same reason above            let fast_scroll_sensitivity = EditorSettings::get_global(cx)

Lines 1–500 of 1191.