diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index aaabcc0eb553a6c905c760defe023bc7459a4719..9900e68471c59b1b2135d53b0883344d2e1aa8a9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1088,6 +1088,7 @@ pub struct Editor { tasks_update_task: Option>, breakpoint_store: Option>, gutter_breakpoint_indicator: (Option, Option>), + hovered_diff_hunk_row: Option, pull_diagnostics_task: Task<()>, in_project_search: bool, previous_search_ranges: Option]>>, @@ -2016,6 +2017,7 @@ impl Editor { breakpoint_store, gutter_breakpoint_indicator: (None, None), + hovered_diff_hunk_row: None, _subscriptions: vec![ cx.observe(&buffer, Self::on_buffer_changed), cx.subscribe_in(&buffer, window, Self::on_buffer_event), diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a0e9587138f62cbd63919a4454d1b9f8c6bca83f..a6cbd5a847c80610826e1c2c37c22210797d4d1d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1072,18 +1072,56 @@ impl EditorElement { editor: &mut Editor, event: &MouseMoveEvent, position_map: &PositionMap, - inline_blame_bounds: &Option<(Bounds, BlameEntry)>, window: &mut Window, cx: &mut Context, ) { 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); editor.mouse_cursor_hidden = false; - if let Some((bounds, blame_entry)) = inline_blame_bounds { + let point_for_position = position_map.point_for_position(event.position); + let valid_point = point_for_position.previous_valid; + + 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(¤t_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 let Some((bounds, blame_entry)) = &position_map.inline_blame_bounds { let mouse_over_inline_blame = bounds.contains(&event.position); let mouse_over_popover = editor .inline_blame_popover @@ -1101,12 +1139,9 @@ impl EditorElement { } let breakpoint_indicator = if gutter_hovered { - let new_point = position_map - .point_for_position(event.position) - .previous_valid; let buffer_anchor = position_map .snapshot - .display_point_to_anchor(new_point, Bias::Left); + .display_point_to_anchor(valid_point, Bias::Left); if let Some((buffer_snapshot, file)) = position_map .snapshot @@ -1161,7 +1196,7 @@ impl EditorElement { } Some(PhantomBreakpointIndicator { - display_row: new_point.row(), + display_row: valid_point.row(), is_active: is_visible, collides_with_existing_breakpoint: has_existing_breakpoint, }) @@ -1180,9 +1215,7 @@ impl EditorElement { } // Don't trigger hover popover if mouse is hovering over context menu - if text_hitbox.is_hovered(window) { - let point_for_position = position_map.point_for_position(event.position); - + if text_hovered { editor.update_hovered_link( point_for_position, &position_map.snapshot, @@ -4769,7 +4802,6 @@ impl EditorElement { row_range: Range, row_infos: &[RowInfo], text_hitbox: &Hitbox, - position_map: &PositionMap, newest_cursor_position: Option, line_height: Pixels, right_margin: Pixels, @@ -4779,14 +4811,15 @@ impl EditorElement { editor: Entity, window: &mut Window, cx: &mut App, - ) -> Vec { + ) -> (Vec, Vec<(DisplayRow, Bounds)>) { let render_diff_hunk_controls = editor.read(cx).render_diff_hunk_controls.clone(); - let point_for_position = position_map.point_for_position(window.mouse_position()); + let hovered_diff_hunk_row = editor.read(cx).hovered_diff_hunk_row; let mut controls = vec![]; + let mut control_bounds = vec![]; let active_positions = [ - Some(point_for_position.previous_valid), + hovered_diff_hunk_row.map(|row| DisplayPoint::new(row, 0)), newest_cursor_position, ]; @@ -4831,6 +4864,7 @@ impl EditorElement { { continue; } + if active_positions .iter() .any(|p| p.map_or(false, |p| display_row_range.contains(&p.row()))) @@ -4854,6 +4888,9 @@ impl EditorElement { let x = text_hitbox.bounds.right() - right_margin - px(10.) - size.width; + let bounds = Bounds::new(gpui::Point::new(x, y), size); + control_bounds.push((display_row_range.start, bounds)); + window.with_absolute_element_offset(gpui::Point::new(x, y), |window| { element.prepaint(window, cx) }); @@ -4862,7 +4899,7 @@ impl EditorElement { } } - controls + (controls, control_bounds) } fn layout_signature_help( @@ -6699,10 +6736,6 @@ impl EditorElement { window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); - let inline_blame_bounds = layout - .inline_blame_layout - .as_ref() - .map(|layout| (layout.bounds, layout.entry.clone())); move |event: &MouseMoveEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { @@ -6716,14 +6749,7 @@ impl EditorElement { Self::mouse_dragged(editor, event, &position_map, window, cx) } - Self::mouse_moved( - editor, - event, - &position_map, - &inline_blame_bounds, - window, - cx, - ) + Self::mouse_moved(editor, event, &position_map, window, cx) }); } } @@ -8698,6 +8724,25 @@ impl Element for EditorElement { let mode = snapshot.mode.clone(); + let (diff_hunk_controls, diff_hunk_control_bounds) = if is_read_only { + (vec![], vec![]) + } else { + self.layout_diff_hunk_controls( + start_row..end_row, + &row_infos, + &text_hitbox, + newest_selection_head, + line_height, + right_margin, + scroll_pixel_position, + &display_hunks, + &highlighted_rows, + self.editor.clone(), + window, + cx, + ) + }; + let position_map = Rc::new(PositionMap { size: bounds.size, visible_row_range, @@ -8710,32 +8755,17 @@ impl Element for EditorElement { snapshot, gutter_hitbox: gutter_hitbox.clone(), text_hitbox: text_hitbox.clone(), + inline_blame_bounds: inline_blame_layout + .as_ref() + .map(|layout| (layout.bounds, layout.entry.clone())), + display_hunks: display_hunks.clone(), + diff_hunk_control_bounds: diff_hunk_control_bounds.clone(), }); self.editor.update(cx, |editor, _| { editor.last_position_map = Some(position_map.clone()) }); - let diff_hunk_controls = if is_read_only { - vec![] - } else { - self.layout_diff_hunk_controls( - start_row..end_row, - &row_infos, - &text_hitbox, - &position_map, - newest_selection_head, - line_height, - right_margin, - scroll_pixel_position, - &display_hunks, - &highlighted_rows, - self.editor.clone(), - window, - cx, - ) - }; - EditorLayout { mode, position_map, @@ -9398,6 +9428,9 @@ pub(crate) struct PositionMap { pub snapshot: EditorSnapshot, pub text_hitbox: Hitbox, pub gutter_hitbox: Hitbox, + pub inline_blame_bounds: Option<(Bounds, BlameEntry)>, + pub display_hunks: Vec<(DisplayDiffHunk, Option)>, + pub diff_hunk_control_bounds: Vec<(DisplayRow, Bounds)>, } #[derive(Debug, Copy, Clone)]