use super::{ display_map::{BlockContext, ToDisplayPoint}, Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN,};use crate::{ display_map::{BlockStyle, DisplaySnapshot, FoldStatus, TransformBlock}, editor_settings::ShowScrollbar, git::{diff_hunk_to_display, DisplayDiffHunk}, hover_popover::{ hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT, }, link_go_to_definition::{ go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger, }, mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,};use clock::ReplicaId;use collections::{BTreeMap, HashMap};use git::diff::DiffHunkStatus;use gpui::{ color::Color, elements::*, fonts::{HighlightStyle, TextStyle, Underline}, geometry::{ rect::RectF, vector::{vec2f, Vector2F}, PathBuilder, }, json::{self, ToJson}, platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent}, text_layout::{self, Line, RunStyle, TextLayoutCache}, AnyElement, Axis, Border, CursorRegion, Element, EventContext, FontCache, LayoutContext, MouseRegion, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,};use itertools::Itertools;use json::json;use language::{ language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection,};use project::{ project_settings::{GitGutterSetting, ProjectSettings}, ProjectPath,};use smallvec::SmallVec;use std::{ borrow::Cow, cmp::{self, Ordering}, fmt::Write, iter, ops::Range, sync::Arc,};use text::Point;use workspace::item::Item; enum FoldMarkers {} struct SelectionLayout { head: DisplayPoint, cursor_shape: CursorShape, is_newest: bool, is_local: bool, range: Range<DisplayPoint>, active_rows: Range<u32>,} impl SelectionLayout { fn new<T: ToPoint + ToDisplayPoint + Clone>( selection: Selection<T>, line_mode: bool, cursor_shape: CursorShape, map: &DisplaySnapshot, is_newest: bool, is_local: bool, ) -> Self { let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot)); let display_selection = point_selection.map(|p| p.to_display_point(map)); let mut range = display_selection.range(); let mut head = display_selection.head(); let mut active_rows = map.prev_line_boundary(point_selection.start).1.row() ..map.next_line_boundary(point_selection.end).1.row(); // vim visual line mode if line_mode { let point_range = map.expand_to_line(point_selection.range()); range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map); } // any vim visual mode (including line mode) if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed { if head.column() > 0 { head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left) } else if head.row() > 0 && head != map.max_point() { head = map.clip_point( DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)), Bias::Left, ); // updating range.end is a no-op unless you're cursor is // on the newline containing a multi-buffer divider // in which case the clip_point may have moved the head up // an additional row. range.end = DisplayPoint::new(head.row() + 1, 0); active_rows.end = head.row(); } } Self { head, cursor_shape, is_newest, is_local, range, active_rows, } }} pub struct EditorElement { style: Arc<EditorStyle>,} impl EditorElement { pub fn new(style: EditorStyle) -> Self { Self { style: Arc::new(style), } } fn attach_mouse_handlers( scene: &mut SceneBuilder, position_map: &Arc<PositionMap>, has_popovers: bool, visible_bounds: RectF, text_bounds: RectF, gutter_bounds: RectF, bounds: RectF, cx: &mut ViewContext<Editor>, ) { enum EditorElementMouseHandlers {} scene.push_mouse_region( MouseRegion::new::<EditorElementMouseHandlers>( cx.view_id(), cx.view_id(), visible_bounds, ) .on_down(MouseButton::Left, { let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_down( editor, event.platform_event, position_map.as_ref(), text_bounds, gutter_bounds, cx, ) { cx.propagate_event(); } } }) .on_down(MouseButton::Right, { let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_right_down( editor, event.position, position_map.as_ref(), text_bounds, cx, ) { cx.propagate_event(); } } }) .on_up(MouseButton::Left, { let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_up( editor, event.position, event.cmd, event.shift, event.alt, position_map.as_ref(), text_bounds, cx, ) { cx.propagate_event() } } }) .on_drag(MouseButton::Left, { let position_map = position_map.clone(); move |event, editor, cx| { if event.end { return; } if !Self::mouse_dragged( editor, event.platform_event, position_map.as_ref(), text_bounds, cx, ) { cx.propagate_event() } } }) .on_move({ let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_moved( editor, event.platform_event, &position_map, text_bounds, cx, ) { cx.propagate_event() } } }) .on_move_out(move |_, editor: &mut Editor, cx| { if has_popovers { hide_hover(editor, cx); } }) .on_scroll({ let position_map = position_map.clone(); move |event, editor, cx| { if !Self::scroll( editor, event.position, *event.delta.raw(), event.delta.precise(), &position_map, bounds, cx, ) { cx.propagate_event() } } }), ); enum GutterHandlers {} scene.push_mouse_region( MouseRegion::new::<GutterHandlers>(cx.view_id(), cx.view_id() + 1, gutter_bounds) .on_hover(|hover, editor: &mut Editor, cx| { editor.gutter_hover( &GutterHover { hovered: hover.started, }, cx, ); }), ) } fn mouse_down( editor: &mut Editor, MouseButtonEvent { position, modifiers: Modifiers { shift, ctrl, alt, cmd, .. }, mut click_count, .. }: MouseButtonEvent, position_map: &PositionMap, text_bounds: RectF, gutter_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { if gutter_bounds.contains_point(position) { click_count = 3; // Simulate triple-click when clicking the gutter to select lines } else if !text_bounds.contains_point(position) { return false; } let point_for_position = position_map.point_for_position(text_bounds, position); let position = point_for_position.previous_valid; if shift && alt { editor.select( SelectPhase::BeginColumnar { position, goal_column: point_for_position.exact_unclipped.column(), }, cx, ); } else if shift && !ctrl && !alt && !cmd { editor.select( SelectPhase::Extend { position, click_count, }, cx, ); } else { editor.select( SelectPhase::Begin { position, add: alt, click_count, }, cx, ); } true } fn mouse_right_down( editor: &mut Editor, position: Vector2F, position_map: &PositionMap, text_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { if !text_bounds.contains_point(position) { return false; } let point_for_position = position_map.point_for_position(text_bounds, position); mouse_context_menu::deploy_context_menu( editor, position, point_for_position.previous_valid, cx, ); true } fn mouse_up( editor: &mut Editor, position: Vector2F, cmd: bool, shift: bool, alt: bool, position_map: &PositionMap, text_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { let end_selection = editor.has_pending_selection(); let pending_nonempty_selections = editor.has_pending_nonempty_selection(); if end_selection { editor.select(SelectPhase::End, cx); } if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) { let point = position_map.point_for_position(text_bounds, position); let could_be_inlay = point.as_valid().is_none(); if shift || could_be_inlay { go_to_fetched_type_definition(editor, point, alt, cx); } else { go_to_fetched_definition(editor, point, alt, cx); } return true; } end_selection } fn mouse_dragged( editor: &mut Editor, MouseMovedEvent { modifiers: Modifiers { cmd, shift, .. }, position, .. }: MouseMovedEvent, position_map: &PositionMap, text_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed // Don't trigger hover popover if mouse is hovering over context menu let point = if text_bounds.contains_point(position) { position_map .point_for_position(text_bounds, position) .as_valid() } else { None }; update_go_to_definition_link( editor, point.map(GoToDefinitionTrigger::Text), cmd, shift, cx, ); if editor.has_pending_selection() { let mut scroll_delta = Vector2F::zero(); let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0); let top = text_bounds.origin_y() + vertical_margin; let bottom = text_bounds.lower_left().y() - vertical_margin; if position.y() < top { scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y())) } if position.y() > bottom { scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom)) } let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0); let left = text_bounds.origin_x() + horizontal_margin; let right = text_bounds.upper_right().x() - horizontal_margin; if position.x() < left { scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta( left - position.x(), )) } if position.x() > right { scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta( position.x() - right, )) } let point_for_position = position_map.point_for_position(text_bounds, position); editor.select( SelectPhase::Update { position: point_for_position.previous_valid, goal_column: point_for_position.exact_unclipped.column(), scroll_position: (position_map.snapshot.scroll_position() + scroll_delta) .clamp(Vector2F::zero(), position_map.scroll_max), }, cx, ); hover_at(editor, point, cx); true } else { hover_at(editor, point, cx); false } } fn mouse_moved( editor: &mut Editor, MouseMovedEvent { modifiers: Modifiers { shift, cmd, .. }, position, .. }: MouseMovedEvent, position_map: &PositionMap, text_bounds: RectF, cx: &mut ViewContext<Editor>, ) -> bool { // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed // Don't trigger hover popover if mouse is hovering over context menu if text_bounds.contains_point(position) { let point_for_position = position_map.point_for_position(text_bounds, position); match point_for_position.as_valid() { Some(point) => { update_go_to_definition_link( editor, Some(GoToDefinitionTrigger::Text(point)), cmd, shift, cx, ); hover_at(editor, Some(point), cx); } None => { update_inlay_link_and_hover_points( &position_map.snapshot, point_for_position, editor, cmd, shift, cx, ); } } } else { update_go_to_definition_link(editor, None, cmd, shift, cx); hover_at(editor, None, cx); } true } fn scroll( editor: &mut Editor, position: Vector2F, mut delta: Vector2F, precise: bool, position_map: &PositionMap,Lines 1–500 of 3594. Load more
use super::{ display_map::{BlockContext, ToDisplayPoint}, Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN, }; use crate::{ display_map::{BlockStyle, DisplaySnapshot, FoldStatus, TransformBlock}, editor_settings::ShowScrollbar, git::{diff_hunk_to_display, DisplayDiffHunk}, hover_popover::{ hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT, }, link_go_to_definition::{ go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger, }, mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt, }; use clock::ReplicaId; use collections::{BTreeMap, HashMap}; use git::diff::DiffHunkStatus; use gpui::{ color::Color, elements::*, fonts::{HighlightStyle, TextStyle, Underline}, geometry::{ rect::RectF, vector::{vec2f, Vector2F}, PathBuilder, }, json::{self, ToJson}, platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent}, text_layout::{self, Line, RunStyle, TextLayoutCache}, AnyElement, Axis, Border, CursorRegion, Element, EventContext, FontCache, LayoutContext, MouseRegion, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext, }; use itertools::Itertools; use json::json; use language::{ language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection, }; use project::{ project_settings::{GitGutterSetting, ProjectSettings}, ProjectPath, }; use smallvec::SmallVec; use std::{ borrow::Cow, cmp::{self, Ordering}, fmt::Write, iter, ops::Range, sync::Arc, }; use text::Point; use workspace::item::Item; enum FoldMarkers {} struct SelectionLayout { head: DisplayPoint, cursor_shape: CursorShape, is_newest: bool, is_local: bool, range: Range<DisplayPoint>, active_rows: Range<u32>, } impl SelectionLayout { fn new<T: ToPoint + ToDisplayPoint + Clone>( selection: Selection<T>, line_mode: bool, cursor_shape: CursorShape, map: &DisplaySnapshot, is_newest: bool, is_local: bool, ) -> Self { let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot)); let display_selection = point_selection.map(|p| p.to_display_point(map)); let mut range = display_selection.range(); let mut head = display_selection.head(); let mut active_rows = map.prev_line_boundary(point_selection.start).1.row() ..map.next_line_boundary(point_selection.end).1.row(); // vim visual line mode if line_mode { let point_range = map.expand_to_line(point_selection.range()); range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map); } // any vim visual mode (including line mode) if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed { if head.column() > 0 { head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left) } else if head.row() > 0 && head != map.max_point() { head = map.clip_point( DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)), Bias::Left, ); // updating range.end is a no-op unless you're cursor is // on the newline containing a multi-buffer divider // in which case the clip_point may have moved the head up // an additional row. range.end = DisplayPoint::new(head.row() + 1, 0); active_rows.end = head.row(); } } Self { head, cursor_shape, is_newest, is_local, range, active_rows, } } } pub struct EditorElement { style: Arc<EditorStyle>, } impl EditorElement { pub fn new(style: EditorStyle) -> Self { Self { style: Arc::new(style), } } fn attach_mouse_handlers( scene: &mut SceneBuilder, position_map: &Arc<PositionMap>, has_popovers: bool, visible_bounds: RectF, text_bounds: RectF, gutter_bounds: RectF, bounds: RectF, cx: &mut ViewContext<Editor>, ) { enum EditorElementMouseHandlers {} scene.push_mouse_region( MouseRegion::new::<EditorElementMouseHandlers>( cx.view_id(), cx.view_id(), visible_bounds, ) .on_down(MouseButton::Left, { let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_down( editor, event.platform_event, position_map.as_ref(), text_bounds, gutter_bounds, cx, ) { cx.propagate_event(); } } }) .on_down(MouseButton::Right, { let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_right_down( editor, event.position, position_map.as_ref(), text_bounds, cx, ) { cx.propagate_event(); } } }) .on_up(MouseButton::Left, { let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_up( editor, event.position, event.cmd, event.shift, event.alt, position_map.as_ref(), text_bounds, cx, ) { cx.propagate_event() } } }) .on_drag(MouseButton::Left, { let position_map = position_map.clone(); move |event, editor, cx| { if event.end { return; } if !Self::mouse_dragged( editor, event.platform_event, position_map.as_ref(), text_bounds, cx, ) { cx.propagate_event() } } }) .on_move({ let position_map = position_map.clone(); move |event, editor, cx| { if !Self::mouse_moved( editor, event.platform_event, &position_map, text_bounds, cx, ) { cx.propagate_event() } } }) .on_move_out(move |_, editor: &mut Editor, cx| { if has_popovers { hide_hover(editor, cx); } }) .on_scroll({ let position_map = position_map.clone(); move |event, editor, cx| { if !Self::scroll( editor, event.position, *event.delta.raw(), event.delta.precise(), &position_map, bounds, cx, ) { cx.propagate_event() } } }), ); enum GutterHandlers {} scene.push_mouse_region( MouseRegion::new::<GutterHandlers>(cx.view_id(), cx.view_id() + 1, gutter_bounds) .on_hover(|hover, editor: &mut Editor, cx| { editor.gutter_hover( &GutterHover { hovered: hover.started, }, cx, ); }), ) } fn mouse_down( editor: &mut Editor, MouseButtonEvent { position, modifiers: Modifiers { shift, ctrl, alt, cmd, .. }, mut click_count, .. }: MouseButtonEvent, position_map: &PositionMap, text_bounds: RectF, gutter_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { if gutter_bounds.contains_point(position) { click_count = 3; // Simulate triple-click when clicking the gutter to select lines } else if !text_bounds.contains_point(position) { return false; } let point_for_position = position_map.point_for_position(text_bounds, position); let position = point_for_position.previous_valid; if shift && alt { editor.select( SelectPhase::BeginColumnar { position, goal_column: point_for_position.exact_unclipped.column(), }, cx, ); } else if shift && !ctrl && !alt && !cmd { editor.select( SelectPhase::Extend { position, click_count, }, cx, ); } else { editor.select( SelectPhase::Begin { position, add: alt, click_count, }, cx, ); } true } fn mouse_right_down( editor: &mut Editor, position: Vector2F, position_map: &PositionMap, text_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { if !text_bounds.contains_point(position) { return false; } let point_for_position = position_map.point_for_position(text_bounds, position); mouse_context_menu::deploy_context_menu( editor, position, point_for_position.previous_valid, cx, ); true } fn mouse_up( editor: &mut Editor, position: Vector2F, cmd: bool, shift: bool, alt: bool, position_map: &PositionMap, text_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { let end_selection = editor.has_pending_selection(); let pending_nonempty_selections = editor.has_pending_nonempty_selection(); if end_selection { editor.select(SelectPhase::End, cx); } if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) { let point = position_map.point_for_position(text_bounds, position); let could_be_inlay = point.as_valid().is_none(); if shift || could_be_inlay { go_to_fetched_type_definition(editor, point, alt, cx); } else { go_to_fetched_definition(editor, point, alt, cx); } return true; } end_selection } fn mouse_dragged( editor: &mut Editor, MouseMovedEvent { modifiers: Modifiers { cmd, shift, .. }, position, .. }: MouseMovedEvent, position_map: &PositionMap, text_bounds: RectF, cx: &mut EventContext<Editor>, ) -> bool { // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed // Don't trigger hover popover if mouse is hovering over context menu let point = if text_bounds.contains_point(position) { position_map .point_for_position(text_bounds, position) .as_valid() } else { None }; update_go_to_definition_link( editor, point.map(GoToDefinitionTrigger::Text), cmd, shift, cx, ); if editor.has_pending_selection() { let mut scroll_delta = Vector2F::zero(); let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0); let top = text_bounds.origin_y() + vertical_margin; let bottom = text_bounds.lower_left().y() - vertical_margin; if position.y() < top { scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y())) } if position.y() > bottom { scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom)) } let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0); let left = text_bounds.origin_x() + horizontal_margin; let right = text_bounds.upper_right().x() - horizontal_margin; if position.x() < left { scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta( left - position.x(), )) } if position.x() > right { scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta( position.x() - right, )) } let point_for_position = position_map.point_for_position(text_bounds, position); editor.select( SelectPhase::Update { position: point_for_position.previous_valid, goal_column: point_for_position.exact_unclipped.column(), scroll_position: (position_map.snapshot.scroll_position() + scroll_delta) .clamp(Vector2F::zero(), position_map.scroll_max), }, cx, ); hover_at(editor, point, cx); true } else { hover_at(editor, point, cx); false } } fn mouse_moved( editor: &mut Editor, MouseMovedEvent { modifiers: Modifiers { shift, cmd, .. }, position, .. }: MouseMovedEvent, position_map: &PositionMap, text_bounds: RectF, cx: &mut ViewContext<Editor>, ) -> bool { // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed // Don't trigger hover popover if mouse is hovering over context menu if text_bounds.contains_point(position) { let point_for_position = position_map.point_for_position(text_bounds, position); match point_for_position.as_valid() { Some(point) => { update_go_to_definition_link( editor, Some(GoToDefinitionTrigger::Text(point)), cmd, shift, cx, ); hover_at(editor, Some(point), cx); } None => { update_inlay_link_and_hover_points( &position_map.snapshot, point_for_position, editor, cmd, shift, cx, ); } } } else { update_go_to_definition_link(editor, None, cmd, shift, cx); hover_at(editor, None, cx); } true } fn scroll( editor: &mut Editor, position: Vector2F, mut delta: Vector2F, precise: bool, position_map: &PositionMap,