element.rs

   1use crate::{
   2    display_map::{
   3        BlockContext, BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint,
   4        TransformBlock,
   5    },
   6    editor_settings::ShowScrollbar,
   7    git::{diff_hunk_to_display, DisplayDiffHunk},
   8    hover_popover::{
   9        self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
  10    },
  11    items::BufferSearchHighlights,
  12    link_go_to_definition::{
  13        go_to_fetched_definition, go_to_fetched_type_definition, show_link_definition,
  14        update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger,
  15        LinkGoToDefinitionState,
  16    },
  17    mouse_context_menu,
  18    scroll::scroll_amount::ScrollAmount,
  19    CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
  20    HalfPageDown, HalfPageUp, HoveredCursor, LineDown, LineUp, OpenExcerpts, PageDown, PageUp,
  21    Point, SelectPhase, Selection, SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
  22};
  23use anyhow::Result;
  24use collections::{BTreeMap, HashMap};
  25use git::diff::DiffHunkStatus;
  26use gpui::{
  27    div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
  28    AnchorCorner, AnyElement, AvailableSpace, Bounds, ContentMask, Corners, CursorStyle,
  29    DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla, InteractiveBounds,
  30    InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
  31    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
  32    SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
  33    TextStyle, View, ViewContext, WindowContext,
  34};
  35use itertools::Itertools;
  36use language::language_settings::ShowWhitespaceSetting;
  37use multi_buffer::Anchor;
  38use project::{
  39    project_settings::{GitGutterSetting, ProjectSettings},
  40    ProjectPath,
  41};
  42use settings::Settings;
  43use smallvec::SmallVec;
  44use std::{
  45    any::TypeId,
  46    borrow::Cow,
  47    cmp::{self, Ordering},
  48    fmt::Write,
  49    iter,
  50    ops::Range,
  51    sync::Arc,
  52};
  53use sum_tree::Bias;
  54use theme::{ActiveTheme, PlayerColor};
  55use ui::prelude::*;
  56use ui::{h_flex, ButtonLike, ButtonStyle, IconButton, Tooltip};
  57use util::ResultExt;
  58use workspace::item::Item;
  59
  60struct SelectionLayout {
  61    head: DisplayPoint,
  62    cursor_shape: CursorShape,
  63    is_newest: bool,
  64    is_local: bool,
  65    range: Range<DisplayPoint>,
  66    active_rows: Range<u32>,
  67    user_name: Option<SharedString>,
  68}
  69
  70impl SelectionLayout {
  71    fn new<T: ToPoint + ToDisplayPoint + Clone>(
  72        selection: Selection<T>,
  73        line_mode: bool,
  74        cursor_shape: CursorShape,
  75        map: &DisplaySnapshot,
  76        is_newest: bool,
  77        is_local: bool,
  78        user_name: Option<SharedString>,
  79    ) -> Self {
  80        let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
  81        let display_selection = point_selection.map(|p| p.to_display_point(map));
  82        let mut range = display_selection.range();
  83        let mut head = display_selection.head();
  84        let mut active_rows = map.prev_line_boundary(point_selection.start).1.row()
  85            ..map.next_line_boundary(point_selection.end).1.row();
  86
  87        // vim visual line mode
  88        if line_mode {
  89            let point_range = map.expand_to_line(point_selection.range());
  90            range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map);
  91        }
  92
  93        // any vim visual mode (including line mode)
  94        if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed {
  95            if head.column() > 0 {
  96                head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left)
  97            } else if head.row() > 0 && head != map.max_point() {
  98                head = map.clip_point(
  99                    DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)),
 100                    Bias::Left,
 101                );
 102                // updating range.end is a no-op unless you're cursor is
 103                // on the newline containing a multi-buffer divider
 104                // in which case the clip_point may have moved the head up
 105                // an additional row.
 106                range.end = DisplayPoint::new(head.row() + 1, 0);
 107                active_rows.end = head.row();
 108            }
 109        }
 110
 111        Self {
 112            head,
 113            cursor_shape,
 114            is_newest,
 115            is_local,
 116            range,
 117            active_rows,
 118            user_name,
 119        }
 120    }
 121}
 122
 123pub struct EditorElement {
 124    editor: View<Editor>,
 125    style: EditorStyle,
 126}
 127
 128impl EditorElement {
 129    pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
 130        Self {
 131            editor: editor.clone(),
 132            style,
 133        }
 134    }
 135
 136    fn register_actions(&self, cx: &mut WindowContext) {
 137        let view = &self.editor;
 138        view.update(cx, |editor, cx| {
 139            for action in editor.editor_actions.iter() {
 140                (action)(cx)
 141            }
 142        });
 143
 144        crate::rust_analyzer_ext::apply_related_actions(view, cx);
 145        register_action(view, cx, Editor::move_left);
 146        register_action(view, cx, Editor::move_right);
 147        register_action(view, cx, Editor::move_down);
 148        register_action(view, cx, Editor::move_up);
 149        register_action(view, cx, Editor::cancel);
 150        register_action(view, cx, Editor::newline);
 151        register_action(view, cx, Editor::newline_above);
 152        register_action(view, cx, Editor::newline_below);
 153        register_action(view, cx, Editor::backspace);
 154        register_action(view, cx, Editor::delete);
 155        register_action(view, cx, Editor::tab);
 156        register_action(view, cx, Editor::tab_prev);
 157        register_action(view, cx, Editor::indent);
 158        register_action(view, cx, Editor::outdent);
 159        register_action(view, cx, Editor::delete_line);
 160        register_action(view, cx, Editor::join_lines);
 161        register_action(view, cx, Editor::sort_lines_case_sensitive);
 162        register_action(view, cx, Editor::sort_lines_case_insensitive);
 163        register_action(view, cx, Editor::reverse_lines);
 164        register_action(view, cx, Editor::shuffle_lines);
 165        register_action(view, cx, Editor::convert_to_upper_case);
 166        register_action(view, cx, Editor::convert_to_lower_case);
 167        register_action(view, cx, Editor::convert_to_title_case);
 168        register_action(view, cx, Editor::convert_to_snake_case);
 169        register_action(view, cx, Editor::convert_to_kebab_case);
 170        register_action(view, cx, Editor::convert_to_upper_camel_case);
 171        register_action(view, cx, Editor::convert_to_lower_camel_case);
 172        register_action(view, cx, Editor::delete_to_previous_word_start);
 173        register_action(view, cx, Editor::delete_to_previous_subword_start);
 174        register_action(view, cx, Editor::delete_to_next_word_end);
 175        register_action(view, cx, Editor::delete_to_next_subword_end);
 176        register_action(view, cx, Editor::delete_to_beginning_of_line);
 177        register_action(view, cx, Editor::delete_to_end_of_line);
 178        register_action(view, cx, Editor::cut_to_end_of_line);
 179        register_action(view, cx, Editor::duplicate_line);
 180        register_action(view, cx, Editor::move_line_up);
 181        register_action(view, cx, Editor::move_line_down);
 182        register_action(view, cx, Editor::transpose);
 183        register_action(view, cx, Editor::cut);
 184        register_action(view, cx, Editor::copy);
 185        register_action(view, cx, Editor::paste);
 186        register_action(view, cx, Editor::undo);
 187        register_action(view, cx, Editor::redo);
 188        register_action(view, cx, Editor::move_page_up);
 189        register_action(view, cx, Editor::move_page_down);
 190        register_action(view, cx, Editor::next_screen);
 191        register_action(view, cx, Editor::scroll_cursor_top);
 192        register_action(view, cx, Editor::scroll_cursor_center);
 193        register_action(view, cx, Editor::scroll_cursor_bottom);
 194        register_action(view, cx, |editor, _: &LineDown, cx| {
 195            editor.scroll_screen(&ScrollAmount::Line(1.), cx)
 196        });
 197        register_action(view, cx, |editor, _: &LineUp, cx| {
 198            editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
 199        });
 200        register_action(view, cx, |editor, _: &HalfPageDown, cx| {
 201            editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
 202        });
 203        register_action(view, cx, |editor, _: &HalfPageUp, cx| {
 204            editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
 205        });
 206        register_action(view, cx, |editor, _: &PageDown, cx| {
 207            editor.scroll_screen(&ScrollAmount::Page(1.), cx)
 208        });
 209        register_action(view, cx, |editor, _: &PageUp, cx| {
 210            editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
 211        });
 212        register_action(view, cx, Editor::move_to_previous_word_start);
 213        register_action(view, cx, Editor::move_to_previous_subword_start);
 214        register_action(view, cx, Editor::move_to_next_word_end);
 215        register_action(view, cx, Editor::move_to_next_subword_end);
 216        register_action(view, cx, Editor::move_to_beginning_of_line);
 217        register_action(view, cx, Editor::move_to_end_of_line);
 218        register_action(view, cx, Editor::move_to_start_of_paragraph);
 219        register_action(view, cx, Editor::move_to_end_of_paragraph);
 220        register_action(view, cx, Editor::move_to_beginning);
 221        register_action(view, cx, Editor::move_to_end);
 222        register_action(view, cx, Editor::select_up);
 223        register_action(view, cx, Editor::select_down);
 224        register_action(view, cx, Editor::select_left);
 225        register_action(view, cx, Editor::select_right);
 226        register_action(view, cx, Editor::select_to_previous_word_start);
 227        register_action(view, cx, Editor::select_to_previous_subword_start);
 228        register_action(view, cx, Editor::select_to_next_word_end);
 229        register_action(view, cx, Editor::select_to_next_subword_end);
 230        register_action(view, cx, Editor::select_to_beginning_of_line);
 231        register_action(view, cx, Editor::select_to_end_of_line);
 232        register_action(view, cx, Editor::select_to_start_of_paragraph);
 233        register_action(view, cx, Editor::select_to_end_of_paragraph);
 234        register_action(view, cx, Editor::select_to_beginning);
 235        register_action(view, cx, Editor::select_to_end);
 236        register_action(view, cx, Editor::select_all);
 237        register_action(view, cx, |editor, action, cx| {
 238            editor.select_all_matches(action, cx).log_err();
 239        });
 240        register_action(view, cx, Editor::select_line);
 241        register_action(view, cx, Editor::split_selection_into_lines);
 242        register_action(view, cx, Editor::add_selection_above);
 243        register_action(view, cx, Editor::add_selection_below);
 244        register_action(view, cx, |editor, action, cx| {
 245            editor.select_next(action, cx).log_err();
 246        });
 247        register_action(view, cx, |editor, action, cx| {
 248            editor.select_previous(action, cx).log_err();
 249        });
 250        register_action(view, cx, Editor::toggle_comments);
 251        register_action(view, cx, Editor::select_larger_syntax_node);
 252        register_action(view, cx, Editor::select_smaller_syntax_node);
 253        register_action(view, cx, Editor::move_to_enclosing_bracket);
 254        register_action(view, cx, Editor::undo_selection);
 255        register_action(view, cx, Editor::redo_selection);
 256        register_action(view, cx, Editor::go_to_diagnostic);
 257        register_action(view, cx, Editor::go_to_prev_diagnostic);
 258        register_action(view, cx, Editor::go_to_hunk);
 259        register_action(view, cx, Editor::go_to_prev_hunk);
 260        register_action(view, cx, Editor::go_to_definition);
 261        register_action(view, cx, Editor::go_to_definition_split);
 262        register_action(view, cx, Editor::go_to_type_definition);
 263        register_action(view, cx, Editor::go_to_type_definition_split);
 264        register_action(view, cx, Editor::fold);
 265        register_action(view, cx, Editor::fold_at);
 266        register_action(view, cx, Editor::unfold_lines);
 267        register_action(view, cx, Editor::unfold_at);
 268        register_action(view, cx, Editor::fold_selected_ranges);
 269        register_action(view, cx, Editor::show_completions);
 270        register_action(view, cx, Editor::toggle_code_actions);
 271        register_action(view, cx, Editor::open_excerpts);
 272        register_action(view, cx, Editor::toggle_soft_wrap);
 273        register_action(view, cx, Editor::toggle_inlay_hints);
 274        register_action(view, cx, hover_popover::hover);
 275        register_action(view, cx, Editor::reveal_in_finder);
 276        register_action(view, cx, Editor::copy_path);
 277        register_action(view, cx, Editor::copy_relative_path);
 278        register_action(view, cx, Editor::copy_highlight_json);
 279        register_action(view, cx, |editor, action, cx| {
 280            if let Some(task) = editor.format(action, cx) {
 281                task.detach_and_log_err(cx);
 282            } else {
 283                cx.propagate();
 284            }
 285        });
 286        register_action(view, cx, Editor::restart_language_server);
 287        register_action(view, cx, Editor::show_character_palette);
 288        register_action(view, cx, |editor, action, cx| {
 289            if let Some(task) = editor.confirm_completion(action, cx) {
 290                task.detach_and_log_err(cx);
 291            } else {
 292                cx.propagate();
 293            }
 294        });
 295        register_action(view, cx, |editor, action, cx| {
 296            if let Some(task) = editor.confirm_code_action(action, cx) {
 297                task.detach_and_log_err(cx);
 298            } else {
 299                cx.propagate();
 300            }
 301        });
 302        register_action(view, cx, |editor, action, cx| {
 303            if let Some(task) = editor.rename(action, cx) {
 304                task.detach_and_log_err(cx);
 305            } else {
 306                cx.propagate();
 307            }
 308        });
 309        register_action(view, cx, |editor, action, cx| {
 310            if let Some(task) = editor.confirm_rename(action, cx) {
 311                task.detach_and_log_err(cx);
 312            } else {
 313                cx.propagate();
 314            }
 315        });
 316        register_action(view, cx, |editor, action, cx| {
 317            if let Some(task) = editor.find_all_references(action, cx) {
 318                task.detach_and_log_err(cx);
 319            } else {
 320                cx.propagate();
 321            }
 322        });
 323        register_action(view, cx, Editor::next_copilot_suggestion);
 324        register_action(view, cx, Editor::previous_copilot_suggestion);
 325        register_action(view, cx, Editor::copilot_suggest);
 326        register_action(view, cx, Editor::context_menu_first);
 327        register_action(view, cx, Editor::context_menu_prev);
 328        register_action(view, cx, Editor::context_menu_next);
 329        register_action(view, cx, Editor::context_menu_last);
 330        register_action(view, cx, Editor::display_cursor_names);
 331    }
 332
 333    fn register_key_listeners(&self, cx: &mut ElementContext) {
 334        cx.on_key_event({
 335            let editor = self.editor.clone();
 336            move |event: &ModifiersChangedEvent, phase, cx| {
 337                if phase != DispatchPhase::Bubble {
 338                    return;
 339                }
 340
 341                if editor.update(cx, |editor, cx| Self::modifiers_changed(editor, event, cx)) {
 342                    cx.stop_propagation();
 343                }
 344            }
 345        });
 346    }
 347
 348    pub(crate) fn modifiers_changed(
 349        editor: &mut Editor,
 350        event: &ModifiersChangedEvent,
 351        cx: &mut ViewContext<Editor>,
 352    ) -> bool {
 353        let pending_selection = editor.has_pending_selection();
 354
 355        if let Some(point) = &editor.link_go_to_definition_state.last_trigger_point {
 356            if event.command && !pending_selection {
 357                let point = point.clone();
 358                let snapshot = editor.snapshot(cx);
 359                let kind = point.definition_kind(event.shift);
 360
 361                show_link_definition(kind, editor, point, snapshot, cx);
 362                return false;
 363            }
 364        }
 365
 366        {
 367            if editor.link_go_to_definition_state.symbol_range.is_some()
 368                || !editor.link_go_to_definition_state.definitions.is_empty()
 369            {
 370                editor.link_go_to_definition_state.symbol_range.take();
 371                editor.link_go_to_definition_state.definitions.clear();
 372                cx.notify();
 373            }
 374
 375            editor.link_go_to_definition_state.task = None;
 376
 377            editor.clear_highlights::<LinkGoToDefinitionState>(cx);
 378        }
 379
 380        false
 381    }
 382
 383    fn mouse_left_down(
 384        editor: &mut Editor,
 385        event: &MouseDownEvent,
 386        position_map: &PositionMap,
 387        text_bounds: Bounds<Pixels>,
 388        gutter_bounds: Bounds<Pixels>,
 389        stacking_order: &StackingOrder,
 390        cx: &mut ViewContext<Editor>,
 391    ) {
 392        let mut click_count = event.click_count;
 393        let modifiers = event.modifiers;
 394
 395        if cx.default_prevented() {
 396            return;
 397        } else if gutter_bounds.contains(&event.position) {
 398            click_count = 3; // Simulate triple-click when clicking the gutter to select lines
 399        } else if !text_bounds.contains(&event.position) {
 400            return;
 401        }
 402        if !cx.was_top_layer(&event.position, stacking_order) {
 403            return;
 404        }
 405
 406        let point_for_position = position_map.point_for_position(text_bounds, event.position);
 407        let position = point_for_position.previous_valid;
 408        if modifiers.shift && modifiers.alt {
 409            editor.select(
 410                SelectPhase::BeginColumnar {
 411                    position,
 412                    goal_column: point_for_position.exact_unclipped.column(),
 413                },
 414                cx,
 415            );
 416        } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.command {
 417            editor.select(
 418                SelectPhase::Extend {
 419                    position,
 420                    click_count,
 421                },
 422                cx,
 423            );
 424        } else {
 425            editor.select(
 426                SelectPhase::Begin {
 427                    position,
 428                    add: modifiers.alt,
 429                    click_count,
 430                },
 431                cx,
 432            );
 433        }
 434
 435        cx.stop_propagation();
 436    }
 437
 438    fn mouse_right_down(
 439        editor: &mut Editor,
 440        event: &MouseDownEvent,
 441        position_map: &PositionMap,
 442        text_bounds: Bounds<Pixels>,
 443        cx: &mut ViewContext<Editor>,
 444    ) {
 445        if !text_bounds.contains(&event.position) {
 446            return;
 447        }
 448        let point_for_position = position_map.point_for_position(text_bounds, event.position);
 449        mouse_context_menu::deploy_context_menu(
 450            editor,
 451            event.position,
 452            point_for_position.previous_valid,
 453            cx,
 454        );
 455        cx.stop_propagation();
 456    }
 457
 458    fn mouse_up(
 459        editor: &mut Editor,
 460        event: &MouseUpEvent,
 461        position_map: &PositionMap,
 462        text_bounds: Bounds<Pixels>,
 463        interactive_bounds: &InteractiveBounds,
 464        stacking_order: &StackingOrder,
 465        cx: &mut ViewContext<Editor>,
 466    ) {
 467        let end_selection = editor.has_pending_selection();
 468        let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 469
 470        if end_selection {
 471            editor.select(SelectPhase::End, cx);
 472        }
 473
 474        if interactive_bounds.visibly_contains(&event.position, cx)
 475            && !pending_nonempty_selections
 476            && event.modifiers.command
 477            && text_bounds.contains(&event.position)
 478            && cx.was_top_layer(&event.position, stacking_order)
 479        {
 480            let point = position_map.point_for_position(text_bounds, event.position);
 481            let could_be_inlay = point.as_valid().is_none();
 482            let split = event.modifiers.alt;
 483            if event.modifiers.shift || could_be_inlay {
 484                go_to_fetched_type_definition(editor, point, split, cx);
 485            } else {
 486                go_to_fetched_definition(editor, point, split, cx);
 487            }
 488
 489            cx.stop_propagation();
 490        } else if end_selection {
 491            cx.stop_propagation();
 492        }
 493    }
 494
 495    fn mouse_dragged(
 496        editor: &mut Editor,
 497        event: &MouseMoveEvent,
 498        position_map: &PositionMap,
 499        text_bounds: Bounds<Pixels>,
 500        _gutter_bounds: Bounds<Pixels>,
 501        _stacking_order: &StackingOrder,
 502        cx: &mut ViewContext<Editor>,
 503    ) {
 504        if !editor.has_pending_selection() {
 505            return;
 506        }
 507
 508        let point_for_position = position_map.point_for_position(text_bounds, event.position);
 509        let mut scroll_delta = gpui::Point::<f32>::default();
 510        let vertical_margin = position_map.line_height.min(text_bounds.size.height / 3.0);
 511        let top = text_bounds.origin.y + vertical_margin;
 512        let bottom = text_bounds.lower_left().y - vertical_margin;
 513        if event.position.y < top {
 514            scroll_delta.y = -scale_vertical_mouse_autoscroll_delta(top - event.position.y);
 515        }
 516        if event.position.y > bottom {
 517            scroll_delta.y = scale_vertical_mouse_autoscroll_delta(event.position.y - bottom);
 518        }
 519
 520        let horizontal_margin = position_map.line_height.min(text_bounds.size.width / 3.0);
 521        let left = text_bounds.origin.x + horizontal_margin;
 522        let right = text_bounds.upper_right().x - horizontal_margin;
 523        if event.position.x < left {
 524            scroll_delta.x = -scale_horizontal_mouse_autoscroll_delta(left - event.position.x);
 525        }
 526        if event.position.x > right {
 527            scroll_delta.x = scale_horizontal_mouse_autoscroll_delta(event.position.x - right);
 528        }
 529
 530        editor.select(
 531            SelectPhase::Update {
 532                position: point_for_position.previous_valid,
 533                goal_column: point_for_position.exact_unclipped.column(),
 534                scroll_delta,
 535            },
 536            cx,
 537        );
 538    }
 539
 540    fn mouse_moved(
 541        editor: &mut Editor,
 542        event: &MouseMoveEvent,
 543        position_map: &PositionMap,
 544        text_bounds: Bounds<Pixels>,
 545        gutter_bounds: Bounds<Pixels>,
 546        stacking_order: &StackingOrder,
 547        cx: &mut ViewContext<Editor>,
 548    ) {
 549        let modifiers = event.modifiers;
 550        let text_hovered = text_bounds.contains(&event.position);
 551        let gutter_hovered = gutter_bounds.contains(&event.position);
 552        let was_top = cx.was_top_layer(&event.position, stacking_order);
 553
 554        editor.set_gutter_hovered(gutter_hovered, cx);
 555
 556        // Don't trigger hover popover if mouse is hovering over context menu
 557        if text_hovered && was_top {
 558            let point_for_position = position_map.point_for_position(text_bounds, event.position);
 559
 560            match point_for_position.as_valid() {
 561                Some(point) => {
 562                    update_go_to_definition_link(
 563                        editor,
 564                        Some(GoToDefinitionTrigger::Text(point)),
 565                        modifiers.command,
 566                        modifiers.shift,
 567                        cx,
 568                    );
 569                    hover_at(editor, Some(point), cx);
 570                    Self::update_visible_cursor(editor, point, position_map, cx);
 571                }
 572                None => {
 573                    update_inlay_link_and_hover_points(
 574                        &position_map.snapshot,
 575                        point_for_position,
 576                        editor,
 577                        modifiers.command,
 578                        modifiers.shift,
 579                        cx,
 580                    );
 581                }
 582            }
 583        } else {
 584            update_go_to_definition_link(editor, None, modifiers.command, modifiers.shift, cx);
 585            hover_at(editor, None, cx);
 586            if gutter_hovered && was_top {
 587                cx.stop_propagation();
 588            }
 589        }
 590    }
 591
 592    fn update_visible_cursor(
 593        editor: &mut Editor,
 594        point: DisplayPoint,
 595        position_map: &PositionMap,
 596        cx: &mut ViewContext<Editor>,
 597    ) {
 598        let snapshot = &position_map.snapshot;
 599        let Some(hub) = editor.collaboration_hub() else {
 600            return;
 601        };
 602        let range = DisplayPoint::new(point.row(), point.column().saturating_sub(1))
 603            ..DisplayPoint::new(
 604                point.row(),
 605                (point.column() + 1).min(snapshot.line_len(point.row())),
 606            );
 607
 608        let range = snapshot
 609            .buffer_snapshot
 610            .anchor_at(range.start.to_point(&snapshot.display_snapshot), Bias::Left)
 611            ..snapshot
 612                .buffer_snapshot
 613                .anchor_at(range.end.to_point(&snapshot.display_snapshot), Bias::Right);
 614
 615        let Some(selection) = snapshot.remote_selections_in_range(&range, hub, cx).next() else {
 616            return;
 617        };
 618        let key = crate::HoveredCursor {
 619            replica_id: selection.replica_id,
 620            selection_id: selection.selection.id,
 621        };
 622        editor.hovered_cursors.insert(
 623            key.clone(),
 624            cx.spawn(|editor, mut cx| async move {
 625                cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 626                editor
 627                    .update(&mut cx, |editor, cx| {
 628                        editor.hovered_cursors.remove(&key);
 629                        cx.notify();
 630                    })
 631                    .ok();
 632            }),
 633        );
 634        cx.notify()
 635    }
 636
 637    fn paint_background(
 638        &self,
 639        gutter_bounds: Bounds<Pixels>,
 640        text_bounds: Bounds<Pixels>,
 641        layout: &LayoutState,
 642        cx: &mut ElementContext,
 643    ) {
 644        let bounds = gutter_bounds.union(&text_bounds);
 645        let scroll_top =
 646            layout.position_map.snapshot.scroll_position().y * layout.position_map.line_height;
 647        let gutter_bg = cx.theme().colors().editor_gutter_background;
 648        cx.paint_quad(fill(gutter_bounds, gutter_bg));
 649        cx.paint_quad(fill(text_bounds, self.style.background));
 650
 651        if let EditorMode::Full = layout.mode {
 652            let mut active_rows = layout.active_rows.iter().peekable();
 653            while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
 654                let mut end_row = *start_row;
 655                while active_rows.peek().map_or(false, |r| {
 656                    *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
 657                }) {
 658                    active_rows.next().unwrap();
 659                    end_row += 1;
 660                }
 661
 662                if !contains_non_empty_selection {
 663                    let origin = point(
 664                        bounds.origin.x,
 665                        bounds.origin.y + (layout.position_map.line_height * *start_row as f32)
 666                            - scroll_top,
 667                    );
 668                    let size = size(
 669                        bounds.size.width,
 670                        layout.position_map.line_height * (end_row - start_row + 1) as f32,
 671                    );
 672                    let active_line_bg = cx.theme().colors().editor_active_line_background;
 673                    cx.paint_quad(fill(Bounds { origin, size }, active_line_bg));
 674                }
 675            }
 676
 677            if let Some(highlighted_rows) = &layout.highlighted_rows {
 678                let origin = point(
 679                    bounds.origin.x,
 680                    bounds.origin.y
 681                        + (layout.position_map.line_height * highlighted_rows.start as f32)
 682                        - scroll_top,
 683                );
 684                let size = size(
 685                    bounds.size.width,
 686                    layout.position_map.line_height * highlighted_rows.len() as f32,
 687                );
 688                let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background;
 689                cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg));
 690            }
 691
 692            let scroll_left =
 693                layout.position_map.snapshot.scroll_position().x * layout.position_map.em_width;
 694
 695            for (wrap_position, active) in layout.wrap_guides.iter() {
 696                let x = (text_bounds.origin.x + *wrap_position + layout.position_map.em_width / 2.)
 697                    - scroll_left;
 698
 699                if x < text_bounds.origin.x
 700                    || (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
 701                {
 702                    continue;
 703                }
 704
 705                let color = if *active {
 706                    cx.theme().colors().editor_active_wrap_guide
 707                } else {
 708                    cx.theme().colors().editor_wrap_guide
 709                };
 710                cx.paint_quad(fill(
 711                    Bounds {
 712                        origin: point(x, text_bounds.origin.y),
 713                        size: size(px(1.), text_bounds.size.height),
 714                    },
 715                    color,
 716                ));
 717            }
 718        }
 719    }
 720
 721    fn paint_gutter(
 722        &mut self,
 723        bounds: Bounds<Pixels>,
 724        layout: &mut LayoutState,
 725        cx: &mut ElementContext,
 726    ) {
 727        let line_height = layout.position_map.line_height;
 728
 729        let scroll_position = layout.position_map.snapshot.scroll_position();
 730        let scroll_top = scroll_position.y * line_height;
 731
 732        let show_gutter = matches!(
 733            ProjectSettings::get_global(cx).git.git_gutter,
 734            Some(GitGutterSetting::TrackedFiles)
 735        );
 736
 737        if show_gutter {
 738            Self::paint_diff_hunks(bounds, layout, cx);
 739        }
 740
 741        for (ix, line) in layout.line_numbers.iter().enumerate() {
 742            if let Some(line) = line {
 743                let line_origin = bounds.origin
 744                    + point(
 745                        bounds.size.width - line.width - layout.gutter_padding,
 746                        ix as f32 * line_height - (scroll_top % line_height),
 747                    );
 748
 749                line.paint(line_origin, line_height, cx).log_err();
 750            }
 751        }
 752
 753        cx.with_z_index(1, |cx| {
 754            for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
 755                if let Some(fold_indicator) = fold_indicator {
 756                    let mut fold_indicator = fold_indicator.into_any_element();
 757                    let available_space = size(
 758                        AvailableSpace::MinContent,
 759                        AvailableSpace::Definite(line_height * 0.55),
 760                    );
 761                    let fold_indicator_size = fold_indicator.measure(available_space, cx);
 762
 763                    let position = point(
 764                        bounds.size.width - layout.gutter_padding,
 765                        ix as f32 * line_height - (scroll_top % line_height),
 766                    );
 767                    let centering_offset = point(
 768                        (layout.gutter_padding + layout.gutter_margin - fold_indicator_size.width)
 769                            / 2.,
 770                        (line_height - fold_indicator_size.height) / 2.,
 771                    );
 772                    let origin = bounds.origin + position + centering_offset;
 773                    fold_indicator.draw(origin, available_space, cx);
 774                }
 775            }
 776
 777            if let Some(indicator) = layout.code_actions_indicator.take() {
 778                let mut button = indicator.button.into_any_element();
 779                let available_space = size(
 780                    AvailableSpace::MinContent,
 781                    AvailableSpace::Definite(line_height),
 782                );
 783                let indicator_size = button.measure(available_space, cx);
 784
 785                let mut x = Pixels::ZERO;
 786                let mut y = indicator.row as f32 * line_height - scroll_top;
 787                // Center indicator.
 788                x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.;
 789                y += (line_height - indicator_size.height) / 2.;
 790
 791                button.draw(bounds.origin + point(x, y), available_space, cx);
 792            }
 793        });
 794    }
 795
 796    fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
 797        let line_height = layout.position_map.line_height;
 798
 799        let scroll_position = layout.position_map.snapshot.scroll_position();
 800        let scroll_top = scroll_position.y * line_height;
 801
 802        for hunk in &layout.display_hunks {
 803            let (display_row_range, status) = match hunk {
 804                //TODO: This rendering is entirely a horrible hack
 805                &DisplayDiffHunk::Folded { display_row: row } => {
 806                    let start_y = row as f32 * line_height - scroll_top;
 807                    let end_y = start_y + line_height;
 808
 809                    let width = 0.275 * line_height;
 810                    let highlight_origin = bounds.origin + point(-width, start_y);
 811                    let highlight_size = size(width * 2., end_y - start_y);
 812                    let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
 813                    cx.paint_quad(quad(
 814                        highlight_bounds,
 815                        Corners::all(1. * line_height),
 816                        cx.theme().status().modified,
 817                        Edges::default(),
 818                        transparent_black(),
 819                    ));
 820
 821                    continue;
 822                }
 823
 824                DisplayDiffHunk::Unfolded {
 825                    display_row_range,
 826                    status,
 827                } => (display_row_range, status),
 828            };
 829
 830            let color = match status {
 831                DiffHunkStatus::Added => cx.theme().status().created,
 832                DiffHunkStatus::Modified => cx.theme().status().modified,
 833
 834                //TODO: This rendering is entirely a horrible hack
 835                DiffHunkStatus::Removed => {
 836                    let row = display_row_range.start;
 837
 838                    let offset = line_height / 2.;
 839                    let start_y = row as f32 * line_height - offset - scroll_top;
 840                    let end_y = start_y + line_height;
 841
 842                    let width = 0.275 * line_height;
 843                    let highlight_origin = bounds.origin + point(-width, start_y);
 844                    let highlight_size = size(width * 2., end_y - start_y);
 845                    let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
 846                    cx.paint_quad(quad(
 847                        highlight_bounds,
 848                        Corners::all(1. * line_height),
 849                        cx.theme().status().deleted,
 850                        Edges::default(),
 851                        transparent_black(),
 852                    ));
 853
 854                    continue;
 855                }
 856            };
 857
 858            let start_row = display_row_range.start;
 859            let end_row = display_row_range.end;
 860            // If we're in a multibuffer, row range span might include an
 861            // excerpt header, so if we were to draw the marker straight away,
 862            // the hunk might include the rows of that header.
 863            // Making the range inclusive doesn't quite cut it, as we rely on the exclusivity for the soft wrap.
 864            // Instead, we simply check whether the range we're dealing with includes
 865            // any excerpt headers and if so, we stop painting the diff hunk on the first row of that header.
 866            let end_row_in_current_excerpt = layout
 867                .position_map
 868                .snapshot
 869                .blocks_in_range(start_row..end_row)
 870                .find_map(|(start_row, block)| {
 871                    if matches!(block, TransformBlock::ExcerptHeader { .. }) {
 872                        Some(start_row)
 873                    } else {
 874                        None
 875                    }
 876                })
 877                .unwrap_or(end_row);
 878
 879            let start_y = start_row as f32 * line_height - scroll_top;
 880            let end_y = end_row_in_current_excerpt as f32 * line_height - scroll_top;
 881
 882            let width = 0.275 * line_height;
 883            let highlight_origin = bounds.origin + point(-width, start_y);
 884            let highlight_size = size(width * 2., end_y - start_y);
 885            let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
 886            cx.paint_quad(quad(
 887                highlight_bounds,
 888                Corners::all(0.05 * line_height),
 889                color,
 890                Edges::default(),
 891                transparent_black(),
 892            ));
 893        }
 894    }
 895
 896    fn paint_text(
 897        &mut self,
 898        text_bounds: Bounds<Pixels>,
 899        layout: &mut LayoutState,
 900        cx: &mut ElementContext,
 901    ) {
 902        let start_row = layout.visible_display_row_range.start;
 903        let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
 904        let line_end_overshoot = 0.15 * layout.position_map.line_height;
 905        let whitespace_setting = self
 906            .editor
 907            .read(cx)
 908            .buffer
 909            .read(cx)
 910            .settings_at(0, cx)
 911            .show_whitespaces;
 912
 913        cx.with_content_mask(
 914            Some(ContentMask {
 915                bounds: text_bounds,
 916            }),
 917            |cx| {
 918                let interactive_text_bounds = InteractiveBounds {
 919                    bounds: text_bounds,
 920                    stacking_order: cx.stacking_order().clone(),
 921                };
 922                if interactive_text_bounds.visibly_contains(&cx.mouse_position(), cx) {
 923                    if self
 924                        .editor
 925                        .read(cx)
 926                        .link_go_to_definition_state
 927                        .definitions
 928                        .is_empty()
 929                    {
 930                        cx.set_cursor_style(CursorStyle::IBeam);
 931                    } else {
 932                        cx.set_cursor_style(CursorStyle::PointingHand);
 933                    }
 934                }
 935
 936                let fold_corner_radius = 0.15 * layout.position_map.line_height;
 937                cx.with_element_id(Some("folds"), |cx| {
 938                    let snapshot = &layout.position_map.snapshot;
 939
 940                    for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
 941                        let fold_range = fold.range.clone();
 942                        let display_range = fold.range.start.to_display_point(&snapshot)
 943                            ..fold.range.end.to_display_point(&snapshot);
 944                        debug_assert_eq!(display_range.start.row(), display_range.end.row());
 945                        let row = display_range.start.row();
 946                        debug_assert!(row < layout.visible_display_row_range.end);
 947                        let Some(line_layout) = &layout
 948                            .position_map
 949                            .line_layouts
 950                            .get((row - layout.visible_display_row_range.start) as usize)
 951                            .map(|l| &l.line)
 952                        else {
 953                            continue;
 954                        };
 955
 956                        let start_x = content_origin.x
 957                            + line_layout.x_for_index(display_range.start.column() as usize)
 958                            - layout.position_map.scroll_position.x;
 959                        let start_y = content_origin.y
 960                            + row as f32 * layout.position_map.line_height
 961                            - layout.position_map.scroll_position.y;
 962                        let end_x = content_origin.x
 963                            + line_layout.x_for_index(display_range.end.column() as usize)
 964                            - layout.position_map.scroll_position.x;
 965
 966                        let fold_bounds = Bounds {
 967                            origin: point(start_x, start_y),
 968                            size: size(end_x - start_x, layout.position_map.line_height),
 969                        };
 970
 971                        let fold_background = cx.with_z_index(1, |cx| {
 972                            div()
 973                                .id(fold.id)
 974                                .size_full()
 975                                .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
 976                                .on_click(cx.listener_for(
 977                                    &self.editor,
 978                                    move |editor: &mut Editor, _, cx| {
 979                                        editor.unfold_ranges(
 980                                            [fold_range.start..fold_range.end],
 981                                            true,
 982                                            false,
 983                                            cx,
 984                                        );
 985                                        cx.stop_propagation();
 986                                    },
 987                                ))
 988                                .draw_and_update_state(
 989                                    fold_bounds.origin,
 990                                    fold_bounds.size,
 991                                    cx,
 992                                    |fold_element_state, cx| {
 993                                        if fold_element_state.is_active() {
 994                                            cx.theme().colors().ghost_element_active
 995                                        } else if fold_bounds.contains(&cx.mouse_position()) {
 996                                            cx.theme().colors().ghost_element_hover
 997                                        } else {
 998                                            cx.theme().colors().ghost_element_background
 999                                        }
1000                                    },
1001                                )
1002                        });
1003
1004                        self.paint_highlighted_range(
1005                            display_range.clone(),
1006                            fold_background,
1007                            fold_corner_radius,
1008                            fold_corner_radius * 2.,
1009                            layout,
1010                            content_origin,
1011                            text_bounds,
1012                            cx,
1013                        );
1014                    }
1015                });
1016
1017                for (range, color) in &layout.highlighted_ranges {
1018                    self.paint_highlighted_range(
1019                        range.clone(),
1020                        *color,
1021                        Pixels::ZERO,
1022                        line_end_overshoot,
1023                        layout,
1024                        content_origin,
1025                        text_bounds,
1026                        cx,
1027                    );
1028                }
1029
1030                let mut cursors = SmallVec::<[Cursor; 32]>::new();
1031                let corner_radius = 0.15 * layout.position_map.line_height;
1032                let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
1033
1034                for (participant_ix, (selection_style, selections)) in
1035                    layout.selections.iter().enumerate()
1036                {
1037                    for selection in selections.into_iter() {
1038                        self.paint_highlighted_range(
1039                            selection.range.clone(),
1040                            selection_style.selection,
1041                            corner_radius,
1042                            corner_radius * 2.,
1043                            layout,
1044                            content_origin,
1045                            text_bounds,
1046                            cx,
1047                        );
1048
1049                        if selection.is_local && !selection.range.is_empty() {
1050                            invisible_display_ranges.push(selection.range.clone());
1051                        }
1052
1053                        if !selection.is_local || self.editor.read(cx).show_local_cursors(cx) {
1054                            let cursor_position = selection.head;
1055                            if layout
1056                                .visible_display_row_range
1057                                .contains(&cursor_position.row())
1058                            {
1059                                let cursor_row_layout = &layout.position_map.line_layouts
1060                                    [(cursor_position.row() - start_row) as usize]
1061                                    .line;
1062                                let cursor_column = cursor_position.column() as usize;
1063
1064                                let cursor_character_x =
1065                                    cursor_row_layout.x_for_index(cursor_column);
1066                                let mut block_width = cursor_row_layout
1067                                    .x_for_index(cursor_column + 1)
1068                                    - cursor_character_x;
1069                                if block_width == Pixels::ZERO {
1070                                    block_width = layout.position_map.em_width;
1071                                }
1072                                let block_text = if let CursorShape::Block = selection.cursor_shape
1073                                {
1074                                    layout
1075                                        .position_map
1076                                        .snapshot
1077                                        .chars_at(cursor_position)
1078                                        .next()
1079                                        .and_then(|(character, _)| {
1080                                            let text = if character == '\n' {
1081                                                SharedString::from(" ")
1082                                            } else {
1083                                                SharedString::from(character.to_string())
1084                                            };
1085                                            let len = text.len();
1086                                            cx.text_system()
1087                                                .shape_line(
1088                                                    text,
1089                                                    cursor_row_layout.font_size,
1090                                                    &[TextRun {
1091                                                        len,
1092                                                        font: self.style.text.font(),
1093                                                        color: self.style.background,
1094                                                        background_color: None,
1095                                                        underline: None,
1096                                                    }],
1097                                                )
1098                                                .log_err()
1099                                        })
1100                                } else {
1101                                    None
1102                                };
1103
1104                                let x = cursor_character_x - layout.position_map.scroll_position.x;
1105                                let y = cursor_position.row() as f32
1106                                    * layout.position_map.line_height
1107                                    - layout.position_map.scroll_position.y;
1108                                if selection.is_newest {
1109                                    self.editor.update(cx, |editor, _| {
1110                                        editor.pixel_position_of_newest_cursor = Some(point(
1111                                            text_bounds.origin.x + x + block_width / 2.,
1112                                            text_bounds.origin.y
1113                                                + y
1114                                                + layout.position_map.line_height / 2.,
1115                                        ))
1116                                    });
1117                                }
1118
1119                                cursors.push(Cursor {
1120                                    color: selection_style.cursor,
1121                                    block_width,
1122                                    origin: point(x, y),
1123                                    line_height: layout.position_map.line_height,
1124                                    shape: selection.cursor_shape,
1125                                    block_text,
1126                                    cursor_name: selection.user_name.clone().map(|name| {
1127                                        CursorName {
1128                                            string: name,
1129                                            color: self.style.background,
1130                                            is_top_row: cursor_position.row() == 0,
1131                                            z_index: (participant_ix % 256).try_into().unwrap(),
1132                                        }
1133                                    }),
1134                                });
1135                            }
1136                        }
1137                    }
1138                }
1139
1140                for (ix, line_with_invisibles) in
1141                    layout.position_map.line_layouts.iter().enumerate()
1142                {
1143                    let row = start_row + ix as u32;
1144                    line_with_invisibles.draw(
1145                        layout,
1146                        row,
1147                        content_origin,
1148                        whitespace_setting,
1149                        &invisible_display_ranges,
1150                        cx,
1151                    )
1152                }
1153
1154                cx.with_z_index(0, |cx| {
1155                    for cursor in cursors {
1156                        cursor.paint(content_origin, cx);
1157                    }
1158                });
1159            },
1160        )
1161    }
1162
1163    fn paint_overlays(
1164        &mut self,
1165        text_bounds: Bounds<Pixels>,
1166        layout: &mut LayoutState,
1167        cx: &mut ElementContext,
1168    ) {
1169        let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
1170        let start_row = layout.visible_display_row_range.start;
1171        if let Some((position, mut context_menu)) = layout.context_menu.take() {
1172            let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
1173            let context_menu_size = context_menu.measure(available_space, cx);
1174
1175            let cursor_row_layout =
1176                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
1177            let x = cursor_row_layout.x_for_index(position.column() as usize)
1178                - layout.position_map.scroll_position.x;
1179            let y = (position.row() + 1) as f32 * layout.position_map.line_height
1180                - layout.position_map.scroll_position.y;
1181            let mut list_origin = content_origin + point(x, y);
1182            let list_width = context_menu_size.width;
1183            let list_height = context_menu_size.height;
1184
1185            // Snap the right edge of the list to the right edge of the window if
1186            // its horizontal bounds overflow.
1187            if list_origin.x + list_width > cx.viewport_size().width {
1188                list_origin.x = (cx.viewport_size().width - list_width).max(Pixels::ZERO);
1189            }
1190
1191            if list_origin.y + list_height > text_bounds.lower_right().y {
1192                list_origin.y -= layout.position_map.line_height + list_height;
1193            }
1194
1195            cx.break_content_mask(|cx| context_menu.draw(list_origin, available_space, cx));
1196        }
1197
1198        if let Some((position, mut hover_popovers)) = layout.hover_popovers.take() {
1199            let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
1200
1201            // This is safe because we check on layout whether the required row is available
1202            let hovered_row_layout =
1203                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
1204
1205            // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
1206            // height. This is the size we will use to decide whether to render popovers above or below
1207            // the hovered line.
1208            let first_size = hover_popovers[0].measure(available_space, cx);
1209            let height_to_reserve =
1210                first_size.height + 1.5 * MIN_POPOVER_LINE_HEIGHT * layout.position_map.line_height;
1211
1212            // Compute Hovered Point
1213            let x = hovered_row_layout.x_for_index(position.column() as usize)
1214                - layout.position_map.scroll_position.x;
1215            let y = position.row() as f32 * layout.position_map.line_height
1216                - layout.position_map.scroll_position.y;
1217            let hovered_point = content_origin + point(x, y);
1218
1219            if hovered_point.y - height_to_reserve > Pixels::ZERO {
1220                // There is enough space above. Render popovers above the hovered point
1221                let mut current_y = hovered_point.y;
1222                for mut hover_popover in hover_popovers {
1223                    let size = hover_popover.measure(available_space, cx);
1224                    let mut popover_origin = point(hovered_point.x, current_y - size.height);
1225
1226                    let x_out_of_bounds =
1227                        text_bounds.upper_right().x - (popover_origin.x + size.width);
1228                    if x_out_of_bounds < Pixels::ZERO {
1229                        popover_origin.x = popover_origin.x + x_out_of_bounds;
1230                    }
1231
1232                    if cx.was_top_layer(&popover_origin, cx.stacking_order()) {
1233                        cx.break_content_mask(|cx| {
1234                            hover_popover.draw(popover_origin, available_space, cx)
1235                        });
1236                    }
1237
1238                    current_y = popover_origin.y - HOVER_POPOVER_GAP;
1239                }
1240            } else {
1241                // There is not enough space above. Render popovers below the hovered point
1242                let mut current_y = hovered_point.y + layout.position_map.line_height;
1243                for mut hover_popover in hover_popovers {
1244                    let size = hover_popover.measure(available_space, cx);
1245                    let mut popover_origin = point(hovered_point.x, current_y);
1246
1247                    let x_out_of_bounds =
1248                        text_bounds.upper_right().x - (popover_origin.x + size.width);
1249                    if x_out_of_bounds < Pixels::ZERO {
1250                        popover_origin.x = popover_origin.x + x_out_of_bounds;
1251                    }
1252
1253                    hover_popover.draw(popover_origin, available_space, cx);
1254
1255                    current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP;
1256                }
1257            }
1258        }
1259
1260        if let Some(mouse_context_menu) = self.editor.read(cx).mouse_context_menu.as_ref() {
1261            let element = overlay()
1262                .position(mouse_context_menu.position)
1263                .child(mouse_context_menu.context_menu.clone())
1264                .anchor(AnchorCorner::TopLeft)
1265                .snap_to_window();
1266            element.into_any().draw(
1267                gpui::Point::default(),
1268                size(AvailableSpace::MinContent, AvailableSpace::MinContent),
1269                cx,
1270            );
1271        }
1272    }
1273
1274    fn scrollbar_left(&self, bounds: &Bounds<Pixels>) -> Pixels {
1275        bounds.upper_right().x - self.style.scrollbar_width
1276    }
1277
1278    fn paint_scrollbar(
1279        &mut self,
1280        bounds: Bounds<Pixels>,
1281        layout: &mut LayoutState,
1282        cx: &mut ElementContext,
1283    ) {
1284        if layout.mode != EditorMode::Full {
1285            return;
1286        }
1287
1288        // If a drag took place after we started dragging the scrollbar,
1289        // cancel the scrollbar drag.
1290        if cx.has_active_drag() {
1291            self.editor.update(cx, |editor, cx| {
1292                editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
1293            });
1294        }
1295
1296        let top = bounds.origin.y;
1297        let bottom = bounds.lower_left().y;
1298        let right = bounds.lower_right().x;
1299        let left = self.scrollbar_left(&bounds);
1300        let row_range = layout.scrollbar_row_range.clone();
1301        let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
1302
1303        let mut height = bounds.size.height;
1304        let mut first_row_y_offset = px(0.0);
1305
1306        // Impose a minimum height on the scrollbar thumb
1307        let row_height = height / max_row;
1308        let min_thumb_height = layout.position_map.line_height;
1309        let thumb_height = (row_range.end - row_range.start) * row_height;
1310        if thumb_height < min_thumb_height {
1311            first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
1312            height -= min_thumb_height - thumb_height;
1313        }
1314
1315        let y_for_row = |row: f32| -> Pixels { top + first_row_y_offset + row * row_height };
1316
1317        let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
1318        let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
1319        let track_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
1320        let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
1321
1322        if layout.show_scrollbars {
1323            cx.paint_quad(quad(
1324                track_bounds,
1325                Corners::default(),
1326                cx.theme().colors().scrollbar_track_background,
1327                Edges {
1328                    top: Pixels::ZERO,
1329                    right: Pixels::ZERO,
1330                    bottom: Pixels::ZERO,
1331                    left: px(1.),
1332                },
1333                cx.theme().colors().scrollbar_track_border,
1334            ));
1335            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
1336            if layout.is_singleton && scrollbar_settings.selections {
1337                let start_anchor = Anchor::min();
1338                let end_anchor = Anchor::max();
1339                let background_ranges = self
1340                    .editor
1341                    .read(cx)
1342                    .background_highlight_row_ranges::<BufferSearchHighlights>(
1343                        start_anchor..end_anchor,
1344                        &layout.position_map.snapshot,
1345                        50000,
1346                    );
1347                for range in background_ranges {
1348                    let start_y = y_for_row(range.start().row() as f32);
1349                    let mut end_y = y_for_row(range.end().row() as f32);
1350                    if end_y - start_y < px(1.) {
1351                        end_y = start_y + px(1.);
1352                    }
1353                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
1354                    cx.paint_quad(quad(
1355                        bounds,
1356                        Corners::default(),
1357                        cx.theme().status().info,
1358                        Edges {
1359                            top: Pixels::ZERO,
1360                            right: px(1.),
1361                            bottom: Pixels::ZERO,
1362                            left: px(1.),
1363                        },
1364                        cx.theme().colors().scrollbar_thumb_border,
1365                    ));
1366                }
1367            }
1368
1369            if layout.is_singleton && scrollbar_settings.git_diff {
1370                for hunk in layout
1371                    .position_map
1372                    .snapshot
1373                    .buffer_snapshot
1374                    .git_diff_hunks_in_range(0..(max_row.floor() as u32))
1375                {
1376                    let start_display = Point::new(hunk.buffer_range.start, 0)
1377                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1378                    let end_display = Point::new(hunk.buffer_range.end, 0)
1379                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1380                    let start_y = y_for_row(start_display.row() as f32);
1381                    let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
1382                        y_for_row((end_display.row() + 1) as f32)
1383                    } else {
1384                        y_for_row((end_display.row()) as f32)
1385                    };
1386
1387                    if end_y - start_y < px(1.) {
1388                        end_y = start_y + px(1.);
1389                    }
1390                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
1391
1392                    let color = match hunk.status() {
1393                        DiffHunkStatus::Added => cx.theme().status().created,
1394                        DiffHunkStatus::Modified => cx.theme().status().modified,
1395                        DiffHunkStatus::Removed => cx.theme().status().deleted,
1396                    };
1397                    cx.paint_quad(quad(
1398                        bounds,
1399                        Corners::default(),
1400                        color,
1401                        Edges {
1402                            top: Pixels::ZERO,
1403                            right: px(1.),
1404                            bottom: Pixels::ZERO,
1405                            left: px(1.),
1406                        },
1407                        cx.theme().colors().scrollbar_thumb_border,
1408                    ));
1409                }
1410            }
1411
1412            cx.paint_quad(quad(
1413                thumb_bounds,
1414                Corners::default(),
1415                cx.theme().colors().scrollbar_thumb_background,
1416                Edges {
1417                    top: Pixels::ZERO,
1418                    right: px(1.),
1419                    bottom: Pixels::ZERO,
1420                    left: px(1.),
1421                },
1422                cx.theme().colors().scrollbar_thumb_border,
1423            ));
1424        }
1425
1426        let interactive_track_bounds = InteractiveBounds {
1427            bounds: track_bounds,
1428            stacking_order: cx.stacking_order().clone(),
1429        };
1430        let mut mouse_position = cx.mouse_position();
1431        if interactive_track_bounds.visibly_contains(&mouse_position, cx) {
1432            cx.set_cursor_style(CursorStyle::Arrow);
1433        }
1434
1435        cx.on_mouse_event({
1436            let editor = self.editor.clone();
1437            move |event: &MouseMoveEvent, phase, cx| {
1438                if phase == DispatchPhase::Capture {
1439                    return;
1440                }
1441
1442                editor.update(cx, |editor, cx| {
1443                    if event.pressed_button == Some(MouseButton::Left)
1444                        && editor.scroll_manager.is_dragging_scrollbar()
1445                    {
1446                        let y = mouse_position.y;
1447                        let new_y = event.position.y;
1448                        if (track_bounds.top()..track_bounds.bottom()).contains(&y) {
1449                            let mut position = editor.scroll_position(cx);
1450                            position.y += (new_y - y) * (max_row as f32) / height;
1451                            if position.y < 0.0 {
1452                                position.y = 0.0;
1453                            }
1454                            editor.set_scroll_position(position, cx);
1455                        }
1456
1457                        mouse_position = event.position;
1458                        cx.stop_propagation();
1459                    } else {
1460                        editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
1461                        if interactive_track_bounds.visibly_contains(&event.position, cx) {
1462                            editor.scroll_manager.show_scrollbar(cx);
1463                        }
1464                    }
1465                })
1466            }
1467        });
1468
1469        if self.editor.read(cx).scroll_manager.is_dragging_scrollbar() {
1470            cx.on_mouse_event({
1471                let editor = self.editor.clone();
1472                move |_: &MouseUpEvent, phase, cx| {
1473                    if phase == DispatchPhase::Capture {
1474                        return;
1475                    }
1476
1477                    editor.update(cx, |editor, cx| {
1478                        editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
1479                        cx.stop_propagation();
1480                    });
1481                }
1482            });
1483        } else {
1484            cx.on_mouse_event({
1485                let editor = self.editor.clone();
1486                move |event: &MouseDownEvent, phase, cx| {
1487                    if phase == DispatchPhase::Capture {
1488                        return;
1489                    }
1490
1491                    editor.update(cx, |editor, cx| {
1492                        if track_bounds.contains(&event.position) {
1493                            editor.scroll_manager.set_is_dragging_scrollbar(true, cx);
1494
1495                            let y = event.position.y;
1496                            if y < thumb_top || thumb_bottom < y {
1497                                let center_row =
1498                                    ((y - top) * max_row as f32 / height).round() as u32;
1499                                let top_row = center_row
1500                                    .saturating_sub((row_range.end - row_range.start) as u32 / 2);
1501                                let mut position = editor.scroll_position(cx);
1502                                position.y = top_row as f32;
1503                                editor.set_scroll_position(position, cx);
1504                            } else {
1505                                editor.scroll_manager.show_scrollbar(cx);
1506                            }
1507
1508                            cx.stop_propagation();
1509                        }
1510                    });
1511                }
1512            });
1513        }
1514    }
1515
1516    #[allow(clippy::too_many_arguments)]
1517    fn paint_highlighted_range(
1518        &self,
1519        range: Range<DisplayPoint>,
1520        color: Hsla,
1521        corner_radius: Pixels,
1522        line_end_overshoot: Pixels,
1523        layout: &LayoutState,
1524        content_origin: gpui::Point<Pixels>,
1525        bounds: Bounds<Pixels>,
1526        cx: &mut ElementContext,
1527    ) {
1528        let start_row = layout.visible_display_row_range.start;
1529        let end_row = layout.visible_display_row_range.end;
1530        if range.start != range.end {
1531            let row_range = if range.end.column() == 0 {
1532                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
1533            } else {
1534                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
1535            };
1536
1537            let highlighted_range = HighlightedRange {
1538                color,
1539                line_height: layout.position_map.line_height,
1540                corner_radius,
1541                start_y: content_origin.y
1542                    + row_range.start as f32 * layout.position_map.line_height
1543                    - layout.position_map.scroll_position.y,
1544                lines: row_range
1545                    .into_iter()
1546                    .map(|row| {
1547                        let line_layout =
1548                            &layout.position_map.line_layouts[(row - start_row) as usize].line;
1549                        HighlightedRangeLine {
1550                            start_x: if row == range.start.row() {
1551                                content_origin.x
1552                                    + line_layout.x_for_index(range.start.column() as usize)
1553                                    - layout.position_map.scroll_position.x
1554                            } else {
1555                                content_origin.x - layout.position_map.scroll_position.x
1556                            },
1557                            end_x: if row == range.end.row() {
1558                                content_origin.x
1559                                    + line_layout.x_for_index(range.end.column() as usize)
1560                                    - layout.position_map.scroll_position.x
1561                            } else {
1562                                content_origin.x + line_layout.width + line_end_overshoot
1563                                    - layout.position_map.scroll_position.x
1564                            },
1565                        }
1566                    })
1567                    .collect(),
1568            };
1569
1570            highlighted_range.paint(bounds, cx);
1571        }
1572    }
1573
1574    fn paint_blocks(
1575        &mut self,
1576        bounds: Bounds<Pixels>,
1577        layout: &mut LayoutState,
1578        cx: &mut ElementContext,
1579    ) {
1580        let scroll_position = layout.position_map.snapshot.scroll_position();
1581        let scroll_left = scroll_position.x * layout.position_map.em_width;
1582        let scroll_top = scroll_position.y * layout.position_map.line_height;
1583
1584        for mut block in layout.blocks.drain(..) {
1585            let mut origin = bounds.origin
1586                + point(
1587                    Pixels::ZERO,
1588                    block.row as f32 * layout.position_map.line_height - scroll_top,
1589                );
1590            if !matches!(block.style, BlockStyle::Sticky) {
1591                origin += point(-scroll_left, Pixels::ZERO);
1592            }
1593            block.element.draw(origin, block.available_space, cx);
1594        }
1595    }
1596
1597    fn column_pixels(&self, column: usize, cx: &WindowContext) -> Pixels {
1598        let style = &self.style;
1599        let font_size = style.text.font_size.to_pixels(cx.rem_size());
1600        let layout = cx
1601            .text_system()
1602            .shape_line(
1603                SharedString::from(" ".repeat(column)),
1604                font_size,
1605                &[TextRun {
1606                    len: column,
1607                    font: style.text.font(),
1608                    color: Hsla::default(),
1609                    background_color: None,
1610                    underline: None,
1611                }],
1612            )
1613            .unwrap();
1614
1615        layout.width
1616    }
1617
1618    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &WindowContext) -> Pixels {
1619        let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
1620        self.column_pixels(digit_count, cx)
1621    }
1622
1623    //Folds contained in a hunk are ignored apart from shrinking visual size
1624    //If a fold contains any hunks then that fold line is marked as modified
1625    fn layout_git_gutters(
1626        &self,
1627        display_rows: Range<u32>,
1628        snapshot: &EditorSnapshot,
1629    ) -> Vec<DisplayDiffHunk> {
1630        let buffer_snapshot = &snapshot.buffer_snapshot;
1631
1632        let buffer_start_row = DisplayPoint::new(display_rows.start, 0)
1633            .to_point(snapshot)
1634            .row;
1635        let buffer_end_row = DisplayPoint::new(display_rows.end, 0)
1636            .to_point(snapshot)
1637            .row;
1638
1639        buffer_snapshot
1640            .git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
1641            .map(|hunk| diff_hunk_to_display(hunk, snapshot))
1642            .dedup()
1643            .collect()
1644    }
1645
1646    fn calculate_relative_line_numbers(
1647        &self,
1648        snapshot: &EditorSnapshot,
1649        rows: &Range<u32>,
1650        relative_to: Option<u32>,
1651    ) -> HashMap<u32, u32> {
1652        let mut relative_rows: HashMap<u32, u32> = Default::default();
1653        let Some(relative_to) = relative_to else {
1654            return relative_rows;
1655        };
1656
1657        let start = rows.start.min(relative_to);
1658        let end = rows.end.max(relative_to);
1659
1660        let buffer_rows = snapshot
1661            .buffer_rows(start)
1662            .take(1 + (end - start) as usize)
1663            .collect::<Vec<_>>();
1664
1665        let head_idx = relative_to - start;
1666        let mut delta = 1;
1667        let mut i = head_idx + 1;
1668        while i < buffer_rows.len() as u32 {
1669            if buffer_rows[i as usize].is_some() {
1670                if rows.contains(&(i + start)) {
1671                    relative_rows.insert(i + start, delta);
1672                }
1673                delta += 1;
1674            }
1675            i += 1;
1676        }
1677        delta = 1;
1678        i = head_idx.min(buffer_rows.len() as u32 - 1);
1679        while i > 0 && buffer_rows[i as usize].is_none() {
1680            i -= 1;
1681        }
1682
1683        while i > 0 {
1684            i -= 1;
1685            if buffer_rows[i as usize].is_some() {
1686                if rows.contains(&(i + start)) {
1687                    relative_rows.insert(i + start, delta);
1688                }
1689                delta += 1;
1690            }
1691        }
1692
1693        relative_rows
1694    }
1695
1696    fn shape_line_numbers(
1697        &self,
1698        rows: Range<u32>,
1699        active_rows: &BTreeMap<u32, bool>,
1700        newest_selection_head: DisplayPoint,
1701        is_singleton: bool,
1702        snapshot: &EditorSnapshot,
1703        cx: &ViewContext<Editor>,
1704    ) -> (
1705        Vec<Option<ShapedLine>>,
1706        Vec<Option<(FoldStatus, BufferRow, bool)>>,
1707    ) {
1708        let font_size = self.style.text.font_size.to_pixels(cx.rem_size());
1709        let include_line_numbers = snapshot.mode == EditorMode::Full;
1710        let mut shaped_line_numbers = Vec::with_capacity(rows.len());
1711        let mut fold_statuses = Vec::with_capacity(rows.len());
1712        let mut line_number = String::new();
1713        let is_relative = EditorSettings::get_global(cx).relative_line_numbers;
1714        let relative_to = if is_relative {
1715            Some(newest_selection_head.row())
1716        } else {
1717            None
1718        };
1719
1720        let relative_rows = self.calculate_relative_line_numbers(&snapshot, &rows, relative_to);
1721
1722        for (ix, row) in snapshot
1723            .buffer_rows(rows.start)
1724            .take((rows.end - rows.start) as usize)
1725            .enumerate()
1726        {
1727            let display_row = rows.start + ix as u32;
1728            let (active, color) = if active_rows.contains_key(&display_row) {
1729                (true, cx.theme().colors().editor_active_line_number)
1730            } else {
1731                (false, cx.theme().colors().editor_line_number)
1732            };
1733            if let Some(buffer_row) = row {
1734                if include_line_numbers {
1735                    line_number.clear();
1736                    let default_number = buffer_row + 1;
1737                    let number = relative_rows
1738                        .get(&(ix as u32 + rows.start))
1739                        .unwrap_or(&default_number);
1740                    write!(&mut line_number, "{}", number).unwrap();
1741                    let run = TextRun {
1742                        len: line_number.len(),
1743                        font: self.style.text.font(),
1744                        color,
1745                        background_color: None,
1746                        underline: None,
1747                    };
1748                    let shaped_line = cx
1749                        .text_system()
1750                        .shape_line(line_number.clone().into(), font_size, &[run])
1751                        .unwrap();
1752                    shaped_line_numbers.push(Some(shaped_line));
1753                    fold_statuses.push(
1754                        is_singleton
1755                            .then(|| {
1756                                snapshot
1757                                    .fold_for_line(buffer_row)
1758                                    .map(|fold_status| (fold_status, buffer_row, active))
1759                            })
1760                            .flatten(),
1761                    )
1762                }
1763            } else {
1764                fold_statuses.push(None);
1765                shaped_line_numbers.push(None);
1766            }
1767        }
1768
1769        (shaped_line_numbers, fold_statuses)
1770    }
1771
1772    fn layout_lines(
1773        &self,
1774        rows: Range<u32>,
1775        line_number_layouts: &[Option<ShapedLine>],
1776        snapshot: &EditorSnapshot,
1777        cx: &ViewContext<Editor>,
1778    ) -> Vec<LineWithInvisibles> {
1779        if rows.start >= rows.end {
1780            return Vec::new();
1781        }
1782
1783        // Show the placeholder when the editor is empty
1784        if snapshot.is_empty() {
1785            let font_size = self.style.text.font_size.to_pixels(cx.rem_size());
1786            let placeholder_color = cx.theme().colors().text_placeholder;
1787            let placeholder_text = snapshot.placeholder_text();
1788
1789            let placeholder_lines = placeholder_text
1790                .as_ref()
1791                .map_or("", AsRef::as_ref)
1792                .split('\n')
1793                .skip(rows.start as usize)
1794                .chain(iter::repeat(""))
1795                .take(rows.len());
1796            placeholder_lines
1797                .filter_map(move |line| {
1798                    let run = TextRun {
1799                        len: line.len(),
1800                        font: self.style.text.font(),
1801                        color: placeholder_color,
1802                        background_color: None,
1803                        underline: Default::default(),
1804                    };
1805                    cx.text_system()
1806                        .shape_line(line.to_string().into(), font_size, &[run])
1807                        .log_err()
1808                })
1809                .map(|line| LineWithInvisibles {
1810                    line,
1811                    invisibles: Vec::new(),
1812                })
1813                .collect()
1814        } else {
1815            let chunks = snapshot.highlighted_chunks(rows.clone(), true, &self.style);
1816            LineWithInvisibles::from_chunks(
1817                chunks,
1818                &self.style.text,
1819                MAX_LINE_LEN,
1820                rows.len() as usize,
1821                line_number_layouts,
1822                snapshot.mode,
1823                cx,
1824            )
1825        }
1826    }
1827
1828    fn compute_layout(&mut self, bounds: Bounds<Pixels>, cx: &mut ElementContext) -> LayoutState {
1829        self.editor.update(cx, |editor, cx| {
1830            let snapshot = editor.snapshot(cx);
1831            let style = self.style.clone();
1832
1833            let font_id = cx.text_system().resolve_font(&style.text.font());
1834            let font_size = style.text.font_size.to_pixels(cx.rem_size());
1835            let line_height = style.text.line_height_in_pixels(cx.rem_size());
1836            let em_width = cx
1837                .text_system()
1838                .typographic_bounds(font_id, font_size, 'm')
1839                .unwrap()
1840                .size
1841                .width;
1842            let em_advance = cx
1843                .text_system()
1844                .advance(font_id, font_size, 'm')
1845                .unwrap()
1846                .width;
1847
1848            let gutter_dimensions = snapshot.gutter_dimensions(font_id, font_size, em_width, self.max_line_number_width(&snapshot, cx), cx);
1849
1850            editor.gutter_width = gutter_dimensions.width;
1851
1852            let text_width = bounds.size.width - gutter_dimensions.width;
1853            let overscroll = size(em_width, px(0.));
1854            let _snapshot = {
1855                editor.set_visible_line_count((bounds.size.height / line_height).into(), cx);
1856
1857                let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width;
1858                let wrap_width = match editor.soft_wrap_mode(cx) {
1859                    SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
1860                    SoftWrap::EditorWidth => editor_width,
1861                    SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
1862                };
1863
1864                if editor.set_wrap_width(Some(wrap_width), cx) {
1865                    editor.snapshot(cx)
1866                } else {
1867                    snapshot
1868                }
1869            };
1870
1871            let wrap_guides = editor
1872                .wrap_guides(cx)
1873                .iter()
1874                .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
1875                .collect::<SmallVec<[_; 2]>>();
1876
1877            let gutter_size = size(gutter_dimensions.width, bounds.size.height);
1878            let text_size = size(text_width, bounds.size.height);
1879
1880            let autoscroll_horizontally =
1881                editor.autoscroll_vertically(bounds.size.height, line_height, cx);
1882            let mut snapshot = editor.snapshot(cx);
1883
1884            let scroll_position = snapshot.scroll_position();
1885            // The scroll position is a fractional point, the whole number of which represents
1886            // the top of the window in terms of display rows.
1887            let start_row = scroll_position.y as u32;
1888            let height_in_lines = f32::from(bounds.size.height / line_height);
1889            let max_row = snapshot.max_point().row();
1890
1891            // Add 1 to ensure selections bleed off screen
1892            let end_row = 1 + cmp::min((scroll_position.y + height_in_lines).ceil() as u32, max_row);
1893
1894            let start_anchor = if start_row == 0 {
1895                Anchor::min()
1896            } else {
1897                snapshot
1898                    .buffer_snapshot
1899                    .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
1900            };
1901            let end_anchor = if end_row > max_row {
1902                Anchor::max()
1903            } else {
1904                snapshot
1905                    .buffer_snapshot
1906                    .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
1907            };
1908
1909            let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
1910            let mut active_rows = BTreeMap::new();
1911            let is_singleton = editor.is_singleton(cx);
1912
1913            let highlighted_rows = editor.highlighted_rows();
1914            let highlighted_ranges = editor.background_highlights_in_range(
1915                start_anchor..end_anchor,
1916                &snapshot.display_snapshot,
1917                cx.theme().colors(),
1918            );
1919
1920            let mut newest_selection_head = None;
1921
1922            if editor.show_local_selections {
1923                let mut local_selections: Vec<Selection<Point>> = editor
1924                    .selections
1925                    .disjoint_in_range(start_anchor..end_anchor, cx);
1926                local_selections.extend(editor.selections.pending(cx));
1927                let mut layouts = Vec::new();
1928                let newest = editor.selections.newest(cx);
1929                for selection in local_selections.drain(..) {
1930                    let is_empty = selection.start == selection.end;
1931                    let is_newest = selection == newest;
1932
1933                    let layout = SelectionLayout::new(
1934                        selection,
1935                        editor.selections.line_mode,
1936                        editor.cursor_shape,
1937                        &snapshot.display_snapshot,
1938                        is_newest,
1939                        true,
1940                        None,
1941                    );
1942                    if is_newest {
1943                        newest_selection_head = Some(layout.head);
1944                    }
1945
1946                    for row in cmp::max(layout.active_rows.start, start_row)
1947                        ..=cmp::min(layout.active_rows.end, end_row)
1948                    {
1949                        let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
1950                        *contains_non_empty_selection |= !is_empty;
1951                    }
1952                    layouts.push(layout);
1953                }
1954
1955                let player = if editor.read_only(cx) {
1956                    cx.theme().players().read_only()
1957                } else {
1958                    style.local_player
1959                };
1960
1961                selections.push((player, layouts));
1962            }
1963
1964            if let Some(collaboration_hub) = &editor.collaboration_hub {
1965                // When following someone, render the local selections in their color.
1966                if let Some(leader_id) = editor.leader_peer_id {
1967                    if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) {
1968                        if let Some(participant_index) = collaboration_hub
1969                            .user_participant_indices(cx)
1970                            .get(&collaborator.user_id)
1971                        {
1972                            if let Some((local_selection_style, _)) = selections.first_mut() {
1973                                *local_selection_style = cx
1974                                    .theme()
1975                                    .players()
1976                                    .color_for_participant(participant_index.0);
1977                            }
1978                        }
1979                    }
1980                }
1981
1982                let mut remote_selections = HashMap::default();
1983                for selection in snapshot.remote_selections_in_range(
1984                    &(start_anchor..end_anchor),
1985                    collaboration_hub.as_ref(),
1986                    cx,
1987                ) {
1988                    let selection_style = if let Some(participant_index) = selection.participant_index {
1989                        cx.theme()
1990                            .players()
1991                            .color_for_participant(participant_index.0)
1992                    } else {
1993                        cx.theme().players().absent()
1994                    };
1995
1996                    // Don't re-render the leader's selections, since the local selections
1997                    // match theirs.
1998                    if Some(selection.peer_id) == editor.leader_peer_id {
1999                        continue;
2000                    }
2001                    let key = HoveredCursor{replica_id: selection.replica_id, selection_id: selection.selection.id};
2002
2003                    let is_shown = editor.show_cursor_names || editor.hovered_cursors.contains_key(&key);
2004
2005                    remote_selections
2006                        .entry(selection.replica_id)
2007                        .or_insert((selection_style, Vec::new()))
2008                        .1
2009                        .push(SelectionLayout::new(
2010                            selection.selection,
2011                            selection.line_mode,
2012                            selection.cursor_shape,
2013                            &snapshot.display_snapshot,
2014                            false,
2015                            false,
2016                            if is_shown {
2017                                selection.user_name
2018                            } else {
2019                                None
2020                            },
2021                        ));
2022                }
2023
2024                selections.extend(remote_selections.into_values());
2025            }
2026
2027            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
2028            let show_scrollbars = match scrollbar_settings.show {
2029                ShowScrollbar::Auto => {
2030                    // Git
2031                    (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
2032                    ||
2033                    // Selections
2034                    (is_singleton && scrollbar_settings.selections && editor.has_background_highlights::<BufferSearchHighlights>())
2035                    // Scrollmanager
2036                    || editor.scroll_manager.scrollbars_visible()
2037                }
2038                ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
2039                ShowScrollbar::Always => true,
2040                ShowScrollbar::Never => false,
2041            };
2042
2043            let head_for_relative = newest_selection_head.unwrap_or_else(|| {
2044                let newest = editor.selections.newest::<Point>(cx);
2045                SelectionLayout::new(
2046                    newest,
2047                    editor.selections.line_mode,
2048                    editor.cursor_shape,
2049                    &snapshot.display_snapshot,
2050                    true,
2051                    true,
2052                    None,
2053                )
2054                .head
2055            });
2056
2057            let (line_numbers, fold_statuses) = self.shape_line_numbers(
2058                start_row..end_row,
2059                &active_rows,
2060                head_for_relative,
2061                is_singleton,
2062                &snapshot,
2063                cx,
2064            );
2065
2066            let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
2067
2068            let scrollbar_row_range = scroll_position.y..(scroll_position.y + height_in_lines);
2069
2070            let mut max_visible_line_width = Pixels::ZERO;
2071            let line_layouts = self.layout_lines(start_row..end_row, &line_numbers, &snapshot, cx);
2072            for line_with_invisibles in &line_layouts {
2073                if line_with_invisibles.line.width > max_visible_line_width {
2074                    max_visible_line_width = line_with_invisibles.line.width;
2075                }
2076            }
2077
2078            let longest_line_width = layout_line(snapshot.longest_row(), &snapshot, &style, cx)
2079                .unwrap()
2080                .width;
2081            let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
2082
2083            let editor_view = cx.view().clone();
2084            let (scroll_width, blocks) = cx.with_element_context(|cx| {
2085             cx.with_element_id(Some("editor_blocks"), |cx| {
2086                self.layout_blocks(
2087                    start_row..end_row,
2088                    &snapshot,
2089                    bounds.size.width,
2090                    scroll_width,
2091                    text_width,
2092                    gutter_dimensions.padding,
2093                    gutter_dimensions.width,
2094                    em_width,
2095                    gutter_dimensions.width + gutter_dimensions.margin,
2096                    line_height,
2097                    &style,
2098                    &line_layouts,
2099                    editor,
2100                    editor_view,
2101                    cx,
2102                )
2103            })
2104            });
2105
2106            let scroll_max = point(
2107                f32::from((scroll_width - text_size.width) / em_width).max(0.0),
2108                max_row as f32,
2109            );
2110
2111            let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x);
2112
2113            let autoscrolled = if autoscroll_horizontally {
2114                editor.autoscroll_horizontally(
2115                    start_row,
2116                    text_size.width,
2117                    scroll_width,
2118                    em_width,
2119                    &line_layouts,
2120                    cx,
2121                )
2122            } else {
2123                false
2124            };
2125
2126            if clamped || autoscrolled {
2127                snapshot = editor.snapshot(cx);
2128            }
2129
2130            let mut context_menu = None;
2131            let mut code_actions_indicator = None;
2132            if let Some(newest_selection_head) = newest_selection_head {
2133                if (start_row..end_row).contains(&newest_selection_head.row()) {
2134                    if editor.context_menu_visible() {
2135                        let max_height = cmp::min(
2136                            12. * line_height,
2137                            cmp::max(
2138                                3. * line_height,
2139                                (bounds.size.height - line_height) / 2.,
2140                            )
2141                        );
2142                        context_menu =
2143                            editor.render_context_menu(newest_selection_head, &self.style, max_height, cx);
2144                    }
2145
2146                    let active = matches!(
2147                        editor.context_menu.read().as_ref(),
2148                        Some(crate::ContextMenu::CodeActions(_))
2149                    );
2150
2151                    code_actions_indicator = editor
2152                        .render_code_actions_indicator(&style, active, cx)
2153                        .map(|element| CodeActionsIndicator {
2154                            row: newest_selection_head.row(),
2155                            button: element,
2156                        });
2157                }
2158            }
2159
2160            let visible_rows = start_row..start_row + line_layouts.len() as u32;
2161            let max_size = size(
2162                (120. * em_width) // Default size
2163                    .min(bounds.size.width / 2.) // Shrink to half of the editor width
2164                    .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
2165                (16. * line_height) // Default size
2166                    .min(bounds.size.height / 2.) // Shrink to half of the editor height
2167                    .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
2168            );
2169
2170            let hover = if context_menu.is_some() {
2171                None
2172            } else {
2173                editor.hover_state.render(
2174                &snapshot,
2175                &style,
2176                visible_rows,
2177                max_size,
2178                editor.workspace.as_ref().map(|(w, _)| w.clone()),
2179                cx,
2180            )
2181            };
2182
2183            let editor_view = cx.view().clone();
2184            let fold_indicators = cx.with_element_context(|cx| {
2185
2186                cx.with_element_id(Some("gutter_fold_indicators"), |_cx| {
2187                editor.render_fold_indicators(
2188                    fold_statuses,
2189                    &style,
2190                    editor.gutter_hovered,
2191                    line_height,
2192                    gutter_dimensions.margin,
2193                    editor_view,
2194                )
2195            })
2196            });
2197
2198            let invisible_symbol_font_size = font_size / 2.;
2199            let tab_invisible = cx
2200                .text_system()
2201                .shape_line(
2202                    "".into(),
2203                    invisible_symbol_font_size,
2204                    &[TextRun {
2205                        len: "".len(),
2206                        font: self.style.text.font(),
2207                        color: cx.theme().colors().editor_invisible,
2208                        background_color: None,
2209                        underline: None,
2210                    }],
2211                )
2212                .unwrap();
2213            let space_invisible = cx
2214                .text_system()
2215                .shape_line(
2216                    "".into(),
2217                    invisible_symbol_font_size,
2218                    &[TextRun {
2219                        len: "".len(),
2220                        font: self.style.text.font(),
2221                        color: cx.theme().colors().editor_invisible,
2222                        background_color: None,
2223                        underline: None,
2224                    }],
2225                )
2226                .unwrap();
2227
2228            LayoutState {
2229                mode: snapshot.mode,
2230                position_map: Arc::new(PositionMap {
2231                    size: bounds.size,
2232                    scroll_position: point(
2233                        scroll_position.x * em_width,
2234                        scroll_position.y * line_height,
2235                    ),
2236                    scroll_max,
2237                    line_layouts,
2238                    line_height,
2239                    em_width,
2240                    em_advance,
2241                    snapshot,
2242                }),
2243                visible_anchor_range: start_anchor..end_anchor,
2244                visible_display_row_range: start_row..end_row,
2245                wrap_guides,
2246                gutter_size,
2247                gutter_padding: gutter_dimensions.padding,
2248                text_size,
2249                scrollbar_row_range,
2250                show_scrollbars,
2251                is_singleton,
2252                max_row,
2253                gutter_margin: gutter_dimensions.margin,
2254                active_rows,
2255                highlighted_rows,
2256                highlighted_ranges,
2257                line_numbers,
2258                display_hunks,
2259                blocks,
2260                selections,
2261                context_menu,
2262                code_actions_indicator,
2263                fold_indicators,
2264                tab_invisible,
2265                space_invisible,
2266                hover_popovers: hover,
2267            }
2268        })
2269    }
2270
2271    #[allow(clippy::too_many_arguments)]
2272    fn layout_blocks(
2273        &self,
2274        rows: Range<u32>,
2275        snapshot: &EditorSnapshot,
2276        editor_width: Pixels,
2277        scroll_width: Pixels,
2278        text_width: Pixels,
2279        gutter_padding: Pixels,
2280        gutter_width: Pixels,
2281        em_width: Pixels,
2282        text_x: Pixels,
2283        line_height: Pixels,
2284        style: &EditorStyle,
2285        line_layouts: &[LineWithInvisibles],
2286        editor: &mut Editor,
2287        editor_view: View<Editor>,
2288        cx: &mut ElementContext,
2289    ) -> (Pixels, Vec<BlockLayout>) {
2290        let mut block_id = 0;
2291        let (fixed_blocks, non_fixed_blocks) = snapshot
2292            .blocks_in_range(rows.clone())
2293            .partition::<Vec<_>, _>(|(_, block)| match block {
2294                TransformBlock::ExcerptHeader { .. } => false,
2295                TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
2296            });
2297
2298        let render_block = |block: &TransformBlock,
2299                            available_space: Size<AvailableSpace>,
2300                            block_id: usize,
2301                            editor: &mut Editor,
2302                            cx: &mut ElementContext| {
2303            let mut element = match block {
2304                TransformBlock::Custom(block) => {
2305                    let align_to = block
2306                        .position()
2307                        .to_point(&snapshot.buffer_snapshot)
2308                        .to_display_point(snapshot);
2309                    let anchor_x = text_x
2310                        + if rows.contains(&align_to.row()) {
2311                            line_layouts[(align_to.row() - rows.start) as usize]
2312                                .line
2313                                .x_for_index(align_to.column() as usize)
2314                        } else {
2315                            layout_line(align_to.row(), snapshot, style, cx)
2316                                .unwrap()
2317                                .x_for_index(align_to.column() as usize)
2318                        };
2319
2320                    block.render(&mut BlockContext {
2321                        context: cx,
2322                        anchor_x,
2323                        gutter_padding,
2324                        line_height,
2325                        gutter_width,
2326                        em_width,
2327                        block_id,
2328                        max_width: scroll_width.max(text_width),
2329                        view: editor_view.clone(),
2330                        editor_style: &self.style,
2331                    })
2332                }
2333
2334                TransformBlock::ExcerptHeader {
2335                    buffer,
2336                    range,
2337                    starts_new_buffer,
2338                    ..
2339                } => {
2340                    let include_root = editor
2341                        .project
2342                        .as_ref()
2343                        .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
2344                        .unwrap_or_default();
2345
2346                    let jump_handler = project::File::from_dyn(buffer.file()).map(|file| {
2347                        let jump_path = ProjectPath {
2348                            worktree_id: file.worktree_id(cx),
2349                            path: file.path.clone(),
2350                        };
2351                        let jump_anchor = range
2352                            .primary
2353                            .as_ref()
2354                            .map_or(range.context.start, |primary| primary.start);
2355                        let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
2356
2357                        cx.listener_for(&self.editor, move |editor, _, cx| {
2358                            editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
2359                        })
2360                    });
2361
2362                    let element = if *starts_new_buffer {
2363                        let path = buffer.resolve_file_path(cx, include_root);
2364                        let mut filename = None;
2365                        let mut parent_path = None;
2366                        // Can't use .and_then() because `.file_name()` and `.parent()` return references :(
2367                        if let Some(path) = path {
2368                            filename = path.file_name().map(|f| f.to_string_lossy().to_string());
2369                            parent_path = path
2370                                .parent()
2371                                .map(|p| SharedString::from(p.to_string_lossy().to_string() + "/"));
2372                        }
2373
2374                        v_flex()
2375                            .id(("path header container", block_id))
2376                            .size_full()
2377                            .justify_center()
2378                            .p(gpui::px(6.))
2379                            .child(
2380                                h_flex()
2381                                    .id("path header block")
2382                                    .size_full()
2383                                    .pl(gpui::px(12.))
2384                                    .pr(gpui::px(8.))
2385                                    .rounded_md()
2386                                    .shadow_md()
2387                                    .border()
2388                                    .border_color(cx.theme().colors().border)
2389                                    .bg(cx.theme().colors().editor_subheader_background)
2390                                    .justify_between()
2391                                    .hover(|style| style.bg(cx.theme().colors().element_hover))
2392                                    .child(
2393                                        h_flex().gap_3().child(
2394                                            h_flex()
2395                                                .gap_2()
2396                                                .child(
2397                                                    filename
2398                                                        .map(SharedString::from)
2399                                                        .unwrap_or_else(|| "untitled".into()),
2400                                                )
2401                                                .when_some(parent_path, |then, path| {
2402                                                    then.child(
2403                                                        div().child(path).text_color(
2404                                                            cx.theme().colors().text_muted,
2405                                                        ),
2406                                                    )
2407                                                }),
2408                                        ),
2409                                    )
2410                                    .when_some(jump_handler, |this, jump_handler| {
2411                                        this.cursor_pointer()
2412                                            .tooltip(|cx| {
2413                                                Tooltip::for_action(
2414                                                    "Jump to Buffer",
2415                                                    &OpenExcerpts,
2416                                                    cx,
2417                                                )
2418                                            })
2419                                            .on_mouse_down(MouseButton::Left, |_, cx| {
2420                                                cx.stop_propagation()
2421                                            })
2422                                            .on_click(jump_handler)
2423                                    }),
2424                            )
2425                    } else {
2426                        h_flex()
2427                            .id(("collapsed context", block_id))
2428                            .size_full()
2429                            .gap(gutter_padding)
2430                            .child(
2431                                h_flex()
2432                                    .justify_end()
2433                                    .flex_none()
2434                                    .w(gutter_width - gutter_padding)
2435                                    .h_full()
2436                                    .text_buffer(cx)
2437                                    .text_color(cx.theme().colors().editor_line_number)
2438                                    .child("..."),
2439                            )
2440                            .child(
2441                                ButtonLike::new("jump to collapsed context")
2442                                    .style(ButtonStyle::Transparent)
2443                                    .full_width()
2444                                    .child(
2445                                        div()
2446                                            .h_px()
2447                                            .w_full()
2448                                            .bg(cx.theme().colors().border_variant)
2449                                            .group_hover("", |style| {
2450                                                style.bg(cx.theme().colors().border)
2451                                            }),
2452                                    )
2453                                    .when_some(jump_handler, |this, jump_handler| {
2454                                        this.on_click(jump_handler).tooltip(|cx| {
2455                                            Tooltip::for_action("Jump to Buffer", &OpenExcerpts, cx)
2456                                        })
2457                                    }),
2458                            )
2459                    };
2460                    element.into_any()
2461                }
2462            };
2463
2464            let size = element.measure(available_space, cx);
2465            (element, size)
2466        };
2467
2468        let mut fixed_block_max_width = Pixels::ZERO;
2469        let mut blocks = Vec::new();
2470        for (row, block) in fixed_blocks {
2471            let available_space = size(
2472                AvailableSpace::MinContent,
2473                AvailableSpace::Definite(block.height() as f32 * line_height),
2474            );
2475            let (element, element_size) =
2476                render_block(block, available_space, block_id, editor, cx);
2477            block_id += 1;
2478            fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
2479            blocks.push(BlockLayout {
2480                row,
2481                element,
2482                available_space,
2483                style: BlockStyle::Fixed,
2484            });
2485        }
2486        for (row, block) in non_fixed_blocks {
2487            let style = match block {
2488                TransformBlock::Custom(block) => block.style(),
2489                TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
2490            };
2491            let width = match style {
2492                BlockStyle::Sticky => editor_width,
2493                BlockStyle::Flex => editor_width
2494                    .max(fixed_block_max_width)
2495                    .max(gutter_width + scroll_width),
2496                BlockStyle::Fixed => unreachable!(),
2497            };
2498            let available_space = size(
2499                AvailableSpace::Definite(width),
2500                AvailableSpace::Definite(block.height() as f32 * line_height),
2501            );
2502            let (element, _) = render_block(block, available_space, block_id, editor, cx);
2503            block_id += 1;
2504            blocks.push(BlockLayout {
2505                row,
2506                element,
2507                available_space,
2508                style,
2509            });
2510        }
2511        (
2512            scroll_width.max(fixed_block_max_width - gutter_width),
2513            blocks,
2514        )
2515    }
2516
2517    fn paint_scroll_wheel_listener(
2518        &mut self,
2519        interactive_bounds: &InteractiveBounds,
2520        layout: &LayoutState,
2521        cx: &mut ElementContext,
2522    ) {
2523        cx.on_mouse_event({
2524            let position_map = layout.position_map.clone();
2525            let editor = self.editor.clone();
2526            let interactive_bounds = interactive_bounds.clone();
2527            let mut delta = ScrollDelta::default();
2528
2529            move |event: &ScrollWheelEvent, phase, cx| {
2530                if phase == DispatchPhase::Bubble
2531                    && interactive_bounds.visibly_contains(&event.position, cx)
2532                {
2533                    delta = delta.coalesce(event.delta);
2534                    editor.update(cx, |editor, cx| {
2535                        let position = event.position;
2536                        let position_map: &PositionMap = &position_map;
2537                        let bounds = &interactive_bounds;
2538                        if !bounds.visibly_contains(&position, cx) {
2539                            return;
2540                        }
2541
2542                        let line_height = position_map.line_height;
2543                        let max_glyph_width = position_map.em_width;
2544                        let (delta, axis) = match delta {
2545                            gpui::ScrollDelta::Pixels(mut pixels) => {
2546                                //Trackpad
2547                                let axis = position_map.snapshot.ongoing_scroll.filter(&mut pixels);
2548                                (pixels, axis)
2549                            }
2550
2551                            gpui::ScrollDelta::Lines(lines) => {
2552                                //Not trackpad
2553                                let pixels =
2554                                    point(lines.x * max_glyph_width, lines.y * line_height);
2555                                (pixels, None)
2556                            }
2557                        };
2558
2559                        let scroll_position = position_map.snapshot.scroll_position();
2560                        let x = f32::from(
2561                            (scroll_position.x * max_glyph_width - delta.x) / max_glyph_width,
2562                        );
2563                        let y =
2564                            f32::from((scroll_position.y * line_height - delta.y) / line_height);
2565                        let scroll_position =
2566                            point(x, y).clamp(&point(0., 0.), &position_map.scroll_max);
2567                        editor.scroll(scroll_position, axis, cx);
2568                        cx.stop_propagation();
2569                    });
2570                }
2571            }
2572        });
2573    }
2574
2575    fn paint_mouse_listeners(
2576        &mut self,
2577        bounds: Bounds<Pixels>,
2578        gutter_bounds: Bounds<Pixels>,
2579        text_bounds: Bounds<Pixels>,
2580        layout: &LayoutState,
2581        cx: &mut ElementContext,
2582    ) {
2583        let interactive_bounds = InteractiveBounds {
2584            bounds: bounds.intersect(&cx.content_mask().bounds),
2585            stacking_order: cx.stacking_order().clone(),
2586        };
2587
2588        self.paint_scroll_wheel_listener(&interactive_bounds, layout, cx);
2589
2590        cx.on_mouse_event({
2591            let position_map = layout.position_map.clone();
2592            let editor = self.editor.clone();
2593            let stacking_order = cx.stacking_order().clone();
2594            let interactive_bounds = interactive_bounds.clone();
2595
2596            move |event: &MouseDownEvent, phase, cx| {
2597                if phase == DispatchPhase::Bubble
2598                    && interactive_bounds.visibly_contains(&event.position, cx)
2599                {
2600                    match event.button {
2601                        MouseButton::Left => editor.update(cx, |editor, cx| {
2602                            Self::mouse_left_down(
2603                                editor,
2604                                event,
2605                                &position_map,
2606                                text_bounds,
2607                                gutter_bounds,
2608                                &stacking_order,
2609                                cx,
2610                            );
2611                        }),
2612                        MouseButton::Right => editor.update(cx, |editor, cx| {
2613                            Self::mouse_right_down(editor, event, &position_map, text_bounds, cx);
2614                        }),
2615                        _ => {}
2616                    };
2617                }
2618            }
2619        });
2620
2621        cx.on_mouse_event({
2622            let position_map = layout.position_map.clone();
2623            let editor = self.editor.clone();
2624            let stacking_order = cx.stacking_order().clone();
2625            let interactive_bounds = interactive_bounds.clone();
2626
2627            move |event: &MouseUpEvent, phase, cx| {
2628                if phase == DispatchPhase::Bubble {
2629                    editor.update(cx, |editor, cx| {
2630                        Self::mouse_up(
2631                            editor,
2632                            event,
2633                            &position_map,
2634                            text_bounds,
2635                            &interactive_bounds,
2636                            &stacking_order,
2637                            cx,
2638                        )
2639                    });
2640                }
2641            }
2642        });
2643        cx.on_mouse_event({
2644            let position_map = layout.position_map.clone();
2645            let editor = self.editor.clone();
2646            let stacking_order = cx.stacking_order().clone();
2647
2648            move |event: &MouseMoveEvent, phase, cx| {
2649                // if editor.has_pending_selection() && event.pressed_button == Some(MouseButton::Left) {
2650
2651                if phase == DispatchPhase::Bubble {
2652                    editor.update(cx, |editor, cx| {
2653                        if event.pressed_button == Some(MouseButton::Left) {
2654                            Self::mouse_dragged(
2655                                editor,
2656                                event,
2657                                &position_map,
2658                                text_bounds,
2659                                gutter_bounds,
2660                                &stacking_order,
2661                                cx,
2662                            )
2663                        }
2664
2665                        if interactive_bounds.visibly_contains(&event.position, cx) {
2666                            Self::mouse_moved(
2667                                editor,
2668                                event,
2669                                &position_map,
2670                                text_bounds,
2671                                gutter_bounds,
2672                                &stacking_order,
2673                                cx,
2674                            )
2675                        }
2676                    });
2677                }
2678            }
2679        });
2680    }
2681}
2682
2683#[derive(Debug)]
2684pub(crate) struct LineWithInvisibles {
2685    pub line: ShapedLine,
2686    invisibles: Vec<Invisible>,
2687}
2688
2689impl LineWithInvisibles {
2690    fn from_chunks<'a>(
2691        chunks: impl Iterator<Item = HighlightedChunk<'a>>,
2692        text_style: &TextStyle,
2693        max_line_len: usize,
2694        max_line_count: usize,
2695        line_number_layouts: &[Option<ShapedLine>],
2696        editor_mode: EditorMode,
2697        cx: &WindowContext,
2698    ) -> Vec<Self> {
2699        let mut layouts = Vec::with_capacity(max_line_count);
2700        let mut line = String::new();
2701        let mut invisibles = Vec::new();
2702        let mut styles = Vec::new();
2703        let mut non_whitespace_added = false;
2704        let mut row = 0;
2705        let mut line_exceeded_max_len = false;
2706        let font_size = text_style.font_size.to_pixels(cx.rem_size());
2707
2708        for highlighted_chunk in chunks.chain([HighlightedChunk {
2709            chunk: "\n",
2710            style: None,
2711            is_tab: false,
2712        }]) {
2713            for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
2714                if ix > 0 {
2715                    let shaped_line = cx
2716                        .text_system()
2717                        .shape_line(line.clone().into(), font_size, &styles)
2718                        .unwrap();
2719                    layouts.push(Self {
2720                        line: shaped_line,
2721                        invisibles: invisibles.drain(..).collect(),
2722                    });
2723
2724                    line.clear();
2725                    styles.clear();
2726                    row += 1;
2727                    line_exceeded_max_len = false;
2728                    non_whitespace_added = false;
2729                    if row == max_line_count {
2730                        return layouts;
2731                    }
2732                }
2733
2734                if !line_chunk.is_empty() && !line_exceeded_max_len {
2735                    let text_style = if let Some(style) = highlighted_chunk.style {
2736                        Cow::Owned(text_style.clone().highlight(style))
2737                    } else {
2738                        Cow::Borrowed(text_style)
2739                    };
2740
2741                    if line.len() + line_chunk.len() > max_line_len {
2742                        let mut chunk_len = max_line_len - line.len();
2743                        while !line_chunk.is_char_boundary(chunk_len) {
2744                            chunk_len -= 1;
2745                        }
2746                        line_chunk = &line_chunk[..chunk_len];
2747                        line_exceeded_max_len = true;
2748                    }
2749
2750                    styles.push(TextRun {
2751                        len: line_chunk.len(),
2752                        font: text_style.font(),
2753                        color: text_style.color,
2754                        background_color: text_style.background_color,
2755                        underline: text_style.underline,
2756                    });
2757
2758                    if editor_mode == EditorMode::Full {
2759                        // Line wrap pads its contents with fake whitespaces,
2760                        // avoid printing them
2761                        let inside_wrapped_string = line_number_layouts
2762                            .get(row)
2763                            .and_then(|layout| layout.as_ref())
2764                            .is_none();
2765                        if highlighted_chunk.is_tab {
2766                            if non_whitespace_added || !inside_wrapped_string {
2767                                invisibles.push(Invisible::Tab {
2768                                    line_start_offset: line.len(),
2769                                });
2770                            }
2771                        } else {
2772                            invisibles.extend(
2773                                line_chunk
2774                                    .chars()
2775                                    .enumerate()
2776                                    .filter(|(_, line_char)| {
2777                                        let is_whitespace = line_char.is_whitespace();
2778                                        non_whitespace_added |= !is_whitespace;
2779                                        is_whitespace
2780                                            && (non_whitespace_added || !inside_wrapped_string)
2781                                    })
2782                                    .map(|(whitespace_index, _)| Invisible::Whitespace {
2783                                        line_offset: line.len() + whitespace_index,
2784                                    }),
2785                            )
2786                        }
2787                    }
2788
2789                    line.push_str(line_chunk);
2790                }
2791            }
2792        }
2793
2794        layouts
2795    }
2796
2797    fn draw(
2798        &self,
2799        layout: &LayoutState,
2800        row: u32,
2801        content_origin: gpui::Point<Pixels>,
2802        whitespace_setting: ShowWhitespaceSetting,
2803        selection_ranges: &[Range<DisplayPoint>],
2804        cx: &mut ElementContext,
2805    ) {
2806        let line_height = layout.position_map.line_height;
2807        let line_y = line_height * row as f32 - layout.position_map.scroll_position.y;
2808
2809        self.line
2810            .paint(
2811                content_origin + gpui::point(-layout.position_map.scroll_position.x, line_y),
2812                line_height,
2813                cx,
2814            )
2815            .log_err();
2816
2817        self.draw_invisibles(
2818            &selection_ranges,
2819            layout,
2820            content_origin,
2821            line_y,
2822            row,
2823            line_height,
2824            whitespace_setting,
2825            cx,
2826        );
2827    }
2828
2829    fn draw_invisibles(
2830        &self,
2831        selection_ranges: &[Range<DisplayPoint>],
2832        layout: &LayoutState,
2833        content_origin: gpui::Point<Pixels>,
2834        line_y: Pixels,
2835        row: u32,
2836        line_height: Pixels,
2837        whitespace_setting: ShowWhitespaceSetting,
2838        cx: &mut ElementContext,
2839    ) {
2840        let allowed_invisibles_regions = match whitespace_setting {
2841            ShowWhitespaceSetting::None => return,
2842            ShowWhitespaceSetting::Selection => Some(selection_ranges),
2843            ShowWhitespaceSetting::All => None,
2844        };
2845
2846        for invisible in &self.invisibles {
2847            let (&token_offset, invisible_symbol) = match invisible {
2848                Invisible::Tab { line_start_offset } => (line_start_offset, &layout.tab_invisible),
2849                Invisible::Whitespace { line_offset } => (line_offset, &layout.space_invisible),
2850            };
2851
2852            let x_offset = self.line.x_for_index(token_offset);
2853            let invisible_offset =
2854                (layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0;
2855            let origin = content_origin
2856                + gpui::point(
2857                    x_offset + invisible_offset - layout.position_map.scroll_position.x,
2858                    line_y,
2859                );
2860
2861            if let Some(allowed_regions) = allowed_invisibles_regions {
2862                let invisible_point = DisplayPoint::new(row, token_offset as u32);
2863                if !allowed_regions
2864                    .iter()
2865                    .any(|region| region.start <= invisible_point && invisible_point < region.end)
2866                {
2867                    continue;
2868                }
2869            }
2870            invisible_symbol.paint(origin, line_height, cx).log_err();
2871        }
2872    }
2873}
2874
2875#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2876enum Invisible {
2877    Tab { line_start_offset: usize },
2878    Whitespace { line_offset: usize },
2879}
2880
2881impl Element for EditorElement {
2882    type State = ();
2883
2884    fn request_layout(
2885        &mut self,
2886        _element_state: Option<Self::State>,
2887        cx: &mut gpui::ElementContext,
2888    ) -> (gpui::LayoutId, Self::State) {
2889        cx.with_view_id(self.editor.entity_id(), |cx| {
2890            self.editor.update(cx, |editor, cx| {
2891                editor.set_style(self.style.clone(), cx);
2892
2893                let layout_id = match editor.mode {
2894                    EditorMode::SingleLine => {
2895                        let rem_size = cx.rem_size();
2896                        let mut style = Style::default();
2897                        style.size.width = relative(1.).into();
2898                        style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
2899                        cx.with_element_context(|cx| cx.request_layout(&style, None))
2900                    }
2901                    EditorMode::AutoHeight { max_lines } => {
2902                        let editor_handle = cx.view().clone();
2903                        let max_line_number_width =
2904                            self.max_line_number_width(&editor.snapshot(cx), cx);
2905                        cx.with_element_context(|cx| {
2906                            cx.request_measured_layout(
2907                                Style::default(),
2908                                move |known_dimensions, _, cx| {
2909                                    editor_handle
2910                                        .update(cx, |editor, cx| {
2911                                            compute_auto_height_layout(
2912                                                editor,
2913                                                max_lines,
2914                                                max_line_number_width,
2915                                                known_dimensions,
2916                                                cx,
2917                                            )
2918                                        })
2919                                        .unwrap_or_default()
2920                                },
2921                            )
2922                        })
2923                    }
2924                    EditorMode::Full => {
2925                        let mut style = Style::default();
2926                        style.size.width = relative(1.).into();
2927                        style.size.height = relative(1.).into();
2928                        cx.with_element_context(|cx| cx.request_layout(&style, None))
2929                    }
2930                };
2931
2932                (layout_id, ())
2933            })
2934        })
2935    }
2936
2937    fn paint(
2938        &mut self,
2939        bounds: Bounds<gpui::Pixels>,
2940        _element_state: &mut Self::State,
2941        cx: &mut gpui::ElementContext,
2942    ) {
2943        let editor = self.editor.clone();
2944
2945        cx.paint_view(self.editor.entity_id(), |cx| {
2946            cx.with_text_style(
2947                Some(gpui::TextStyleRefinement {
2948                    font_size: Some(self.style.text.font_size),
2949                    line_height: Some(self.style.text.line_height),
2950                    ..Default::default()
2951                }),
2952                |cx| {
2953                    let mut layout = self.compute_layout(bounds, cx);
2954                    let gutter_bounds = Bounds {
2955                        origin: bounds.origin,
2956                        size: layout.gutter_size,
2957                    };
2958                    let text_bounds = Bounds {
2959                        origin: gutter_bounds.upper_right(),
2960                        size: layout.text_size,
2961                    };
2962
2963                    let focus_handle = editor.focus_handle(cx);
2964                    let key_context = self.editor.read(cx).key_context(cx);
2965                    cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
2966                        self.register_actions(cx);
2967                        self.register_key_listeners(cx);
2968
2969                        cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
2970                            cx.handle_input(
2971                                &focus_handle,
2972                                ElementInputHandler::new(bounds, self.editor.clone()),
2973                            );
2974
2975                            self.paint_background(gutter_bounds, text_bounds, &layout, cx);
2976                            if layout.gutter_size.width > Pixels::ZERO {
2977                                self.paint_gutter(gutter_bounds, &mut layout, cx);
2978                            }
2979                            self.paint_text(text_bounds, &mut layout, cx);
2980
2981                            cx.with_z_index(0, |cx| {
2982                                self.paint_mouse_listeners(
2983                                    bounds,
2984                                    gutter_bounds,
2985                                    text_bounds,
2986                                    &layout,
2987                                    cx,
2988                                );
2989                            });
2990                            if !layout.blocks.is_empty() {
2991                                cx.with_z_index(0, |cx| {
2992                                    cx.with_element_id(Some("editor_blocks"), |cx| {
2993                                        self.paint_blocks(bounds, &mut layout, cx);
2994                                    });
2995                                })
2996                            }
2997
2998                            cx.with_z_index(1, |cx| {
2999                                self.paint_overlays(text_bounds, &mut layout, cx);
3000                            });
3001
3002                            cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
3003                        });
3004                    })
3005                },
3006            )
3007        })
3008    }
3009}
3010
3011impl IntoElement for EditorElement {
3012    type Element = Self;
3013
3014    fn element_id(&self) -> Option<gpui::ElementId> {
3015        self.editor.element_id()
3016    }
3017
3018    fn into_element(self) -> Self::Element {
3019        self
3020    }
3021}
3022
3023type BufferRow = u32;
3024
3025pub struct LayoutState {
3026    position_map: Arc<PositionMap>,
3027    gutter_size: Size<Pixels>,
3028    gutter_padding: Pixels,
3029    gutter_margin: Pixels,
3030    text_size: gpui::Size<Pixels>,
3031    mode: EditorMode,
3032    wrap_guides: SmallVec<[(Pixels, bool); 2]>,
3033    visible_anchor_range: Range<Anchor>,
3034    visible_display_row_range: Range<u32>,
3035    active_rows: BTreeMap<u32, bool>,
3036    highlighted_rows: Option<Range<u32>>,
3037    line_numbers: Vec<Option<ShapedLine>>,
3038    display_hunks: Vec<DisplayDiffHunk>,
3039    blocks: Vec<BlockLayout>,
3040    highlighted_ranges: Vec<(Range<DisplayPoint>, Hsla)>,
3041    selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
3042    scrollbar_row_range: Range<f32>,
3043    show_scrollbars: bool,
3044    is_singleton: bool,
3045    max_row: u32,
3046    context_menu: Option<(DisplayPoint, AnyElement)>,
3047    code_actions_indicator: Option<CodeActionsIndicator>,
3048    hover_popovers: Option<(DisplayPoint, Vec<AnyElement>)>,
3049    fold_indicators: Vec<Option<IconButton>>,
3050    tab_invisible: ShapedLine,
3051    space_invisible: ShapedLine,
3052}
3053
3054struct CodeActionsIndicator {
3055    row: u32,
3056    button: IconButton,
3057}
3058
3059struct PositionMap {
3060    size: Size<Pixels>,
3061    line_height: Pixels,
3062    scroll_position: gpui::Point<Pixels>,
3063    scroll_max: gpui::Point<f32>,
3064    em_width: Pixels,
3065    em_advance: Pixels,
3066    line_layouts: Vec<LineWithInvisibles>,
3067    snapshot: EditorSnapshot,
3068}
3069
3070#[derive(Debug, Copy, Clone)]
3071pub struct PointForPosition {
3072    pub previous_valid: DisplayPoint,
3073    pub next_valid: DisplayPoint,
3074    pub exact_unclipped: DisplayPoint,
3075    pub column_overshoot_after_line_end: u32,
3076}
3077
3078impl PointForPosition {
3079    #[cfg(test)]
3080    pub fn valid(valid: DisplayPoint) -> Self {
3081        Self {
3082            previous_valid: valid,
3083            next_valid: valid,
3084            exact_unclipped: valid,
3085            column_overshoot_after_line_end: 0,
3086        }
3087    }
3088
3089    pub fn as_valid(&self) -> Option<DisplayPoint> {
3090        if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
3091            Some(self.previous_valid)
3092        } else {
3093            None
3094        }
3095    }
3096}
3097
3098impl PositionMap {
3099    fn point_for_position(
3100        &self,
3101        text_bounds: Bounds<Pixels>,
3102        position: gpui::Point<Pixels>,
3103    ) -> PointForPosition {
3104        let scroll_position = self.snapshot.scroll_position();
3105        let position = position - text_bounds.origin;
3106        let y = position.y.max(px(0.)).min(self.size.height);
3107        let x = position.x + (scroll_position.x * self.em_width);
3108        let row = (f32::from(y / self.line_height) + scroll_position.y) as u32;
3109
3110        let (column, x_overshoot_after_line_end) = if let Some(line) = self
3111            .line_layouts
3112            .get(row as usize - scroll_position.y as usize)
3113            .map(|&LineWithInvisibles { ref line, .. }| line)
3114        {
3115            if let Some(ix) = line.index_for_x(x) {
3116                (ix as u32, px(0.))
3117            } else {
3118                (line.len as u32, px(0.).max(x - line.width))
3119            }
3120        } else {
3121            (0, x)
3122        };
3123
3124        let mut exact_unclipped = DisplayPoint::new(row, column);
3125        let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
3126        let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
3127
3128        let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
3129        *exact_unclipped.column_mut() += column_overshoot_after_line_end;
3130        PointForPosition {
3131            previous_valid,
3132            next_valid,
3133            exact_unclipped,
3134            column_overshoot_after_line_end,
3135        }
3136    }
3137}
3138
3139struct BlockLayout {
3140    row: u32,
3141    element: AnyElement,
3142    available_space: Size<AvailableSpace>,
3143    style: BlockStyle,
3144}
3145
3146fn layout_line(
3147    row: u32,
3148    snapshot: &EditorSnapshot,
3149    style: &EditorStyle,
3150    cx: &WindowContext,
3151) -> Result<ShapedLine> {
3152    let mut line = snapshot.line(row);
3153
3154    if line.len() > MAX_LINE_LEN {
3155        let mut len = MAX_LINE_LEN;
3156        while !line.is_char_boundary(len) {
3157            len -= 1;
3158        }
3159
3160        line.truncate(len);
3161    }
3162
3163    cx.text_system().shape_line(
3164        line.into(),
3165        style.text.font_size.to_pixels(cx.rem_size()),
3166        &[TextRun {
3167            len: snapshot.line_len(row) as usize,
3168            font: style.text.font(),
3169            color: Hsla::default(),
3170            background_color: None,
3171            underline: None,
3172        }],
3173    )
3174}
3175
3176#[derive(Debug)]
3177pub struct Cursor {
3178    origin: gpui::Point<Pixels>,
3179    block_width: Pixels,
3180    line_height: Pixels,
3181    color: Hsla,
3182    shape: CursorShape,
3183    block_text: Option<ShapedLine>,
3184    cursor_name: Option<CursorName>,
3185}
3186
3187#[derive(Debug)]
3188pub struct CursorName {
3189    string: SharedString,
3190    color: Hsla,
3191    is_top_row: bool,
3192    z_index: u16,
3193}
3194
3195impl Cursor {
3196    pub fn new(
3197        origin: gpui::Point<Pixels>,
3198        block_width: Pixels,
3199        line_height: Pixels,
3200        color: Hsla,
3201        shape: CursorShape,
3202        block_text: Option<ShapedLine>,
3203        cursor_name: Option<CursorName>,
3204    ) -> Cursor {
3205        Cursor {
3206            origin,
3207            block_width,
3208            line_height,
3209            color,
3210            shape,
3211            block_text,
3212            cursor_name,
3213        }
3214    }
3215
3216    pub fn bounding_rect(&self, origin: gpui::Point<Pixels>) -> Bounds<Pixels> {
3217        Bounds {
3218            origin: self.origin + origin,
3219            size: size(self.block_width, self.line_height),
3220        }
3221    }
3222
3223    pub fn paint(&self, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
3224        let bounds = match self.shape {
3225            CursorShape::Bar => Bounds {
3226                origin: self.origin + origin,
3227                size: size(px(2.0), self.line_height),
3228            },
3229            CursorShape::Block | CursorShape::Hollow => Bounds {
3230                origin: self.origin + origin,
3231                size: size(self.block_width, self.line_height),
3232            },
3233            CursorShape::Underscore => Bounds {
3234                origin: self.origin
3235                    + origin
3236                    + gpui::Point::new(Pixels::ZERO, self.line_height - px(2.0)),
3237                size: size(self.block_width, px(2.0)),
3238            },
3239        };
3240
3241        //Draw background or border quad
3242        let cursor = if matches!(self.shape, CursorShape::Hollow) {
3243            outline(bounds, self.color)
3244        } else {
3245            fill(bounds, self.color)
3246        };
3247
3248        if let Some(name) = &self.cursor_name {
3249            let text_size = self.line_height / 1.5;
3250
3251            let name_origin = if name.is_top_row {
3252                point(bounds.right() - px(1.), bounds.top())
3253            } else {
3254                point(bounds.left(), bounds.top() - text_size / 2. - px(1.))
3255            };
3256            cx.with_z_index(name.z_index, |cx| {
3257                div()
3258                    .bg(self.color)
3259                    .text_size(text_size)
3260                    .px_0p5()
3261                    .line_height(text_size + px(2.))
3262                    .text_color(name.color)
3263                    .child(name.string.clone())
3264                    .into_any_element()
3265                    .draw(
3266                        name_origin,
3267                        size(AvailableSpace::MinContent, AvailableSpace::MinContent),
3268                        cx,
3269                    )
3270            })
3271        }
3272
3273        cx.paint_quad(cursor);
3274
3275        if let Some(block_text) = &self.block_text {
3276            block_text
3277                .paint(self.origin + origin, self.line_height, cx)
3278                .log_err();
3279        }
3280    }
3281
3282    pub fn shape(&self) -> CursorShape {
3283        self.shape
3284    }
3285}
3286
3287#[derive(Debug)]
3288pub struct HighlightedRange {
3289    pub start_y: Pixels,
3290    pub line_height: Pixels,
3291    pub lines: Vec<HighlightedRangeLine>,
3292    pub color: Hsla,
3293    pub corner_radius: Pixels,
3294}
3295
3296#[derive(Debug)]
3297pub struct HighlightedRangeLine {
3298    pub start_x: Pixels,
3299    pub end_x: Pixels,
3300}
3301
3302impl HighlightedRange {
3303    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ElementContext) {
3304        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
3305            self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx);
3306            self.paint_lines(
3307                self.start_y + self.line_height,
3308                &self.lines[1..],
3309                bounds,
3310                cx,
3311            );
3312        } else {
3313            self.paint_lines(self.start_y, &self.lines, bounds, cx);
3314        }
3315    }
3316
3317    fn paint_lines(
3318        &self,
3319        start_y: Pixels,
3320        lines: &[HighlightedRangeLine],
3321        _bounds: Bounds<Pixels>,
3322        cx: &mut ElementContext,
3323    ) {
3324        if lines.is_empty() {
3325            return;
3326        }
3327
3328        let first_line = lines.first().unwrap();
3329        let last_line = lines.last().unwrap();
3330
3331        let first_top_left = point(first_line.start_x, start_y);
3332        let first_top_right = point(first_line.end_x, start_y);
3333
3334        let curve_height = point(Pixels::ZERO, self.corner_radius);
3335        let curve_width = |start_x: Pixels, end_x: Pixels| {
3336            let max = (end_x - start_x) / 2.;
3337            let width = if max < self.corner_radius {
3338                max
3339            } else {
3340                self.corner_radius
3341            };
3342
3343            point(width, Pixels::ZERO)
3344        };
3345
3346        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
3347        let mut path = gpui::Path::new(first_top_right - top_curve_width);
3348        path.curve_to(first_top_right + curve_height, first_top_right);
3349
3350        let mut iter = lines.iter().enumerate().peekable();
3351        while let Some((ix, line)) = iter.next() {
3352            let bottom_right = point(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
3353
3354            if let Some((_, next_line)) = iter.peek() {
3355                let next_top_right = point(next_line.end_x, bottom_right.y);
3356
3357                match next_top_right.x.partial_cmp(&bottom_right.x).unwrap() {
3358                    Ordering::Equal => {
3359                        path.line_to(bottom_right);
3360                    }
3361                    Ordering::Less => {
3362                        let curve_width = curve_width(next_top_right.x, bottom_right.x);
3363                        path.line_to(bottom_right - curve_height);
3364                        if self.corner_radius > Pixels::ZERO {
3365                            path.curve_to(bottom_right - curve_width, bottom_right);
3366                        }
3367                        path.line_to(next_top_right + curve_width);
3368                        if self.corner_radius > Pixels::ZERO {
3369                            path.curve_to(next_top_right + curve_height, next_top_right);
3370                        }
3371                    }
3372                    Ordering::Greater => {
3373                        let curve_width = curve_width(bottom_right.x, next_top_right.x);
3374                        path.line_to(bottom_right - curve_height);
3375                        if self.corner_radius > Pixels::ZERO {
3376                            path.curve_to(bottom_right + curve_width, bottom_right);
3377                        }
3378                        path.line_to(next_top_right - curve_width);
3379                        if self.corner_radius > Pixels::ZERO {
3380                            path.curve_to(next_top_right + curve_height, next_top_right);
3381                        }
3382                    }
3383                }
3384            } else {
3385                let curve_width = curve_width(line.start_x, line.end_x);
3386                path.line_to(bottom_right - curve_height);
3387                if self.corner_radius > Pixels::ZERO {
3388                    path.curve_to(bottom_right - curve_width, bottom_right);
3389                }
3390
3391                let bottom_left = point(line.start_x, bottom_right.y);
3392                path.line_to(bottom_left + curve_width);
3393                if self.corner_radius > Pixels::ZERO {
3394                    path.curve_to(bottom_left - curve_height, bottom_left);
3395                }
3396            }
3397        }
3398
3399        if first_line.start_x > last_line.start_x {
3400            let curve_width = curve_width(last_line.start_x, first_line.start_x);
3401            let second_top_left = point(last_line.start_x, start_y + self.line_height);
3402            path.line_to(second_top_left + curve_height);
3403            if self.corner_radius > Pixels::ZERO {
3404                path.curve_to(second_top_left + curve_width, second_top_left);
3405            }
3406            let first_bottom_left = point(first_line.start_x, second_top_left.y);
3407            path.line_to(first_bottom_left - curve_width);
3408            if self.corner_radius > Pixels::ZERO {
3409                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
3410            }
3411        }
3412
3413        path.line_to(first_top_left + curve_height);
3414        if self.corner_radius > Pixels::ZERO {
3415            path.curve_to(first_top_left + top_curve_width, first_top_left);
3416        }
3417        path.line_to(first_top_right - top_curve_width);
3418
3419        cx.paint_path(path, self.color);
3420    }
3421}
3422
3423pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 {
3424    (delta.pow(1.5) / 100.0).into()
3425}
3426
3427fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
3428    (delta.pow(1.2) / 300.0).into()
3429}
3430
3431#[cfg(test)]
3432mod tests {
3433    use super::*;
3434    use crate::{
3435        display_map::{BlockDisposition, BlockProperties},
3436        editor_tests::{init_test, update_test_language_settings},
3437        Editor, MultiBuffer,
3438    };
3439    use gpui::TestAppContext;
3440    use language::language_settings;
3441    use log::info;
3442    use std::{num::NonZeroU32, sync::Arc};
3443    use util::test::sample_text;
3444
3445    #[gpui::test]
3446    fn test_shape_line_numbers(cx: &mut TestAppContext) {
3447        init_test(cx, |_| {});
3448        let window = cx.add_window(|cx| {
3449            let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
3450            Editor::new(EditorMode::Full, buffer, None, cx)
3451        });
3452
3453        let editor = window.root(cx).unwrap();
3454        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3455        let element = EditorElement::new(&editor, style);
3456
3457        let layouts = window
3458            .update(cx, |editor, cx| {
3459                let snapshot = editor.snapshot(cx);
3460                element
3461                    .shape_line_numbers(
3462                        0..6,
3463                        &Default::default(),
3464                        DisplayPoint::new(0, 0),
3465                        false,
3466                        &snapshot,
3467                        cx,
3468                    )
3469                    .0
3470            })
3471            .unwrap();
3472        assert_eq!(layouts.len(), 6);
3473
3474        let relative_rows = window
3475            .update(cx, |editor, cx| {
3476                let snapshot = editor.snapshot(cx);
3477                element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3))
3478            })
3479            .unwrap();
3480        assert_eq!(relative_rows[&0], 3);
3481        assert_eq!(relative_rows[&1], 2);
3482        assert_eq!(relative_rows[&2], 1);
3483        // current line has no relative number
3484        assert_eq!(relative_rows[&4], 1);
3485        assert_eq!(relative_rows[&5], 2);
3486
3487        // works if cursor is before screen
3488        let relative_rows = window
3489            .update(cx, |editor, cx| {
3490                let snapshot = editor.snapshot(cx);
3491
3492                element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1))
3493            })
3494            .unwrap();
3495        assert_eq!(relative_rows.len(), 3);
3496        assert_eq!(relative_rows[&3], 2);
3497        assert_eq!(relative_rows[&4], 3);
3498        assert_eq!(relative_rows[&5], 4);
3499
3500        // works if cursor is after screen
3501        let relative_rows = window
3502            .update(cx, |editor, cx| {
3503                let snapshot = editor.snapshot(cx);
3504
3505                element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6))
3506            })
3507            .unwrap();
3508        assert_eq!(relative_rows.len(), 3);
3509        assert_eq!(relative_rows[&0], 5);
3510        assert_eq!(relative_rows[&1], 4);
3511        assert_eq!(relative_rows[&2], 3);
3512    }
3513
3514    #[gpui::test]
3515    async fn test_vim_visual_selections(cx: &mut TestAppContext) {
3516        init_test(cx, |_| {});
3517
3518        let window = cx.add_window(|cx| {
3519            let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
3520            Editor::new(EditorMode::Full, buffer, None, cx)
3521        });
3522        let editor = window.root(cx).unwrap();
3523        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3524        let mut element = EditorElement::new(&editor, style);
3525
3526        window
3527            .update(cx, |editor, cx| {
3528                editor.cursor_shape = CursorShape::Block;
3529                editor.change_selections(None, cx, |s| {
3530                    s.select_ranges([
3531                        Point::new(0, 0)..Point::new(1, 0),
3532                        Point::new(3, 2)..Point::new(3, 3),
3533                        Point::new(5, 6)..Point::new(6, 0),
3534                    ]);
3535                });
3536            })
3537            .unwrap();
3538        let state = cx
3539            .update_window(window.into(), |view, cx| {
3540                cx.with_element_context(|cx| {
3541                    cx.with_view_id(view.entity_id(), |cx| {
3542                        element.compute_layout(
3543                            Bounds {
3544                                origin: point(px(500.), px(500.)),
3545                                size: size(px(500.), px(500.)),
3546                            },
3547                            cx,
3548                        )
3549                    })
3550                })
3551            })
3552            .unwrap();
3553
3554        assert_eq!(state.selections.len(), 1);
3555        let local_selections = &state.selections[0].1;
3556        assert_eq!(local_selections.len(), 3);
3557        // moves cursor back one line
3558        assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6));
3559        assert_eq!(
3560            local_selections[0].range,
3561            DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0)
3562        );
3563
3564        // moves cursor back one column
3565        assert_eq!(
3566            local_selections[1].range,
3567            DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3)
3568        );
3569        assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2));
3570
3571        // leaves cursor on the max point
3572        assert_eq!(
3573            local_selections[2].range,
3574            DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0)
3575        );
3576        assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0));
3577
3578        // active lines does not include 1 (even though the range of the selection does)
3579        assert_eq!(
3580            state.active_rows.keys().cloned().collect::<Vec<u32>>(),
3581            vec![0, 3, 5, 6]
3582        );
3583
3584        // multi-buffer support
3585        // in DisplayPoint co-ordinates, this is what we're dealing with:
3586        //  0: [[file
3587        //  1:   header]]
3588        //  2: aaaaaa
3589        //  3: bbbbbb
3590        //  4: cccccc
3591        //  5:
3592        //  6: ...
3593        //  7: ffffff
3594        //  8: gggggg
3595        //  9: hhhhhh
3596        // 10:
3597        // 11: [[file
3598        // 12:   header]]
3599        // 13: bbbbbb
3600        // 14: cccccc
3601        // 15: dddddd
3602        let window = cx.add_window(|cx| {
3603            let buffer = MultiBuffer::build_multi(
3604                [
3605                    (
3606                        &(sample_text(8, 6, 'a') + "\n"),
3607                        vec![
3608                            Point::new(0, 0)..Point::new(3, 0),
3609                            Point::new(4, 0)..Point::new(7, 0),
3610                        ],
3611                    ),
3612                    (
3613                        &(sample_text(8, 6, 'a') + "\n"),
3614                        vec![Point::new(1, 0)..Point::new(3, 0)],
3615                    ),
3616                ],
3617                cx,
3618            );
3619            Editor::new(EditorMode::Full, buffer, None, cx)
3620        });
3621        let editor = window.root(cx).unwrap();
3622        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3623        let mut element = EditorElement::new(&editor, style);
3624        let _state = window.update(cx, |editor, cx| {
3625            editor.cursor_shape = CursorShape::Block;
3626            editor.change_selections(None, cx, |s| {
3627                s.select_display_ranges([
3628                    DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0),
3629                    DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
3630                ]);
3631            });
3632        });
3633
3634        let state = cx
3635            .update_window(window.into(), |view, cx| {
3636                cx.with_element_context(|cx| {
3637                    cx.with_view_id(view.entity_id(), |cx| {
3638                        element.compute_layout(
3639                            Bounds {
3640                                origin: point(px(500.), px(500.)),
3641                                size: size(px(500.), px(500.)),
3642                            },
3643                            cx,
3644                        )
3645                    })
3646                })
3647            })
3648            .unwrap();
3649        assert_eq!(state.selections.len(), 1);
3650        let local_selections = &state.selections[0].1;
3651        assert_eq!(local_selections.len(), 2);
3652
3653        // moves cursor on excerpt boundary back a line
3654        // and doesn't allow selection to bleed through
3655        assert_eq!(
3656            local_selections[0].range,
3657            DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0)
3658        );
3659        assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0));
3660        // moves cursor on buffer boundary back two lines
3661        // and doesn't allow selection to bleed through
3662        assert_eq!(
3663            local_selections[1].range,
3664            DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0)
3665        );
3666        assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0));
3667    }
3668
3669    #[gpui::test]
3670    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
3671        init_test(cx, |_| {});
3672
3673        let window = cx.add_window(|cx| {
3674            let buffer = MultiBuffer::build_simple("", cx);
3675            Editor::new(EditorMode::Full, buffer, None, cx)
3676        });
3677        let editor = window.root(cx).unwrap();
3678        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3679        window
3680            .update(cx, |editor, cx| {
3681                editor.set_placeholder_text("hello", cx);
3682                editor.insert_blocks(
3683                    [BlockProperties {
3684                        style: BlockStyle::Fixed,
3685                        disposition: BlockDisposition::Above,
3686                        height: 3,
3687                        position: Anchor::min(),
3688                        render: Arc::new(|_| div().into_any()),
3689                    }],
3690                    None,
3691                    cx,
3692                );
3693
3694                // Blur the editor so that it displays placeholder text.
3695                cx.blur();
3696            })
3697            .unwrap();
3698
3699        let mut element = EditorElement::new(&editor, style);
3700        let state = cx
3701            .update_window(window.into(), |view, cx| {
3702                cx.with_element_context(|cx| {
3703                    cx.with_view_id(view.entity_id(), |cx| {
3704                        element.compute_layout(
3705                            Bounds {
3706                                origin: point(px(500.), px(500.)),
3707                                size: size(px(500.), px(500.)),
3708                            },
3709                            cx,
3710                        )
3711                    })
3712                })
3713            })
3714            .unwrap();
3715        let size = state.position_map.size;
3716
3717        assert_eq!(state.position_map.line_layouts.len(), 4);
3718        assert_eq!(
3719            state
3720                .line_numbers
3721                .iter()
3722                .map(Option::is_some)
3723                .collect::<Vec<_>>(),
3724            &[false, false, false, true]
3725        );
3726
3727        // Don't panic.
3728        let bounds = Bounds::<Pixels>::new(Default::default(), size);
3729        cx.update_window(window.into(), |_, cx| {
3730            cx.with_element_context(|cx| element.paint(bounds, &mut (), cx))
3731        })
3732        .unwrap()
3733    }
3734
3735    #[gpui::test]
3736    fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
3737        const TAB_SIZE: u32 = 4;
3738
3739        let input_text = "\t \t|\t| a b";
3740        let expected_invisibles = vec![
3741            Invisible::Tab {
3742                line_start_offset: 0,
3743            },
3744            Invisible::Whitespace {
3745                line_offset: TAB_SIZE as usize,
3746            },
3747            Invisible::Tab {
3748                line_start_offset: TAB_SIZE as usize + 1,
3749            },
3750            Invisible::Tab {
3751                line_start_offset: TAB_SIZE as usize * 2 + 1,
3752            },
3753            Invisible::Whitespace {
3754                line_offset: TAB_SIZE as usize * 3 + 1,
3755            },
3756            Invisible::Whitespace {
3757                line_offset: TAB_SIZE as usize * 3 + 3,
3758            },
3759        ];
3760        assert_eq!(
3761            expected_invisibles.len(),
3762            input_text
3763                .chars()
3764                .filter(|initial_char| initial_char.is_whitespace())
3765                .count(),
3766            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3767        );
3768
3769        init_test(cx, |s| {
3770            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3771            s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
3772        });
3773
3774        let actual_invisibles =
3775            collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, px(500.0));
3776
3777        assert_eq!(expected_invisibles, actual_invisibles);
3778    }
3779
3780    #[gpui::test]
3781    fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
3782        init_test(cx, |s| {
3783            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3784            s.defaults.tab_size = NonZeroU32::new(4);
3785        });
3786
3787        for editor_mode_without_invisibles in [
3788            EditorMode::SingleLine,
3789            EditorMode::AutoHeight { max_lines: 100 },
3790        ] {
3791            let invisibles = collect_invisibles_from_new_editor(
3792                cx,
3793                editor_mode_without_invisibles,
3794                "\t\t\t| | a b",
3795                px(500.0),
3796            );
3797            assert!(invisibles.is_empty(),
3798                    "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}");
3799        }
3800    }
3801
3802    #[gpui::test]
3803    fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) {
3804        let tab_size = 4;
3805        let input_text = "a\tbcd   ".repeat(9);
3806        let repeated_invisibles = [
3807            Invisible::Tab {
3808                line_start_offset: 1,
3809            },
3810            Invisible::Whitespace {
3811                line_offset: tab_size as usize + 3,
3812            },
3813            Invisible::Whitespace {
3814                line_offset: tab_size as usize + 4,
3815            },
3816            Invisible::Whitespace {
3817                line_offset: tab_size as usize + 5,
3818            },
3819        ];
3820        let expected_invisibles = std::iter::once(repeated_invisibles)
3821            .cycle()
3822            .take(9)
3823            .flatten()
3824            .collect::<Vec<_>>();
3825        assert_eq!(
3826            expected_invisibles.len(),
3827            input_text
3828                .chars()
3829                .filter(|initial_char| initial_char.is_whitespace())
3830                .count(),
3831            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3832        );
3833        info!("Expected invisibles: {expected_invisibles:?}");
3834
3835        init_test(cx, |_| {});
3836
3837        // Put the same string with repeating whitespace pattern into editors of various size,
3838        // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
3839        let resize_step = 10.0;
3840        let mut editor_width = 200.0;
3841        while editor_width <= 1000.0 {
3842            update_test_language_settings(cx, |s| {
3843                s.defaults.tab_size = NonZeroU32::new(tab_size);
3844                s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3845                s.defaults.preferred_line_length = Some(editor_width as u32);
3846                s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
3847            });
3848
3849            let actual_invisibles = collect_invisibles_from_new_editor(
3850                cx,
3851                EditorMode::Full,
3852                &input_text,
3853                px(editor_width),
3854            );
3855
3856            // Whatever the editor size is, ensure it has the same invisible kinds in the same order
3857            // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets).
3858            let mut i = 0;
3859            for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() {
3860                i = actual_index;
3861                match expected_invisibles.get(i) {
3862                    Some(expected_invisible) => match (expected_invisible, actual_invisible) {
3863                        (Invisible::Whitespace { .. }, Invisible::Whitespace { .. })
3864                        | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {}
3865                        _ => {
3866                            panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}")
3867                        }
3868                    },
3869                    None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"),
3870                }
3871            }
3872            let missing_expected_invisibles = &expected_invisibles[i + 1..];
3873            assert!(
3874                missing_expected_invisibles.is_empty(),
3875                "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}"
3876            );
3877
3878            editor_width += resize_step;
3879        }
3880    }
3881
3882    fn collect_invisibles_from_new_editor(
3883        cx: &mut TestAppContext,
3884        editor_mode: EditorMode,
3885        input_text: &str,
3886        editor_width: Pixels,
3887    ) -> Vec<Invisible> {
3888        info!(
3889            "Creating editor with mode {editor_mode:?}, width {}px and text '{input_text}'",
3890            editor_width.0
3891        );
3892        let window = cx.add_window(|cx| {
3893            let buffer = MultiBuffer::build_simple(&input_text, cx);
3894            Editor::new(editor_mode, buffer, None, cx)
3895        });
3896        let editor = window.root(cx).unwrap();
3897        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3898        let mut element = EditorElement::new(&editor, style);
3899        window
3900            .update(cx, |editor, cx| {
3901                editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
3902                editor.set_wrap_width(Some(editor_width), cx);
3903            })
3904            .unwrap();
3905        let layout_state = cx
3906            .update_window(window.into(), |_, cx| {
3907                cx.with_element_context(|cx| {
3908                    element.compute_layout(
3909                        Bounds {
3910                            origin: point(px(500.), px(500.)),
3911                            size: size(px(500.), px(500.)),
3912                        },
3913                        cx,
3914                    )
3915                })
3916            })
3917            .unwrap();
3918
3919        layout_state
3920            .position_map
3921            .line_layouts
3922            .iter()
3923            .map(|line_with_invisibles| &line_with_invisibles.invisibles)
3924            .flatten()
3925            .cloned()
3926            .collect()
3927    }
3928}
3929
3930pub fn register_action<T: Action>(
3931    view: &View<Editor>,
3932    cx: &mut WindowContext,
3933    listener: impl Fn(&mut Editor, &T, &mut ViewContext<Editor>) + 'static,
3934) {
3935    let view = view.clone();
3936    cx.on_action(TypeId::of::<T>(), move |action, phase, cx| {
3937        let action = action.downcast_ref().unwrap();
3938        if phase == DispatchPhase::Bubble {
3939            view.update(cx, |editor, cx| {
3940                listener(editor, action, cx);
3941            })
3942        }
3943    })
3944}
3945
3946fn compute_auto_height_layout(
3947    editor: &mut Editor,
3948    max_lines: usize,
3949    max_line_number_width: Pixels,
3950    known_dimensions: Size<Option<Pixels>>,
3951    cx: &mut ViewContext<Editor>,
3952) -> Option<Size<Pixels>> {
3953    let width = known_dimensions.width?;
3954    if let Some(height) = known_dimensions.height {
3955        return Some(size(width, height));
3956    }
3957
3958    let style = editor.style.as_ref().unwrap();
3959    let font_id = cx.text_system().resolve_font(&style.text.font());
3960    let font_size = style.text.font_size.to_pixels(cx.rem_size());
3961    let line_height = style.text.line_height_in_pixels(cx.rem_size());
3962    let em_width = cx
3963        .text_system()
3964        .typographic_bounds(font_id, font_size, 'm')
3965        .unwrap()
3966        .size
3967        .width;
3968
3969    let mut snapshot = editor.snapshot(cx);
3970    let gutter_dimensions =
3971        snapshot.gutter_dimensions(font_id, font_size, em_width, max_line_number_width, cx);
3972
3973    editor.gutter_width = gutter_dimensions.width;
3974    let text_width = width - gutter_dimensions.width;
3975    let overscroll = size(em_width, px(0.));
3976
3977    let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width;
3978    if editor.set_wrap_width(Some(editor_width), cx) {
3979        snapshot = editor.snapshot(cx);
3980    }
3981
3982    let scroll_height = Pixels::from(snapshot.max_point().row() + 1) * line_height;
3983    let height = scroll_height
3984        .max(line_height)
3985        .min(line_height * max_lines as f32);
3986
3987    Some(size(width, height))
3988}