element.rs

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