element.rs

   1use crate::{
   2    BlockId, COLUMNAR_SELECTION_MODIFIERS, CURSORS_VISIBLE_FOR, ChunkRendererContext,
   3    ChunkReplacement, ContextMenuPlacement, CursorShape, CustomBlockId, DisplayDiffHunk,
   4    DisplayPoint, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode,
   5    Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, FILE_HEADER_HEIGHT,
   6    FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor,
   7    InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineHighlight, LineUp,
   8    MAX_LINE_LEN, MIN_LINE_NUMBER_DIGITS, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts,
   9    PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, Selection,
  10    SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold,
  11    code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP},
  12    display_map::{
  13        Block, BlockContext, BlockStyle, DisplaySnapshot, FoldId, HighlightedChunk, ToDisplayPoint,
  14    },
  15    editor_settings::{
  16        CurrentLineHighlight, DoubleClickInMultibuffer, MultiCursorModifier, ScrollBeyondLastLine,
  17        ScrollbarAxes, ScrollbarDiagnostics, ShowScrollbar,
  18    },
  19    git::blame::{BlameRenderer, GitBlame, GlobalBlameRenderer},
  20    hover_popover::{
  21        self, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT, hover_at,
  22    },
  23    inlay_hint_settings,
  24    items::BufferSearchHighlights,
  25    mouse_context_menu::{self, MenuPosition},
  26    scroll::scroll_amount::ScrollAmount,
  27};
  28use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
  29use client::ParticipantIndex;
  30use collections::{BTreeMap, HashMap, HashSet};
  31use feature_flags::{Debugger, FeatureFlagAppExt};
  32use file_icons::FileIcons;
  33use git::{Oid, blame::BlameEntry, status::FileStatus};
  34use gpui::{
  35    Action, Along, AnyElement, App, AvailableSpace, Axis as ScrollbarAxis, BorderStyle, Bounds,
  36    ClickEvent, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase, Edges, Element,
  37    ElementInputHandler, Entity, Focusable as _, FontId, GlobalElementId, Hitbox, Hsla,
  38    InteractiveElement, IntoElement, Keystroke, Length, ModifiersChangedEvent, MouseButton,
  39    MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta,
  40    ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
  41    TextRun, TextStyleRefinement, WeakEntity, Window, anchored, deferred, div, fill,
  42    linear_color_stop, linear_gradient, outline, point, px, quad, relative, size, solid_background,
  43    transparent_black,
  44};
  45use itertools::Itertools;
  46use language::language_settings::{
  47    IndentGuideBackgroundColoring, IndentGuideColoring, IndentGuideSettings, ShowWhitespaceSetting,
  48};
  49use lsp::DiagnosticSeverity;
  50use multi_buffer::{
  51    Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint,
  52    MultiBufferRow, RowInfo,
  53};
  54use project::{
  55    debugger::breakpoint_store::Breakpoint,
  56    project_settings::{self, GitGutterSetting, GitHunkStyleSetting, ProjectSettings},
  57};
  58use settings::Settings;
  59use smallvec::{SmallVec, smallvec};
  60use std::{
  61    any::TypeId,
  62    borrow::Cow,
  63    cmp::{self, Ordering},
  64    fmt::{self, Write},
  65    iter, mem,
  66    ops::{Deref, Range},
  67    rc::Rc,
  68    sync::Arc,
  69    time::Duration,
  70};
  71use sum_tree::Bias;
  72use text::BufferId;
  73use theme::{ActiveTheme, Appearance, BufferLineHeight, PlayerColor};
  74use ui::{ButtonLike, KeyBinding, POPOVER_Y_PADDING, Tooltip, h_flex, prelude::*};
  75use unicode_segmentation::UnicodeSegmentation;
  76use util::{RangeExt, ResultExt, debug_panic};
  77use workspace::{Workspace, item::Item, notifications::NotifyTaskExt};
  78
  79const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
  80
  81/// Determines what kinds of highlights should be applied to a lines background.
  82#[derive(Clone, Copy, Default)]
  83struct LineHighlightSpec {
  84    selection: bool,
  85    breakpoint: bool,
  86    _active_stack_frame: bool,
  87}
  88
  89struct SelectionLayout {
  90    head: DisplayPoint,
  91    cursor_shape: CursorShape,
  92    is_newest: bool,
  93    is_local: bool,
  94    range: Range<DisplayPoint>,
  95    active_rows: Range<DisplayRow>,
  96    user_name: Option<SharedString>,
  97}
  98
  99impl SelectionLayout {
 100    fn new<T: ToPoint + ToDisplayPoint + Clone>(
 101        selection: Selection<T>,
 102        line_mode: bool,
 103        cursor_shape: CursorShape,
 104        map: &DisplaySnapshot,
 105        is_newest: bool,
 106        is_local: bool,
 107        user_name: Option<SharedString>,
 108    ) -> Self {
 109        let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
 110        let display_selection = point_selection.map(|p| p.to_display_point(map));
 111        let mut range = display_selection.range();
 112        let mut head = display_selection.head();
 113        let mut active_rows = map.prev_line_boundary(point_selection.start).1.row()
 114            ..map.next_line_boundary(point_selection.end).1.row();
 115
 116        // vim visual line mode
 117        if line_mode {
 118            let point_range = map.expand_to_line(point_selection.range());
 119            range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map);
 120        }
 121
 122        // any vim visual mode (including line mode)
 123        if (cursor_shape == CursorShape::Block || cursor_shape == CursorShape::Hollow)
 124            && !range.is_empty()
 125            && !selection.reversed
 126        {
 127            if head.column() > 0 {
 128                head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left)
 129            } else if head.row().0 > 0 && head != map.max_point() {
 130                head = map.clip_point(
 131                    DisplayPoint::new(
 132                        head.row().previous_row(),
 133                        map.line_len(head.row().previous_row()),
 134                    ),
 135                    Bias::Left,
 136                );
 137                // updating range.end is a no-op unless you're cursor is
 138                // on the newline containing a multi-buffer divider
 139                // in which case the clip_point may have moved the head up
 140                // an additional row.
 141                range.end = DisplayPoint::new(head.row().next_row(), 0);
 142                active_rows.end = head.row();
 143            }
 144        }
 145
 146        Self {
 147            head,
 148            cursor_shape,
 149            is_newest,
 150            is_local,
 151            range,
 152            active_rows,
 153            user_name,
 154        }
 155    }
 156}
 157
 158pub struct EditorElement {
 159    editor: Entity<Editor>,
 160    style: EditorStyle,
 161}
 162
 163type DisplayRowDelta = u32;
 164
 165impl EditorElement {
 166    pub(crate) const SCROLLBAR_WIDTH: Pixels = px(15.);
 167
 168    pub fn new(editor: &Entity<Editor>, style: EditorStyle) -> Self {
 169        Self {
 170            editor: editor.clone(),
 171            style,
 172        }
 173    }
 174
 175    fn register_actions(&self, window: &mut Window, cx: &mut App) {
 176        let editor = &self.editor;
 177        editor.update(cx, |editor, cx| {
 178            for action in editor.editor_actions.borrow().values() {
 179                (action)(window, cx)
 180            }
 181        });
 182
 183        crate::rust_analyzer_ext::apply_related_actions(editor, window, cx);
 184        crate::clangd_ext::apply_related_actions(editor, window, cx);
 185
 186        register_action(editor, window, Editor::open_context_menu);
 187        register_action(editor, window, Editor::move_left);
 188        register_action(editor, window, Editor::move_right);
 189        register_action(editor, window, Editor::move_down);
 190        register_action(editor, window, Editor::move_down_by_lines);
 191        register_action(editor, window, Editor::select_down_by_lines);
 192        register_action(editor, window, Editor::move_up);
 193        register_action(editor, window, Editor::move_up_by_lines);
 194        register_action(editor, window, Editor::select_up_by_lines);
 195        register_action(editor, window, Editor::select_page_down);
 196        register_action(editor, window, Editor::select_page_up);
 197        register_action(editor, window, Editor::cancel);
 198        register_action(editor, window, Editor::newline);
 199        register_action(editor, window, Editor::newline_above);
 200        register_action(editor, window, Editor::newline_below);
 201        register_action(editor, window, Editor::backspace);
 202        register_action(editor, window, Editor::delete);
 203        register_action(editor, window, Editor::tab);
 204        register_action(editor, window, Editor::backtab);
 205        register_action(editor, window, Editor::indent);
 206        register_action(editor, window, Editor::outdent);
 207        register_action(editor, window, Editor::autoindent);
 208        register_action(editor, window, Editor::delete_line);
 209        register_action(editor, window, Editor::join_lines);
 210        register_action(editor, window, Editor::sort_lines_case_sensitive);
 211        register_action(editor, window, Editor::sort_lines_case_insensitive);
 212        register_action(editor, window, Editor::reverse_lines);
 213        register_action(editor, window, Editor::shuffle_lines);
 214        register_action(editor, window, Editor::convert_to_upper_case);
 215        register_action(editor, window, Editor::convert_to_lower_case);
 216        register_action(editor, window, Editor::convert_to_title_case);
 217        register_action(editor, window, Editor::convert_to_snake_case);
 218        register_action(editor, window, Editor::convert_to_kebab_case);
 219        register_action(editor, window, Editor::convert_to_upper_camel_case);
 220        register_action(editor, window, Editor::convert_to_lower_camel_case);
 221        register_action(editor, window, Editor::convert_to_opposite_case);
 222        register_action(editor, window, Editor::convert_to_rot13);
 223        register_action(editor, window, Editor::convert_to_rot47);
 224        register_action(editor, window, Editor::delete_to_previous_word_start);
 225        register_action(editor, window, Editor::delete_to_previous_subword_start);
 226        register_action(editor, window, Editor::delete_to_next_word_end);
 227        register_action(editor, window, Editor::delete_to_next_subword_end);
 228        register_action(editor, window, Editor::delete_to_beginning_of_line);
 229        register_action(editor, window, Editor::delete_to_end_of_line);
 230        register_action(editor, window, Editor::cut_to_end_of_line);
 231        register_action(editor, window, Editor::duplicate_line_up);
 232        register_action(editor, window, Editor::duplicate_line_down);
 233        register_action(editor, window, Editor::duplicate_selection);
 234        register_action(editor, window, Editor::move_line_up);
 235        register_action(editor, window, Editor::move_line_down);
 236        register_action(editor, window, Editor::transpose);
 237        register_action(editor, window, Editor::rewrap);
 238        register_action(editor, window, Editor::cut);
 239        register_action(editor, window, Editor::kill_ring_cut);
 240        register_action(editor, window, Editor::kill_ring_yank);
 241        register_action(editor, window, Editor::copy);
 242        register_action(editor, window, Editor::copy_and_trim);
 243        register_action(editor, window, Editor::paste);
 244        register_action(editor, window, Editor::undo);
 245        register_action(editor, window, Editor::redo);
 246        register_action(editor, window, Editor::move_page_up);
 247        register_action(editor, window, Editor::move_page_down);
 248        register_action(editor, window, Editor::next_screen);
 249        register_action(editor, window, Editor::scroll_cursor_top);
 250        register_action(editor, window, Editor::scroll_cursor_center);
 251        register_action(editor, window, Editor::scroll_cursor_bottom);
 252        register_action(editor, window, Editor::scroll_cursor_center_top_bottom);
 253        register_action(editor, window, |editor, _: &LineDown, window, cx| {
 254            editor.scroll_screen(&ScrollAmount::Line(1.), window, cx)
 255        });
 256        register_action(editor, window, |editor, _: &LineUp, window, cx| {
 257            editor.scroll_screen(&ScrollAmount::Line(-1.), window, cx)
 258        });
 259        register_action(editor, window, |editor, _: &HalfPageDown, window, cx| {
 260            editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx)
 261        });
 262        register_action(
 263            editor,
 264            window,
 265            |editor, HandleInput(text): &HandleInput, window, cx| {
 266                if text.is_empty() {
 267                    return;
 268                }
 269                editor.handle_input(text, window, cx);
 270            },
 271        );
 272        register_action(editor, window, |editor, _: &HalfPageUp, window, cx| {
 273            editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx)
 274        });
 275        register_action(editor, window, |editor, _: &PageDown, window, cx| {
 276            editor.scroll_screen(&ScrollAmount::Page(1.), window, cx)
 277        });
 278        register_action(editor, window, |editor, _: &PageUp, window, cx| {
 279            editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx)
 280        });
 281        register_action(editor, window, Editor::move_to_previous_word_start);
 282        register_action(editor, window, Editor::move_to_previous_subword_start);
 283        register_action(editor, window, Editor::move_to_next_word_end);
 284        register_action(editor, window, Editor::move_to_next_subword_end);
 285        register_action(editor, window, Editor::move_to_beginning_of_line);
 286        register_action(editor, window, Editor::move_to_end_of_line);
 287        register_action(editor, window, Editor::move_to_start_of_paragraph);
 288        register_action(editor, window, Editor::move_to_end_of_paragraph);
 289        register_action(editor, window, Editor::move_to_beginning);
 290        register_action(editor, window, Editor::move_to_end);
 291        register_action(editor, window, Editor::move_to_start_of_excerpt);
 292        register_action(editor, window, Editor::move_to_start_of_next_excerpt);
 293        register_action(editor, window, Editor::move_to_end_of_excerpt);
 294        register_action(editor, window, Editor::move_to_end_of_previous_excerpt);
 295        register_action(editor, window, Editor::select_up);
 296        register_action(editor, window, Editor::select_down);
 297        register_action(editor, window, Editor::select_left);
 298        register_action(editor, window, Editor::select_right);
 299        register_action(editor, window, Editor::select_to_previous_word_start);
 300        register_action(editor, window, Editor::select_to_previous_subword_start);
 301        register_action(editor, window, Editor::select_to_next_word_end);
 302        register_action(editor, window, Editor::select_to_next_subword_end);
 303        register_action(editor, window, Editor::select_to_beginning_of_line);
 304        register_action(editor, window, Editor::select_to_end_of_line);
 305        register_action(editor, window, Editor::select_to_start_of_paragraph);
 306        register_action(editor, window, Editor::select_to_end_of_paragraph);
 307        register_action(editor, window, Editor::select_to_start_of_excerpt);
 308        register_action(editor, window, Editor::select_to_start_of_next_excerpt);
 309        register_action(editor, window, Editor::select_to_end_of_excerpt);
 310        register_action(editor, window, Editor::select_to_end_of_previous_excerpt);
 311        register_action(editor, window, Editor::select_to_beginning);
 312        register_action(editor, window, Editor::select_to_end);
 313        register_action(editor, window, Editor::select_all);
 314        register_action(editor, window, |editor, action, window, cx| {
 315            editor.select_all_matches(action, window, cx).log_err();
 316        });
 317        register_action(editor, window, Editor::select_line);
 318        register_action(editor, window, Editor::split_selection_into_lines);
 319        register_action(editor, window, Editor::add_selection_above);
 320        register_action(editor, window, Editor::add_selection_below);
 321        register_action(editor, window, |editor, action, window, cx| {
 322            editor.select_next(action, window, cx).log_err();
 323        });
 324        register_action(editor, window, |editor, action, window, cx| {
 325            editor.select_previous(action, window, cx).log_err();
 326        });
 327        register_action(editor, window, Editor::toggle_comments);
 328        register_action(editor, window, Editor::select_larger_syntax_node);
 329        register_action(editor, window, Editor::select_smaller_syntax_node);
 330        register_action(editor, window, Editor::select_enclosing_symbol);
 331        register_action(editor, window, Editor::move_to_enclosing_bracket);
 332        register_action(editor, window, Editor::undo_selection);
 333        register_action(editor, window, Editor::redo_selection);
 334        if !editor.read(cx).is_singleton(cx) {
 335            register_action(editor, window, Editor::expand_excerpts);
 336            register_action(editor, window, Editor::expand_excerpts_up);
 337            register_action(editor, window, Editor::expand_excerpts_down);
 338        }
 339        register_action(editor, window, Editor::go_to_diagnostic);
 340        register_action(editor, window, Editor::go_to_prev_diagnostic);
 341        register_action(editor, window, Editor::go_to_next_hunk);
 342        register_action(editor, window, Editor::go_to_prev_hunk);
 343        register_action(editor, window, |editor, action, window, cx| {
 344            editor
 345                .go_to_definition(action, window, cx)
 346                .detach_and_log_err(cx);
 347        });
 348        register_action(editor, window, |editor, action, window, cx| {
 349            editor
 350                .go_to_definition_split(action, window, cx)
 351                .detach_and_log_err(cx);
 352        });
 353        register_action(editor, window, |editor, action, window, cx| {
 354            editor
 355                .go_to_declaration(action, window, cx)
 356                .detach_and_log_err(cx);
 357        });
 358        register_action(editor, window, |editor, action, window, cx| {
 359            editor
 360                .go_to_declaration_split(action, window, cx)
 361                .detach_and_log_err(cx);
 362        });
 363        register_action(editor, window, |editor, action, window, cx| {
 364            editor
 365                .go_to_implementation(action, window, cx)
 366                .detach_and_log_err(cx);
 367        });
 368        register_action(editor, window, |editor, action, window, cx| {
 369            editor
 370                .go_to_implementation_split(action, window, cx)
 371                .detach_and_log_err(cx);
 372        });
 373        register_action(editor, window, |editor, action, window, cx| {
 374            editor
 375                .go_to_type_definition(action, window, cx)
 376                .detach_and_log_err(cx);
 377        });
 378        register_action(editor, window, |editor, action, window, cx| {
 379            editor
 380                .go_to_type_definition_split(action, window, cx)
 381                .detach_and_log_err(cx);
 382        });
 383        register_action(editor, window, Editor::open_url);
 384        register_action(editor, window, Editor::open_selected_filename);
 385        register_action(editor, window, Editor::fold);
 386        register_action(editor, window, Editor::fold_at_level);
 387        register_action(editor, window, Editor::fold_all);
 388        register_action(editor, window, Editor::fold_function_bodies);
 389        register_action(editor, window, Editor::fold_at);
 390        register_action(editor, window, Editor::fold_recursive);
 391        register_action(editor, window, Editor::toggle_fold);
 392        register_action(editor, window, Editor::toggle_fold_recursive);
 393        register_action(editor, window, Editor::unfold_lines);
 394        register_action(editor, window, Editor::unfold_recursive);
 395        register_action(editor, window, Editor::unfold_all);
 396        register_action(editor, window, Editor::unfold_at);
 397        register_action(editor, window, Editor::fold_selected_ranges);
 398        register_action(editor, window, Editor::set_mark);
 399        register_action(editor, window, Editor::swap_selection_ends);
 400        register_action(editor, window, Editor::show_completions);
 401        register_action(editor, window, Editor::show_word_completions);
 402        register_action(editor, window, Editor::toggle_code_actions);
 403        register_action(editor, window, Editor::open_excerpts);
 404        register_action(editor, window, Editor::open_excerpts_in_split);
 405        register_action(editor, window, Editor::open_proposed_changes_editor);
 406        register_action(editor, window, Editor::toggle_soft_wrap);
 407        register_action(editor, window, Editor::toggle_tab_bar);
 408        register_action(editor, window, Editor::toggle_line_numbers);
 409        register_action(editor, window, Editor::toggle_relative_line_numbers);
 410        register_action(editor, window, Editor::toggle_indent_guides);
 411        register_action(editor, window, Editor::toggle_inlay_hints);
 412        register_action(editor, window, Editor::toggle_edit_predictions);
 413        register_action(editor, window, Editor::toggle_inline_diagnostics);
 414        register_action(editor, window, hover_popover::hover);
 415        register_action(editor, window, Editor::reveal_in_finder);
 416        register_action(editor, window, Editor::copy_path);
 417        register_action(editor, window, Editor::copy_relative_path);
 418        register_action(editor, window, Editor::copy_file_name);
 419        register_action(editor, window, Editor::copy_file_name_without_extension);
 420        register_action(editor, window, Editor::copy_highlight_json);
 421        register_action(editor, window, Editor::copy_permalink_to_line);
 422        register_action(editor, window, Editor::open_permalink_to_line);
 423        register_action(editor, window, Editor::copy_file_location);
 424        register_action(editor, window, Editor::toggle_git_blame);
 425        register_action(editor, window, Editor::toggle_git_blame_inline);
 426        register_action(editor, window, Editor::open_git_blame_commit);
 427        register_action(editor, window, Editor::toggle_selected_diff_hunks);
 428        register_action(editor, window, Editor::toggle_staged_selected_diff_hunks);
 429        register_action(editor, window, Editor::stage_and_next);
 430        register_action(editor, window, Editor::unstage_and_next);
 431        register_action(editor, window, Editor::expand_all_diff_hunks);
 432
 433        register_action(editor, window, |editor, action, window, cx| {
 434            if let Some(task) = editor.format(action, window, cx) {
 435                task.detach_and_notify_err(window, cx);
 436            } else {
 437                cx.propagate();
 438            }
 439        });
 440        register_action(editor, window, |editor, action, window, cx| {
 441            if let Some(task) = editor.format_selections(action, window, cx) {
 442                task.detach_and_notify_err(window, cx);
 443            } else {
 444                cx.propagate();
 445            }
 446        });
 447        register_action(editor, window, |editor, action, window, cx| {
 448            if let Some(task) = editor.organize_imports(action, window, cx) {
 449                task.detach_and_notify_err(window, cx);
 450            } else {
 451                cx.propagate();
 452            }
 453        });
 454        register_action(editor, window, Editor::restart_language_server);
 455        register_action(editor, window, Editor::stop_language_server);
 456        register_action(editor, window, Editor::show_character_palette);
 457        register_action(editor, window, |editor, action, window, cx| {
 458            if let Some(task) = editor.confirm_completion(action, window, cx) {
 459                task.detach_and_notify_err(window, cx);
 460            } else {
 461                cx.propagate();
 462            }
 463        });
 464        register_action(editor, window, |editor, action, window, cx| {
 465            if let Some(task) = editor.compose_completion(action, window, cx) {
 466                task.detach_and_notify_err(window, cx);
 467            } else {
 468                cx.propagate();
 469            }
 470        });
 471        register_action(editor, window, |editor, action, window, cx| {
 472            if let Some(task) = editor.confirm_code_action(action, window, cx) {
 473                task.detach_and_notify_err(window, cx);
 474            } else {
 475                cx.propagate();
 476            }
 477        });
 478        register_action(editor, window, |editor, action, window, cx| {
 479            if let Some(task) = editor.rename(action, window, cx) {
 480                task.detach_and_notify_err(window, cx);
 481            } else {
 482                cx.propagate();
 483            }
 484        });
 485        register_action(editor, window, |editor, action, window, cx| {
 486            if let Some(task) = editor.confirm_rename(action, window, cx) {
 487                task.detach_and_notify_err(window, cx);
 488            } else {
 489                cx.propagate();
 490            }
 491        });
 492        register_action(editor, window, |editor, action, window, cx| {
 493            if let Some(task) = editor.find_all_references(action, window, cx) {
 494                task.detach_and_log_err(cx);
 495            } else {
 496                cx.propagate();
 497            }
 498        });
 499        register_action(editor, window, Editor::show_signature_help);
 500        register_action(editor, window, Editor::next_edit_prediction);
 501        register_action(editor, window, Editor::previous_edit_prediction);
 502        register_action(editor, window, Editor::show_inline_completion);
 503        register_action(editor, window, Editor::context_menu_first);
 504        register_action(editor, window, Editor::context_menu_prev);
 505        register_action(editor, window, Editor::context_menu_next);
 506        register_action(editor, window, Editor::context_menu_last);
 507        register_action(editor, window, Editor::display_cursor_names);
 508        register_action(editor, window, Editor::unique_lines_case_insensitive);
 509        register_action(editor, window, Editor::unique_lines_case_sensitive);
 510        register_action(editor, window, Editor::accept_partial_inline_completion);
 511        register_action(editor, window, Editor::accept_edit_prediction);
 512        register_action(editor, window, Editor::restore_file);
 513        register_action(editor, window, Editor::git_restore);
 514        register_action(editor, window, Editor::apply_all_diff_hunks);
 515        register_action(editor, window, Editor::apply_selected_diff_hunks);
 516        register_action(editor, window, Editor::open_active_item_in_terminal);
 517        register_action(editor, window, Editor::reload_file);
 518        register_action(editor, window, Editor::spawn_nearest_task);
 519        register_action(editor, window, Editor::insert_uuid_v4);
 520        register_action(editor, window, Editor::insert_uuid_v7);
 521        register_action(editor, window, Editor::open_selections_in_multibuffer);
 522        if cx.has_flag::<Debugger>() {
 523            register_action(editor, window, Editor::toggle_breakpoint);
 524            register_action(editor, window, Editor::edit_log_breakpoint);
 525            register_action(editor, window, Editor::enable_breakpoint);
 526            register_action(editor, window, Editor::disable_breakpoint);
 527        }
 528    }
 529
 530    fn register_key_listeners(&self, window: &mut Window, _: &mut App, layout: &EditorLayout) {
 531        let position_map = layout.position_map.clone();
 532        window.on_key_event({
 533            let editor = self.editor.clone();
 534            move |event: &ModifiersChangedEvent, phase, window, cx| {
 535                if phase != DispatchPhase::Bubble {
 536                    return;
 537                }
 538                editor.update(cx, |editor, cx| {
 539                    let inlay_hint_settings = inlay_hint_settings(
 540                        editor.selections.newest_anchor().head(),
 541                        &editor.buffer.read(cx).snapshot(cx),
 542                        cx,
 543                    );
 544
 545                    if let Some(inlay_modifiers) = inlay_hint_settings
 546                        .toggle_on_modifiers_press
 547                        .as_ref()
 548                        .filter(|modifiers| modifiers.modified())
 549                    {
 550                        editor.refresh_inlay_hints(
 551                            InlayHintRefreshReason::ModifiersChanged(
 552                                inlay_modifiers == &event.modifiers,
 553                            ),
 554                            cx,
 555                        );
 556                    }
 557
 558                    if editor.hover_state.focused(window, cx) {
 559                        return;
 560                    }
 561
 562                    editor.handle_modifiers_changed(event.modifiers, &position_map, window, cx);
 563                })
 564            }
 565        });
 566    }
 567
 568    fn mouse_left_down(
 569        editor: &mut Editor,
 570        event: &MouseDownEvent,
 571        hovered_hunk: Option<Range<Anchor>>,
 572        position_map: &PositionMap,
 573        line_numbers: &HashMap<MultiBufferRow, LineNumberLayout>,
 574        window: &mut Window,
 575        cx: &mut Context<Editor>,
 576    ) {
 577        if window.default_prevented() {
 578            return;
 579        }
 580
 581        let text_hitbox = &position_map.text_hitbox;
 582        let gutter_hitbox = &position_map.gutter_hitbox;
 583        let mut click_count = event.click_count;
 584        let mut modifiers = event.modifiers;
 585
 586        if let Some(hovered_hunk) = hovered_hunk {
 587            editor.toggle_single_diff_hunk(hovered_hunk, cx);
 588            cx.notify();
 589            return;
 590        } else if gutter_hitbox.is_hovered(window) {
 591            click_count = 3; // Simulate triple-click when clicking the gutter to select lines
 592        } else if !text_hitbox.is_hovered(window) {
 593            return;
 594        }
 595
 596        let is_singleton = editor.buffer().read(cx).is_singleton();
 597
 598        if click_count == 2 && !is_singleton {
 599            match EditorSettings::get_global(cx).double_click_in_multibuffer {
 600                DoubleClickInMultibuffer::Select => {
 601                    // do nothing special on double click, all selection logic is below
 602                }
 603                DoubleClickInMultibuffer::Open => {
 604                    if modifiers.alt {
 605                        // if double click is made with alt, pretend it's a regular double click without opening and alt,
 606                        // and run the selection logic.
 607                        modifiers.alt = false;
 608                    } else {
 609                        let scroll_position_row =
 610                            position_map.scroll_pixel_position.y / position_map.line_height;
 611                        let display_row = (((event.position - gutter_hitbox.bounds.origin).y
 612                            + position_map.scroll_pixel_position.y)
 613                            / position_map.line_height)
 614                            as u32;
 615                        let multi_buffer_row = position_map
 616                            .snapshot
 617                            .display_point_to_point(
 618                                DisplayPoint::new(DisplayRow(display_row), 0),
 619                                Bias::Right,
 620                            )
 621                            .row;
 622                        let line_offset_from_top = display_row - scroll_position_row as u32;
 623                        // if double click is made without alt, open the corresponding excerp
 624                        editor.open_excerpts_common(
 625                            Some(JumpData::MultiBufferRow {
 626                                row: MultiBufferRow(multi_buffer_row),
 627                                line_offset_from_top,
 628                            }),
 629                            false,
 630                            window,
 631                            cx,
 632                        );
 633                        return;
 634                    }
 635                }
 636            }
 637        }
 638
 639        let point_for_position = position_map.point_for_position(event.position);
 640        let position = point_for_position.previous_valid;
 641        if modifiers == COLUMNAR_SELECTION_MODIFIERS {
 642            editor.select(
 643                SelectPhase::BeginColumnar {
 644                    position,
 645                    reset: false,
 646                    goal_column: point_for_position.exact_unclipped.column(),
 647                },
 648                window,
 649                cx,
 650            );
 651        } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.secondary()
 652        {
 653            editor.select(
 654                SelectPhase::Extend {
 655                    position,
 656                    click_count,
 657                },
 658                window,
 659                cx,
 660            );
 661        } else {
 662            let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
 663            let multi_cursor_modifier = match multi_cursor_setting {
 664                MultiCursorModifier::Alt => modifiers.alt,
 665                MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 666            };
 667            editor.select(
 668                SelectPhase::Begin {
 669                    position,
 670                    add: multi_cursor_modifier,
 671                    click_count,
 672                },
 673                window,
 674                cx,
 675            );
 676        }
 677        cx.stop_propagation();
 678
 679        if !is_singleton {
 680            let display_row = (((event.position - gutter_hitbox.bounds.origin).y
 681                + position_map.scroll_pixel_position.y)
 682                / position_map.line_height) as u32;
 683            let multi_buffer_row = position_map
 684                .snapshot
 685                .display_point_to_point(DisplayPoint::new(DisplayRow(display_row), 0), Bias::Right)
 686                .row;
 687            if line_numbers
 688                .get(&MultiBufferRow(multi_buffer_row))
 689                .and_then(|line_number| line_number.hitbox.as_ref())
 690                .is_some_and(|hitbox| hitbox.contains(&event.position))
 691            {
 692                let scroll_position_row =
 693                    position_map.scroll_pixel_position.y / position_map.line_height;
 694                let line_offset_from_top = display_row - scroll_position_row as u32;
 695
 696                editor.open_excerpts_common(
 697                    Some(JumpData::MultiBufferRow {
 698                        row: MultiBufferRow(multi_buffer_row),
 699                        line_offset_from_top,
 700                    }),
 701                    modifiers.alt,
 702                    window,
 703                    cx,
 704                );
 705                cx.stop_propagation();
 706            }
 707        }
 708    }
 709
 710    fn mouse_right_down(
 711        editor: &mut Editor,
 712        event: &MouseDownEvent,
 713        position_map: &PositionMap,
 714        window: &mut Window,
 715        cx: &mut Context<Editor>,
 716    ) {
 717        if position_map.gutter_hitbox.is_hovered(window) {
 718            let gutter_right_padding = editor.gutter_dimensions.right_padding;
 719            let hitbox = &position_map.gutter_hitbox;
 720
 721            if event.position.x <= hitbox.bounds.right() - gutter_right_padding {
 722                let point_for_position = position_map.point_for_position(event.position);
 723                editor.set_breakpoint_context_menu(
 724                    point_for_position.previous_valid.row(),
 725                    None,
 726                    event.position,
 727                    window,
 728                    cx,
 729                );
 730            }
 731            return;
 732        }
 733
 734        if !position_map.text_hitbox.is_hovered(window) {
 735            return;
 736        }
 737
 738        let point_for_position = position_map.point_for_position(event.position);
 739        mouse_context_menu::deploy_context_menu(
 740            editor,
 741            Some(event.position),
 742            point_for_position.previous_valid,
 743            window,
 744            cx,
 745        );
 746        cx.stop_propagation();
 747    }
 748
 749    fn mouse_middle_down(
 750        editor: &mut Editor,
 751        event: &MouseDownEvent,
 752        position_map: &PositionMap,
 753        window: &mut Window,
 754        cx: &mut Context<Editor>,
 755    ) {
 756        if !position_map.text_hitbox.is_hovered(window) || window.default_prevented() {
 757            return;
 758        }
 759
 760        let point_for_position = position_map.point_for_position(event.position);
 761        let position = point_for_position.previous_valid;
 762
 763        editor.select(
 764            SelectPhase::BeginColumnar {
 765                position,
 766                reset: true,
 767                goal_column: point_for_position.exact_unclipped.column(),
 768            },
 769            window,
 770            cx,
 771        );
 772    }
 773
 774    fn mouse_up(
 775        editor: &mut Editor,
 776        event: &MouseUpEvent,
 777        position_map: &PositionMap,
 778        window: &mut Window,
 779        cx: &mut Context<Editor>,
 780    ) {
 781        let text_hitbox = &position_map.text_hitbox;
 782        let end_selection = editor.has_pending_selection();
 783        let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 784
 785        if end_selection {
 786            editor.select(SelectPhase::End, window, cx);
 787        }
 788
 789        if end_selection && pending_nonempty_selections {
 790            cx.stop_propagation();
 791        } else if cfg!(any(target_os = "linux", target_os = "freebsd"))
 792            && event.button == MouseButton::Middle
 793        {
 794            if !text_hitbox.is_hovered(window) || editor.read_only(cx) {
 795                return;
 796            }
 797
 798            #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 799            if EditorSettings::get_global(cx).middle_click_paste {
 800                if let Some(text) = cx.read_from_primary().and_then(|item| item.text()) {
 801                    let point_for_position = position_map.point_for_position(event.position);
 802                    let position = point_for_position.previous_valid;
 803
 804                    editor.select(
 805                        SelectPhase::Begin {
 806                            position,
 807                            add: false,
 808                            click_count: 1,
 809                        },
 810                        window,
 811                        cx,
 812                    );
 813                    editor.insert(&text, window, cx);
 814                }
 815                cx.stop_propagation()
 816            }
 817        }
 818    }
 819
 820    fn click(
 821        editor: &mut Editor,
 822        event: &ClickEvent,
 823        position_map: &PositionMap,
 824        window: &mut Window,
 825        cx: &mut Context<Editor>,
 826    ) {
 827        let text_hitbox = &position_map.text_hitbox;
 828        let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 829
 830        let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
 831        let multi_cursor_modifier = match multi_cursor_setting {
 832            MultiCursorModifier::Alt => event.modifiers().secondary(),
 833            MultiCursorModifier::CmdOrCtrl => event.modifiers().alt,
 834        };
 835
 836        if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(window) {
 837            let point = position_map.point_for_position(event.up.position);
 838            editor.handle_click_hovered_link(point, event.modifiers(), window, cx);
 839
 840            cx.stop_propagation();
 841        }
 842    }
 843
 844    fn mouse_dragged(
 845        editor: &mut Editor,
 846        event: &MouseMoveEvent,
 847        position_map: &PositionMap,
 848        window: &mut Window,
 849        cx: &mut Context<Editor>,
 850    ) {
 851        if !editor.has_pending_selection() {
 852            return;
 853        }
 854
 855        let text_bounds = position_map.text_hitbox.bounds;
 856        let point_for_position = position_map.point_for_position(event.position);
 857        let mut scroll_delta = gpui::Point::<f32>::default();
 858        let vertical_margin = position_map.line_height.min(text_bounds.size.height / 3.0);
 859        let top = text_bounds.origin.y + vertical_margin;
 860        let bottom = text_bounds.bottom_left().y - vertical_margin;
 861        if event.position.y < top {
 862            scroll_delta.y = -scale_vertical_mouse_autoscroll_delta(top - event.position.y);
 863        }
 864        if event.position.y > bottom {
 865            scroll_delta.y = scale_vertical_mouse_autoscroll_delta(event.position.y - bottom);
 866        }
 867
 868        // We need horizontal width of text
 869        let style = editor.style.clone().unwrap_or_default();
 870        let font_id = window.text_system().resolve_font(&style.text.font());
 871        let font_size = style.text.font_size.to_pixels(window.rem_size());
 872        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
 873
 874        let scroll_margin_x = EditorSettings::get_global(cx).horizontal_scroll_margin;
 875
 876        let scroll_space: Pixels = scroll_margin_x * em_width;
 877
 878        let left = text_bounds.origin.x + scroll_space;
 879        let right = text_bounds.top_right().x - scroll_space;
 880
 881        if event.position.x < left {
 882            scroll_delta.x = -scale_horizontal_mouse_autoscroll_delta(left - event.position.x);
 883        }
 884        if event.position.x > right {
 885            scroll_delta.x = scale_horizontal_mouse_autoscroll_delta(event.position.x - right);
 886        }
 887
 888        editor.select(
 889            SelectPhase::Update {
 890                position: point_for_position.previous_valid,
 891                goal_column: point_for_position.exact_unclipped.column(),
 892                scroll_delta,
 893            },
 894            window,
 895            cx,
 896        );
 897    }
 898
 899    fn mouse_moved(
 900        editor: &mut Editor,
 901        event: &MouseMoveEvent,
 902        position_map: &PositionMap,
 903        window: &mut Window,
 904        cx: &mut Context<Editor>,
 905    ) {
 906        let text_hitbox = &position_map.text_hitbox;
 907        let gutter_hitbox = &position_map.gutter_hitbox;
 908        let modifiers = event.modifiers;
 909        let gutter_hovered = gutter_hitbox.is_hovered(window);
 910        editor.set_gutter_hovered(gutter_hovered, cx);
 911        editor.mouse_cursor_hidden = false;
 912
 913        if gutter_hovered {
 914            let new_point = position_map
 915                .point_for_position(event.position)
 916                .previous_valid;
 917            let buffer_anchor = position_map
 918                .snapshot
 919                .display_point_to_anchor(new_point, Bias::Left);
 920
 921            if position_map
 922                .snapshot
 923                .buffer_snapshot
 924                .buffer_for_excerpt(buffer_anchor.excerpt_id)
 925                .is_some_and(|buffer| buffer.file().is_some())
 926            {
 927                let was_hovered = editor.gutter_breakpoint_indicator.0.is_some();
 928                let is_visible = editor
 929                    .gutter_breakpoint_indicator
 930                    .0
 931                    .map_or(false, |(_, is_active)| is_active);
 932                editor.gutter_breakpoint_indicator.0 = Some((new_point, is_visible));
 933
 934                editor.gutter_breakpoint_indicator.1.get_or_insert_with(|| {
 935                    cx.spawn(async move |this, cx| {
 936                        if !was_hovered {
 937                            cx.background_executor()
 938                                .timer(Duration::from_millis(200))
 939                                .await;
 940                        }
 941
 942                        this.update(cx, |this, cx| {
 943                            if let Some((_, is_active)) =
 944                                this.gutter_breakpoint_indicator.0.as_mut()
 945                            {
 946                                *is_active = true;
 947                            }
 948
 949                            cx.notify();
 950                        })
 951                        .ok();
 952                    })
 953                });
 954            } else {
 955                editor.gutter_breakpoint_indicator = (None, None);
 956            }
 957        } else {
 958            editor.gutter_breakpoint_indicator = (None, None);
 959        }
 960
 961        cx.notify();
 962
 963        // Don't trigger hover popover if mouse is hovering over context menu
 964        if text_hitbox.is_hovered(window) {
 965            let point_for_position = position_map.point_for_position(event.position);
 966
 967            editor.update_hovered_link(
 968                point_for_position,
 969                &position_map.snapshot,
 970                modifiers,
 971                window,
 972                cx,
 973            );
 974
 975            if let Some(point) = point_for_position.as_valid() {
 976                let anchor = position_map
 977                    .snapshot
 978                    .buffer_snapshot
 979                    .anchor_before(point.to_offset(&position_map.snapshot, Bias::Left));
 980                hover_at(editor, Some(anchor), window, cx);
 981                Self::update_visible_cursor(editor, point, position_map, window, cx);
 982            } else {
 983                hover_at(editor, None, window, cx);
 984            }
 985        } else {
 986            editor.hide_hovered_link(cx);
 987            hover_at(editor, None, window, cx);
 988            if gutter_hovered {
 989                cx.stop_propagation();
 990            }
 991        }
 992    }
 993
 994    fn update_visible_cursor(
 995        editor: &mut Editor,
 996        point: DisplayPoint,
 997        position_map: &PositionMap,
 998        window: &mut Window,
 999        cx: &mut Context<Editor>,
1000    ) {
1001        let snapshot = &position_map.snapshot;
1002        let Some(hub) = editor.collaboration_hub() else {
1003            return;
1004        };
1005        let start = snapshot.display_snapshot.clip_point(
1006            DisplayPoint::new(point.row(), point.column().saturating_sub(1)),
1007            Bias::Left,
1008        );
1009        let end = snapshot.display_snapshot.clip_point(
1010            DisplayPoint::new(
1011                point.row(),
1012                (point.column() + 1).min(snapshot.line_len(point.row())),
1013            ),
1014            Bias::Right,
1015        );
1016
1017        let range = snapshot
1018            .buffer_snapshot
1019            .anchor_at(start.to_point(&snapshot.display_snapshot), Bias::Left)
1020            ..snapshot
1021                .buffer_snapshot
1022                .anchor_at(end.to_point(&snapshot.display_snapshot), Bias::Right);
1023
1024        let Some(selection) = snapshot.remote_selections_in_range(&range, hub, cx).next() else {
1025            return;
1026        };
1027        let key = crate::HoveredCursor {
1028            replica_id: selection.replica_id,
1029            selection_id: selection.selection.id,
1030        };
1031        editor.hovered_cursors.insert(
1032            key.clone(),
1033            cx.spawn_in(window, async move |editor, cx| {
1034                cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
1035                editor
1036                    .update(cx, |editor, cx| {
1037                        editor.hovered_cursors.remove(&key);
1038                        cx.notify();
1039                    })
1040                    .ok();
1041            }),
1042        );
1043        cx.notify()
1044    }
1045
1046    fn layout_selections(
1047        &self,
1048        start_anchor: Anchor,
1049        end_anchor: Anchor,
1050        local_selections: &[Selection<Point>],
1051        snapshot: &EditorSnapshot,
1052        start_row: DisplayRow,
1053        end_row: DisplayRow,
1054        window: &mut Window,
1055        cx: &mut App,
1056    ) -> (
1057        Vec<(PlayerColor, Vec<SelectionLayout>)>,
1058        BTreeMap<DisplayRow, LineHighlightSpec>,
1059        Option<DisplayPoint>,
1060    ) {
1061        let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
1062        let mut active_rows = BTreeMap::new();
1063        let mut newest_selection_head = None;
1064        self.editor.update(cx, |editor, cx| {
1065            if editor.show_local_selections {
1066                let mut layouts = Vec::new();
1067                let newest = editor.selections.newest(cx);
1068                for selection in local_selections.iter().cloned() {
1069                    let is_empty = selection.start == selection.end;
1070                    let is_newest = selection == newest;
1071
1072                    let layout = SelectionLayout::new(
1073                        selection,
1074                        editor.selections.line_mode,
1075                        editor.cursor_shape,
1076                        &snapshot.display_snapshot,
1077                        is_newest,
1078                        editor.leader_peer_id.is_none(),
1079                        None,
1080                    );
1081                    if is_newest {
1082                        newest_selection_head = Some(layout.head);
1083                    }
1084
1085                    for row in cmp::max(layout.active_rows.start.0, start_row.0)
1086                        ..=cmp::min(layout.active_rows.end.0, end_row.0)
1087                    {
1088                        let contains_non_empty_selection = active_rows
1089                            .entry(DisplayRow(row))
1090                            .or_insert_with(LineHighlightSpec::default);
1091                        contains_non_empty_selection.selection |= !is_empty;
1092                    }
1093                    layouts.push(layout);
1094                }
1095
1096                let player = editor.current_user_player_color(cx);
1097                selections.push((player, layouts));
1098            }
1099
1100            if let Some(collaboration_hub) = &editor.collaboration_hub {
1101                // When following someone, render the local selections in their color.
1102                if let Some(leader_id) = editor.leader_peer_id {
1103                    if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id)
1104                    {
1105                        if let Some(participant_index) = collaboration_hub
1106                            .user_participant_indices(cx)
1107                            .get(&collaborator.user_id)
1108                        {
1109                            if let Some((local_selection_style, _)) = selections.first_mut() {
1110                                *local_selection_style = cx
1111                                    .theme()
1112                                    .players()
1113                                    .color_for_participant(participant_index.0);
1114                            }
1115                        }
1116                    }
1117                }
1118
1119                let mut remote_selections = HashMap::default();
1120                for selection in snapshot.remote_selections_in_range(
1121                    &(start_anchor..end_anchor),
1122                    collaboration_hub.as_ref(),
1123                    cx,
1124                ) {
1125                    let selection_style =
1126                        Self::get_participant_color(selection.participant_index, cx);
1127
1128                    // Don't re-render the leader's selections, since the local selections
1129                    // match theirs.
1130                    if Some(selection.peer_id) == editor.leader_peer_id {
1131                        continue;
1132                    }
1133                    let key = HoveredCursor {
1134                        replica_id: selection.replica_id,
1135                        selection_id: selection.selection.id,
1136                    };
1137
1138                    let is_shown =
1139                        editor.show_cursor_names || editor.hovered_cursors.contains_key(&key);
1140
1141                    remote_selections
1142                        .entry(selection.replica_id)
1143                        .or_insert((selection_style, Vec::new()))
1144                        .1
1145                        .push(SelectionLayout::new(
1146                            selection.selection,
1147                            selection.line_mode,
1148                            selection.cursor_shape,
1149                            &snapshot.display_snapshot,
1150                            false,
1151                            false,
1152                            if is_shown { selection.user_name } else { None },
1153                        ));
1154                }
1155
1156                selections.extend(remote_selections.into_values());
1157            } else if !editor.is_focused(window) && editor.show_cursor_when_unfocused {
1158                let layouts = snapshot
1159                    .buffer_snapshot
1160                    .selections_in_range(&(start_anchor..end_anchor), true)
1161                    .map(move |(_, line_mode, cursor_shape, selection)| {
1162                        SelectionLayout::new(
1163                            selection,
1164                            line_mode,
1165                            cursor_shape,
1166                            &snapshot.display_snapshot,
1167                            false,
1168                            false,
1169                            None,
1170                        )
1171                    })
1172                    .collect::<Vec<_>>();
1173                let player = editor.current_user_player_color(cx);
1174                selections.push((player, layouts));
1175            }
1176        });
1177        (selections, active_rows, newest_selection_head)
1178    }
1179
1180    fn collect_cursors(
1181        &self,
1182        snapshot: &EditorSnapshot,
1183        cx: &mut App,
1184    ) -> Vec<(DisplayPoint, Hsla)> {
1185        let editor = self.editor.read(cx);
1186        let mut cursors = Vec::new();
1187        let mut skip_local = false;
1188        let mut add_cursor = |anchor: Anchor, color| {
1189            cursors.push((anchor.to_display_point(&snapshot.display_snapshot), color));
1190        };
1191        // Remote cursors
1192        if let Some(collaboration_hub) = &editor.collaboration_hub {
1193            for remote_selection in snapshot.remote_selections_in_range(
1194                &(Anchor::min()..Anchor::max()),
1195                collaboration_hub.deref(),
1196                cx,
1197            ) {
1198                let color = Self::get_participant_color(remote_selection.participant_index, cx);
1199                add_cursor(remote_selection.selection.head(), color.cursor);
1200                if Some(remote_selection.peer_id) == editor.leader_peer_id {
1201                    skip_local = true;
1202                }
1203            }
1204        }
1205        // Local cursors
1206        if !skip_local {
1207            let color = cx.theme().players().local().cursor;
1208            editor.selections.disjoint.iter().for_each(|selection| {
1209                add_cursor(selection.head(), color);
1210            });
1211            if let Some(ref selection) = editor.selections.pending_anchor() {
1212                add_cursor(selection.head(), color);
1213            }
1214        }
1215        cursors
1216    }
1217
1218    fn layout_visible_cursors(
1219        &self,
1220        snapshot: &EditorSnapshot,
1221        selections: &[(PlayerColor, Vec<SelectionLayout>)],
1222        block_start_rows: &HashSet<DisplayRow>,
1223        visible_display_row_range: Range<DisplayRow>,
1224        line_layouts: &[LineWithInvisibles],
1225        text_hitbox: &Hitbox,
1226        content_origin: gpui::Point<Pixels>,
1227        scroll_position: gpui::Point<f32>,
1228        scroll_pixel_position: gpui::Point<Pixels>,
1229        line_height: Pixels,
1230        em_width: Pixels,
1231        em_advance: Pixels,
1232        autoscroll_containing_element: bool,
1233        window: &mut Window,
1234        cx: &mut App,
1235    ) -> Vec<CursorLayout> {
1236        let mut autoscroll_bounds = None;
1237        let cursor_layouts = self.editor.update(cx, |editor, cx| {
1238            let mut cursors = Vec::new();
1239
1240            let show_local_cursors = editor.show_local_cursors(window, cx);
1241
1242            for (player_color, selections) in selections {
1243                for selection in selections {
1244                    let cursor_position = selection.head;
1245
1246                    let in_range = visible_display_row_range.contains(&cursor_position.row());
1247                    if (selection.is_local && !show_local_cursors)
1248                        || !in_range
1249                        || block_start_rows.contains(&cursor_position.row())
1250                    {
1251                        continue;
1252                    }
1253
1254                    let cursor_row_layout = &line_layouts
1255                        [cursor_position.row().minus(visible_display_row_range.start) as usize];
1256                    let cursor_column = cursor_position.column() as usize;
1257
1258                    let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
1259                    let mut block_width =
1260                        cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x;
1261                    if block_width == Pixels::ZERO {
1262                        block_width = em_advance;
1263                    }
1264                    let block_text = if let CursorShape::Block = selection.cursor_shape {
1265                        snapshot
1266                            .grapheme_at(cursor_position)
1267                            .or_else(|| {
1268                                if cursor_column == 0 {
1269                                    snapshot.placeholder_text().and_then(|s| {
1270                                        s.graphemes(true).next().map(|s| s.to_string().into())
1271                                    })
1272                                } else {
1273                                    None
1274                                }
1275                            })
1276                            .and_then(|text| {
1277                                let len = text.len();
1278
1279                                let font = cursor_row_layout
1280                                    .font_id_for_index(cursor_column)
1281                                    .and_then(|cursor_font_id| {
1282                                        window.text_system().get_font_for_id(cursor_font_id)
1283                                    })
1284                                    .unwrap_or(self.style.text.font());
1285
1286                                // Invert the text color for the block cursor. Ensure that the text
1287                                // color is opaque enough to be visible against the background color.
1288                                //
1289                                // 0.75 is an arbitrary threshold to determine if the background color is
1290                                // opaque enough to use as a text color.
1291                                //
1292                                // TODO: In the future we should ensure themes have a `text_inverse` color.
1293                                let color = if cx.theme().colors().editor_background.a < 0.75 {
1294                                    match cx.theme().appearance {
1295                                        Appearance::Dark => Hsla::black(),
1296                                        Appearance::Light => Hsla::white(),
1297                                    }
1298                                } else {
1299                                    cx.theme().colors().editor_background
1300                                };
1301
1302                                window
1303                                    .text_system()
1304                                    .shape_line(
1305                                        text,
1306                                        cursor_row_layout.font_size,
1307                                        &[TextRun {
1308                                            len,
1309                                            font,
1310                                            color,
1311                                            background_color: None,
1312                                            strikethrough: None,
1313                                            underline: None,
1314                                        }],
1315                                    )
1316                                    .log_err()
1317                            })
1318                    } else {
1319                        None
1320                    };
1321
1322                    let x = cursor_character_x - scroll_pixel_position.x;
1323                    let y = (cursor_position.row().as_f32()
1324                        - scroll_pixel_position.y / line_height)
1325                        * line_height;
1326                    if selection.is_newest {
1327                        editor.pixel_position_of_newest_cursor = Some(point(
1328                            text_hitbox.origin.x + x + block_width / 2.,
1329                            text_hitbox.origin.y + y + line_height / 2.,
1330                        ));
1331
1332                        if autoscroll_containing_element {
1333                            let top = text_hitbox.origin.y
1334                                + (cursor_position.row().as_f32() - scroll_position.y - 3.).max(0.)
1335                                    * line_height;
1336                            let left = text_hitbox.origin.x
1337                                + (cursor_position.column() as f32 - scroll_position.x - 3.)
1338                                    .max(0.)
1339                                    * em_width;
1340
1341                            let bottom = text_hitbox.origin.y
1342                                + (cursor_position.row().as_f32() - scroll_position.y + 4.)
1343                                    * line_height;
1344                            let right = text_hitbox.origin.x
1345                                + (cursor_position.column() as f32 - scroll_position.x + 4.)
1346                                    * em_width;
1347
1348                            autoscroll_bounds =
1349                                Some(Bounds::from_corners(point(left, top), point(right, bottom)))
1350                        }
1351                    }
1352
1353                    let mut cursor = CursorLayout {
1354                        color: player_color.cursor,
1355                        block_width,
1356                        origin: point(x, y),
1357                        line_height,
1358                        shape: selection.cursor_shape,
1359                        block_text,
1360                        cursor_name: None,
1361                    };
1362                    let cursor_name = selection.user_name.clone().map(|name| CursorName {
1363                        string: name,
1364                        color: self.style.background,
1365                        is_top_row: cursor_position.row().0 == 0,
1366                    });
1367                    cursor.layout(content_origin, cursor_name, window, cx);
1368                    cursors.push(cursor);
1369                }
1370            }
1371
1372            cursors
1373        });
1374
1375        if let Some(bounds) = autoscroll_bounds {
1376            window.request_autoscroll(bounds);
1377        }
1378
1379        cursor_layouts
1380    }
1381
1382    fn layout_scrollbars(
1383        &self,
1384        snapshot: &EditorSnapshot,
1385        scrollbar_layout_information: ScrollbarLayoutInformation,
1386        content_offset: gpui::Point<Pixels>,
1387        scroll_position: gpui::Point<f32>,
1388        non_visible_cursors: bool,
1389        window: &mut Window,
1390        cx: &mut App,
1391    ) -> Option<EditorScrollbars> {
1392        if snapshot.mode != EditorMode::Full {
1393            return None;
1394        }
1395
1396        // If a drag took place after we started dragging the scrollbar,
1397        // cancel the scrollbar drag.
1398        if cx.has_active_drag() {
1399            self.editor.update(cx, |editor, cx| {
1400                editor.scroll_manager.reset_scrollbar_dragging_state(cx)
1401            });
1402        }
1403
1404        let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
1405        let show_scrollbars = self.editor.read(cx).show_scrollbars
1406            && match scrollbar_settings.show {
1407                ShowScrollbar::Auto => {
1408                    let editor = self.editor.read(cx);
1409                    let is_singleton = editor.is_singleton(cx);
1410                    // Git
1411                    (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_diff_hunks())
1412                    ||
1413                    // Buffer Search Results
1414                    (is_singleton && scrollbar_settings.search_results && editor.has_background_highlights::<BufferSearchHighlights>())
1415                    ||
1416                    // Selected Text Occurrences
1417                    (is_singleton && scrollbar_settings.selected_text && editor.has_background_highlights::<SelectedTextHighlight>())
1418                    ||
1419                    // Selected Symbol Occurrences
1420                    (is_singleton && scrollbar_settings.selected_symbol && (editor.has_background_highlights::<DocumentHighlightRead>() || editor.has_background_highlights::<DocumentHighlightWrite>()))
1421                    ||
1422                    // Diagnostics
1423                    (is_singleton && scrollbar_settings.diagnostics != ScrollbarDiagnostics::None && snapshot.buffer_snapshot.has_diagnostics())
1424                    ||
1425                    // Cursors out of sight
1426                    non_visible_cursors
1427                    ||
1428                    // Scrollmanager
1429                    editor.scroll_manager.scrollbars_visible()
1430                }
1431                ShowScrollbar::System => self.editor.read(cx).scroll_manager.scrollbars_visible(),
1432                ShowScrollbar::Always => true,
1433                ShowScrollbar::Never => return None,
1434            };
1435
1436        Some(EditorScrollbars::from_scrollbar_axes(
1437            scrollbar_settings.axes,
1438            &scrollbar_layout_information,
1439            content_offset,
1440            scroll_position,
1441            self.style.scrollbar_width,
1442            show_scrollbars,
1443            window,
1444        ))
1445    }
1446
1447    fn prepaint_crease_toggles(
1448        &self,
1449        crease_toggles: &mut [Option<AnyElement>],
1450        line_height: Pixels,
1451        gutter_dimensions: &GutterDimensions,
1452        gutter_settings: crate::editor_settings::Gutter,
1453        scroll_pixel_position: gpui::Point<Pixels>,
1454        gutter_hitbox: &Hitbox,
1455        window: &mut Window,
1456        cx: &mut App,
1457    ) {
1458        for (ix, crease_toggle) in crease_toggles.iter_mut().enumerate() {
1459            if let Some(crease_toggle) = crease_toggle {
1460                debug_assert!(gutter_settings.folds);
1461                let available_space = size(
1462                    AvailableSpace::MinContent,
1463                    AvailableSpace::Definite(line_height * 0.55),
1464                );
1465                let crease_toggle_size = crease_toggle.layout_as_root(available_space, window, cx);
1466
1467                let position = point(
1468                    gutter_dimensions.width - gutter_dimensions.right_padding,
1469                    ix as f32 * line_height - (scroll_pixel_position.y % line_height),
1470                );
1471                let centering_offset = point(
1472                    (gutter_dimensions.fold_area_width() - crease_toggle_size.width) / 2.,
1473                    (line_height - crease_toggle_size.height) / 2.,
1474                );
1475                let origin = gutter_hitbox.origin + position + centering_offset;
1476                crease_toggle.prepaint_as_root(origin, available_space, window, cx);
1477            }
1478        }
1479    }
1480
1481    fn prepaint_expand_toggles(
1482        &self,
1483        expand_toggles: &mut [Option<(AnyElement, gpui::Point<Pixels>)>],
1484        window: &mut Window,
1485        cx: &mut App,
1486    ) {
1487        for (expand_toggle, origin) in expand_toggles.iter_mut().flatten() {
1488            let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
1489            expand_toggle.layout_as_root(available_space, window, cx);
1490            expand_toggle.prepaint_as_root(*origin, available_space, window, cx);
1491        }
1492    }
1493
1494    fn prepaint_crease_trailers(
1495        &self,
1496        trailers: Vec<Option<AnyElement>>,
1497        lines: &[LineWithInvisibles],
1498        line_height: Pixels,
1499        content_origin: gpui::Point<Pixels>,
1500        scroll_pixel_position: gpui::Point<Pixels>,
1501        em_width: Pixels,
1502        window: &mut Window,
1503        cx: &mut App,
1504    ) -> Vec<Option<CreaseTrailerLayout>> {
1505        trailers
1506            .into_iter()
1507            .enumerate()
1508            .map(|(ix, element)| {
1509                let mut element = element?;
1510                let available_space = size(
1511                    AvailableSpace::MinContent,
1512                    AvailableSpace::Definite(line_height),
1513                );
1514                let size = element.layout_as_root(available_space, window, cx);
1515
1516                let line = &lines[ix];
1517                let padding = if line.width == Pixels::ZERO {
1518                    Pixels::ZERO
1519                } else {
1520                    4. * em_width
1521                };
1522                let position = point(
1523                    scroll_pixel_position.x + line.width + padding,
1524                    ix as f32 * line_height - (scroll_pixel_position.y % line_height),
1525                );
1526                let centering_offset = point(px(0.), (line_height - size.height) / 2.);
1527                let origin = content_origin + position + centering_offset;
1528                element.prepaint_as_root(origin, available_space, window, cx);
1529                Some(CreaseTrailerLayout {
1530                    element,
1531                    bounds: Bounds::new(origin, size),
1532                })
1533            })
1534            .collect()
1535    }
1536
1537    // Folds contained in a hunk are ignored apart from shrinking visual size
1538    // If a fold contains any hunks then that fold line is marked as modified
1539    fn layout_gutter_diff_hunks(
1540        &self,
1541        line_height: Pixels,
1542        gutter_hitbox: &Hitbox,
1543        display_rows: Range<DisplayRow>,
1544        snapshot: &EditorSnapshot,
1545        window: &mut Window,
1546        cx: &mut App,
1547    ) -> Vec<(DisplayDiffHunk, Option<Hitbox>)> {
1548        let folded_buffers = self.editor.read(cx).folded_buffers(cx);
1549        let mut display_hunks = snapshot
1550            .display_diff_hunks_for_rows(display_rows, folded_buffers)
1551            .map(|hunk| (hunk, None))
1552            .collect::<Vec<_>>();
1553        let git_gutter_setting = ProjectSettings::get_global(cx)
1554            .git
1555            .git_gutter
1556            .unwrap_or_default();
1557        if let GitGutterSetting::TrackedFiles = git_gutter_setting {
1558            for (hunk, hitbox) in &mut display_hunks {
1559                if matches!(hunk, DisplayDiffHunk::Unfolded { .. }) {
1560                    let hunk_bounds =
1561                        Self::diff_hunk_bounds(snapshot, line_height, gutter_hitbox.bounds, hunk);
1562                    *hitbox = Some(window.insert_hitbox(hunk_bounds, true));
1563                }
1564            }
1565        }
1566
1567        display_hunks
1568    }
1569
1570    fn layout_inline_diagnostics(
1571        &self,
1572        line_layouts: &[LineWithInvisibles],
1573        crease_trailers: &[Option<CreaseTrailerLayout>],
1574        content_origin: gpui::Point<Pixels>,
1575        scroll_pixel_position: gpui::Point<Pixels>,
1576        inline_completion_popover_origin: Option<gpui::Point<Pixels>>,
1577        start_row: DisplayRow,
1578        end_row: DisplayRow,
1579        line_height: Pixels,
1580        em_width: Pixels,
1581        style: &EditorStyle,
1582        window: &mut Window,
1583        cx: &mut App,
1584    ) -> HashMap<DisplayRow, AnyElement> {
1585        let max_severity = ProjectSettings::get_global(cx)
1586            .diagnostics
1587            .inline
1588            .max_severity
1589            .map_or(DiagnosticSeverity::HINT, |severity| match severity {
1590                project_settings::DiagnosticSeverity::Error => DiagnosticSeverity::ERROR,
1591                project_settings::DiagnosticSeverity::Warning => DiagnosticSeverity::WARNING,
1592                project_settings::DiagnosticSeverity::Info => DiagnosticSeverity::INFORMATION,
1593                project_settings::DiagnosticSeverity::Hint => DiagnosticSeverity::HINT,
1594            });
1595
1596        let active_diagnostics_group = self
1597            .editor
1598            .read(cx)
1599            .active_diagnostics
1600            .as_ref()
1601            .map(|active_diagnostics| active_diagnostics.group_id);
1602
1603        let diagnostics_by_rows = self.editor.update(cx, |editor, cx| {
1604            let snapshot = editor.snapshot(window, cx);
1605            editor
1606                .inline_diagnostics
1607                .iter()
1608                .filter(|(_, diagnostic)| diagnostic.severity <= max_severity)
1609                .filter(|(_, diagnostic)| match active_diagnostics_group {
1610                    Some(active_diagnostics_group) => {
1611                        // Active diagnostics are all shown in the editor already, no need to display them inline
1612                        diagnostic.group_id != active_diagnostics_group
1613                    }
1614                    None => true,
1615                })
1616                .map(|(point, diag)| (point.to_display_point(&snapshot), diag.clone()))
1617                .skip_while(|(point, _)| point.row() < start_row)
1618                .take_while(|(point, _)| point.row() < end_row)
1619                .fold(HashMap::default(), |mut acc, (point, diagnostic)| {
1620                    acc.entry(point.row())
1621                        .or_insert_with(Vec::new)
1622                        .push(diagnostic);
1623                    acc
1624                })
1625        });
1626
1627        if diagnostics_by_rows.is_empty() {
1628            return HashMap::default();
1629        }
1630
1631        let severity_to_color = |sev: &DiagnosticSeverity| match sev {
1632            &DiagnosticSeverity::ERROR => Color::Error,
1633            &DiagnosticSeverity::WARNING => Color::Warning,
1634            &DiagnosticSeverity::INFORMATION => Color::Info,
1635            &DiagnosticSeverity::HINT => Color::Hint,
1636            _ => Color::Error,
1637        };
1638
1639        let padding = ProjectSettings::get_global(cx).diagnostics.inline.padding as f32 * em_width;
1640        let min_x = ProjectSettings::get_global(cx)
1641            .diagnostics
1642            .inline
1643            .min_column as f32
1644            * em_width;
1645
1646        let mut elements = HashMap::default();
1647        for (row, mut diagnostics) in diagnostics_by_rows {
1648            diagnostics.sort_by_key(|diagnostic| {
1649                (
1650                    diagnostic.severity,
1651                    std::cmp::Reverse(diagnostic.is_primary),
1652                    diagnostic.start.row,
1653                    diagnostic.start.column,
1654                )
1655            });
1656
1657            let Some(diagnostic_to_render) = diagnostics
1658                .iter()
1659                .find(|diagnostic| diagnostic.is_primary)
1660                .or_else(|| diagnostics.first())
1661            else {
1662                continue;
1663            };
1664
1665            let pos_y = content_origin.y
1666                + line_height * (row.0 as f32 - scroll_pixel_position.y / line_height);
1667
1668            let window_ix = row.0.saturating_sub(start_row.0) as usize;
1669            let pos_x = {
1670                let crease_trailer_layout = &crease_trailers[window_ix];
1671                let line_layout = &line_layouts[window_ix];
1672
1673                let line_end = if let Some(crease_trailer) = crease_trailer_layout {
1674                    crease_trailer.bounds.right()
1675                } else {
1676                    content_origin.x - scroll_pixel_position.x + line_layout.width
1677                };
1678
1679                let padded_line = line_end + padding;
1680                let min_start = content_origin.x - scroll_pixel_position.x + min_x;
1681
1682                cmp::max(padded_line, min_start)
1683            };
1684
1685            let behind_inline_completion_popover = inline_completion_popover_origin
1686                .as_ref()
1687                .map_or(false, |inline_completion_popover_origin| {
1688                    (pos_y..pos_y + line_height).contains(&inline_completion_popover_origin.y)
1689                });
1690            let opacity = if behind_inline_completion_popover {
1691                0.5
1692            } else {
1693                1.0
1694            };
1695
1696            let mut element = h_flex()
1697                .id(("diagnostic", row.0))
1698                .h(line_height)
1699                .w_full()
1700                .px_1()
1701                .rounded_xs()
1702                .opacity(opacity)
1703                .bg(severity_to_color(&diagnostic_to_render.severity)
1704                    .color(cx)
1705                    .opacity(0.05))
1706                .text_color(severity_to_color(&diagnostic_to_render.severity).color(cx))
1707                .text_sm()
1708                .font_family(style.text.font().family)
1709                .child(diagnostic_to_render.message.clone())
1710                .into_any();
1711
1712            element.prepaint_as_root(point(pos_x, pos_y), AvailableSpace::min_size(), window, cx);
1713
1714            elements.insert(row, element);
1715        }
1716
1717        elements
1718    }
1719
1720    fn layout_inline_blame(
1721        &self,
1722        display_row: DisplayRow,
1723        row_info: &RowInfo,
1724        line_layout: &LineWithInvisibles,
1725        crease_trailer: Option<&CreaseTrailerLayout>,
1726        em_width: Pixels,
1727        content_origin: gpui::Point<Pixels>,
1728        scroll_pixel_position: gpui::Point<Pixels>,
1729        line_height: Pixels,
1730        window: &mut Window,
1731        cx: &mut App,
1732    ) -> Option<AnyElement> {
1733        if !self
1734            .editor
1735            .update(cx, |editor, cx| editor.render_git_blame_inline(window, cx))
1736        {
1737            return None;
1738        }
1739
1740        let editor = self.editor.read(cx);
1741        let blame = editor.blame.clone()?;
1742        let padding = {
1743            const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 6.;
1744            const INLINE_ACCEPT_SUGGESTION_EM_WIDTHS: f32 = 14.;
1745
1746            let mut padding = INLINE_BLAME_PADDING_EM_WIDTHS;
1747
1748            if let Some(inline_completion) = editor.active_inline_completion.as_ref() {
1749                match &inline_completion.completion {
1750                    InlineCompletion::Edit {
1751                        display_mode: EditDisplayMode::TabAccept,
1752                        ..
1753                    } => padding += INLINE_ACCEPT_SUGGESTION_EM_WIDTHS,
1754                    _ => {}
1755                }
1756            }
1757
1758            padding * em_width
1759        };
1760
1761        let workspace = editor.workspace()?.downgrade();
1762        let blame_entry = blame
1763            .update(cx, |blame, cx| {
1764                blame.blame_for_rows(&[*row_info], cx).next()
1765            })
1766            .flatten()?;
1767
1768        let mut element = render_inline_blame_entry(
1769            self.editor.clone(),
1770            workspace,
1771            &blame,
1772            blame_entry,
1773            &self.style,
1774            cx,
1775        )?;
1776
1777        let start_y = content_origin.y
1778            + line_height * (display_row.as_f32() - scroll_pixel_position.y / line_height);
1779
1780        let start_x = {
1781            let line_end = if let Some(crease_trailer) = crease_trailer {
1782                crease_trailer.bounds.right()
1783            } else {
1784                content_origin.x - scroll_pixel_position.x + line_layout.width
1785            };
1786
1787            let padded_line_end = line_end + padding;
1788
1789            let min_column_in_pixels = ProjectSettings::get_global(cx)
1790                .git
1791                .inline_blame
1792                .and_then(|settings| settings.min_column)
1793                .map(|col| self.column_pixels(col as usize, window, cx))
1794                .unwrap_or(px(0.));
1795            let min_start = content_origin.x - scroll_pixel_position.x + min_column_in_pixels;
1796
1797            cmp::max(padded_line_end, min_start)
1798        };
1799
1800        let absolute_offset = point(start_x, start_y);
1801        element.prepaint_as_root(absolute_offset, AvailableSpace::min_size(), window, cx);
1802
1803        Some(element)
1804    }
1805
1806    fn layout_blame_entries(
1807        &self,
1808        buffer_rows: &[RowInfo],
1809        em_width: Pixels,
1810        scroll_position: gpui::Point<f32>,
1811        line_height: Pixels,
1812        gutter_hitbox: &Hitbox,
1813        max_width: Option<Pixels>,
1814        window: &mut Window,
1815        cx: &mut App,
1816    ) -> Option<Vec<AnyElement>> {
1817        if !self
1818            .editor
1819            .update(cx, |editor, cx| editor.render_git_blame_gutter(cx))
1820        {
1821            return None;
1822        }
1823
1824        let blame = self.editor.read(cx).blame.clone()?;
1825        let workspace = self.editor.read(cx).workspace()?;
1826        let blamed_rows: Vec<_> = blame.update(cx, |blame, cx| {
1827            blame.blame_for_rows(buffer_rows, cx).collect()
1828        });
1829
1830        let width = if let Some(max_width) = max_width {
1831            AvailableSpace::Definite(max_width)
1832        } else {
1833            AvailableSpace::MaxContent
1834        };
1835        let scroll_top = scroll_position.y * line_height;
1836        let start_x = em_width;
1837
1838        let mut last_used_color: Option<(PlayerColor, Oid)> = None;
1839        let blame_renderer = cx.global::<GlobalBlameRenderer>().0.clone();
1840
1841        let shaped_lines = blamed_rows
1842            .into_iter()
1843            .enumerate()
1844            .flat_map(|(ix, blame_entry)| {
1845                let mut element = render_blame_entry(
1846                    ix,
1847                    &blame,
1848                    blame_entry?,
1849                    &self.style,
1850                    &mut last_used_color,
1851                    self.editor.clone(),
1852                    workspace.clone(),
1853                    blame_renderer.clone(),
1854                    cx,
1855                )?;
1856
1857                let start_y = ix as f32 * line_height - (scroll_top % line_height);
1858                let absolute_offset = gutter_hitbox.origin + point(start_x, start_y);
1859
1860                element.prepaint_as_root(
1861                    absolute_offset,
1862                    size(width, AvailableSpace::MinContent),
1863                    window,
1864                    cx,
1865                );
1866
1867                Some(element)
1868            })
1869            .collect();
1870
1871        Some(shaped_lines)
1872    }
1873
1874    fn layout_indent_guides(
1875        &self,
1876        content_origin: gpui::Point<Pixels>,
1877        text_origin: gpui::Point<Pixels>,
1878        visible_buffer_range: Range<MultiBufferRow>,
1879        scroll_pixel_position: gpui::Point<Pixels>,
1880        line_height: Pixels,
1881        snapshot: &DisplaySnapshot,
1882        window: &mut Window,
1883        cx: &mut App,
1884    ) -> Option<Vec<IndentGuideLayout>> {
1885        let indent_guides = self.editor.update(cx, |editor, cx| {
1886            editor.indent_guides(visible_buffer_range, snapshot, cx)
1887        })?;
1888
1889        let active_indent_guide_indices = self.editor.update(cx, |editor, cx| {
1890            editor
1891                .find_active_indent_guide_indices(&indent_guides, snapshot, window, cx)
1892                .unwrap_or_default()
1893        });
1894
1895        Some(
1896            indent_guides
1897                .into_iter()
1898                .enumerate()
1899                .filter_map(|(i, indent_guide)| {
1900                    let single_indent_width =
1901                        self.column_pixels(indent_guide.tab_size as usize, window, cx);
1902                    let total_width = single_indent_width * indent_guide.depth as f32;
1903                    let start_x = content_origin.x + total_width - scroll_pixel_position.x;
1904                    if start_x >= text_origin.x {
1905                        let (offset_y, length) = Self::calculate_indent_guide_bounds(
1906                            indent_guide.start_row..indent_guide.end_row,
1907                            line_height,
1908                            snapshot,
1909                        );
1910
1911                        let start_y = content_origin.y + offset_y - scroll_pixel_position.y;
1912
1913                        Some(IndentGuideLayout {
1914                            origin: point(start_x, start_y),
1915                            length,
1916                            single_indent_width,
1917                            depth: indent_guide.depth,
1918                            active: active_indent_guide_indices.contains(&i),
1919                            settings: indent_guide.settings,
1920                        })
1921                    } else {
1922                        None
1923                    }
1924                })
1925                .collect(),
1926        )
1927    }
1928
1929    fn calculate_indent_guide_bounds(
1930        row_range: Range<MultiBufferRow>,
1931        line_height: Pixels,
1932        snapshot: &DisplaySnapshot,
1933    ) -> (gpui::Pixels, gpui::Pixels) {
1934        let start_point = Point::new(row_range.start.0, 0);
1935        let end_point = Point::new(row_range.end.0, 0);
1936
1937        let row_range = start_point.to_display_point(snapshot).row()
1938            ..end_point.to_display_point(snapshot).row();
1939
1940        let mut prev_line = start_point;
1941        prev_line.row = prev_line.row.saturating_sub(1);
1942        let prev_line = prev_line.to_display_point(snapshot).row();
1943
1944        let mut cons_line = end_point;
1945        cons_line.row += 1;
1946        let cons_line = cons_line.to_display_point(snapshot).row();
1947
1948        let mut offset_y = row_range.start.0 as f32 * line_height;
1949        let mut length = (cons_line.0.saturating_sub(row_range.start.0)) as f32 * line_height;
1950
1951        // If we are at the end of the buffer, ensure that the indent guide extends to the end of the line.
1952        if row_range.end == cons_line {
1953            length += line_height;
1954        }
1955
1956        // If there is a block (e.g. diagnostic) in between the start of the indent guide and the line above,
1957        // we want to extend the indent guide to the start of the block.
1958        let mut block_height = 0;
1959        let mut block_offset = 0;
1960        let mut found_excerpt_header = false;
1961        for (_, block) in snapshot.blocks_in_range(prev_line..row_range.start) {
1962            if matches!(block, Block::ExcerptBoundary { .. }) {
1963                found_excerpt_header = true;
1964                break;
1965            }
1966            block_offset += block.height();
1967            block_height += block.height();
1968        }
1969        if !found_excerpt_header {
1970            offset_y -= block_offset as f32 * line_height;
1971            length += block_height as f32 * line_height;
1972        }
1973
1974        // If there is a block (e.g. diagnostic) at the end of an multibuffer excerpt,
1975        // we want to ensure that the indent guide stops before the excerpt header.
1976        let mut block_height = 0;
1977        let mut found_excerpt_header = false;
1978        for (_, block) in snapshot.blocks_in_range(row_range.end..cons_line) {
1979            if matches!(block, Block::ExcerptBoundary { .. }) {
1980                found_excerpt_header = true;
1981            }
1982            block_height += block.height();
1983        }
1984        if found_excerpt_header {
1985            length -= block_height as f32 * line_height;
1986        }
1987
1988        (offset_y, length)
1989    }
1990
1991    fn layout_breakpoints(
1992        &self,
1993        line_height: Pixels,
1994        range: Range<DisplayRow>,
1995        scroll_pixel_position: gpui::Point<Pixels>,
1996        gutter_dimensions: &GutterDimensions,
1997        gutter_hitbox: &Hitbox,
1998        display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
1999        snapshot: &EditorSnapshot,
2000        breakpoints: HashMap<DisplayRow, (Anchor, Breakpoint)>,
2001        row_infos: &[RowInfo],
2002        window: &mut Window,
2003        cx: &mut App,
2004    ) -> Vec<AnyElement> {
2005        self.editor.update(cx, |editor, cx| {
2006            breakpoints
2007                .into_iter()
2008                .filter_map(|(display_row, (text_anchor, bp))| {
2009                    if row_infos
2010                        .get((display_row.0.saturating_sub(range.start.0)) as usize)
2011                        .is_some_and(|row_info| {
2012                            row_info.expand_info.is_some()
2013                                || row_info
2014                                    .diff_status
2015                                    .is_some_and(|status| status.is_deleted())
2016                        })
2017                    {
2018                        return None;
2019                    }
2020
2021                    if range.start > display_row || range.end < display_row {
2022                        return None;
2023                    }
2024
2025                    let row =
2026                        MultiBufferRow(DisplayPoint::new(display_row, 0).to_point(&snapshot).row);
2027                    if snapshot.is_line_folded(row) {
2028                        return None;
2029                    }
2030
2031                    let button = editor.render_breakpoint(text_anchor, display_row, &bp, cx);
2032
2033                    let button = prepaint_gutter_button(
2034                        button,
2035                        display_row,
2036                        line_height,
2037                        gutter_dimensions,
2038                        scroll_pixel_position,
2039                        gutter_hitbox,
2040                        display_hunks,
2041                        window,
2042                        cx,
2043                    );
2044                    Some(button)
2045                })
2046                .collect_vec()
2047        })
2048    }
2049
2050    #[allow(clippy::too_many_arguments)]
2051    fn layout_run_indicators(
2052        &self,
2053        line_height: Pixels,
2054        range: Range<DisplayRow>,
2055        row_infos: &[RowInfo],
2056        scroll_pixel_position: gpui::Point<Pixels>,
2057        gutter_dimensions: &GutterDimensions,
2058        gutter_hitbox: &Hitbox,
2059        display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
2060        snapshot: &EditorSnapshot,
2061        breakpoints: &mut HashMap<DisplayRow, (Anchor, Breakpoint)>,
2062        window: &mut Window,
2063        cx: &mut App,
2064    ) -> Vec<AnyElement> {
2065        self.editor.update(cx, |editor, cx| {
2066            let active_task_indicator_row =
2067                if let Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
2068                    deployed_from_indicator,
2069                    actions,
2070                    ..
2071                })) = editor.context_menu.borrow().as_ref()
2072                {
2073                    actions
2074                        .tasks
2075                        .as_ref()
2076                        .map(|tasks| tasks.position.to_display_point(snapshot).row())
2077                        .or(*deployed_from_indicator)
2078                } else {
2079                    None
2080                };
2081
2082            let offset_range_start =
2083                snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left);
2084
2085            let offset_range_end =
2086                snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
2087
2088            editor
2089                .tasks
2090                .iter()
2091                .filter_map(|(_, tasks)| {
2092                    let multibuffer_point = tasks.offset.to_point(&snapshot.buffer_snapshot);
2093                    if multibuffer_point < offset_range_start
2094                        || multibuffer_point > offset_range_end
2095                    {
2096                        return None;
2097                    }
2098                    let multibuffer_row = MultiBufferRow(multibuffer_point.row);
2099                    let buffer_folded = snapshot
2100                        .buffer_snapshot
2101                        .buffer_line_for_row(multibuffer_row)
2102                        .map(|(buffer_snapshot, _)| buffer_snapshot.remote_id())
2103                        .map(|buffer_id| editor.is_buffer_folded(buffer_id, cx))
2104                        .unwrap_or(false);
2105                    if buffer_folded {
2106                        return None;
2107                    }
2108
2109                    if snapshot.is_line_folded(multibuffer_row) {
2110                        // Skip folded indicators, unless it's the starting line of a fold.
2111                        if multibuffer_row
2112                            .0
2113                            .checked_sub(1)
2114                            .map_or(false, |previous_row| {
2115                                snapshot.is_line_folded(MultiBufferRow(previous_row))
2116                            })
2117                        {
2118                            return None;
2119                        }
2120                    }
2121
2122                    let display_row = multibuffer_point.to_display_point(snapshot).row();
2123                    if row_infos
2124                        .get((display_row - range.start).0 as usize)
2125                        .is_some_and(|row_info| row_info.expand_info.is_some())
2126                    {
2127                        return None;
2128                    }
2129
2130                    let button = editor.render_run_indicator(
2131                        &self.style,
2132                        Some(display_row) == active_task_indicator_row,
2133                        display_row,
2134                        breakpoints.remove(&display_row),
2135                        cx,
2136                    );
2137
2138                    let button = prepaint_gutter_button(
2139                        button,
2140                        display_row,
2141                        line_height,
2142                        gutter_dimensions,
2143                        scroll_pixel_position,
2144                        gutter_hitbox,
2145                        display_hunks,
2146                        window,
2147                        cx,
2148                    );
2149                    Some(button)
2150                })
2151                .collect_vec()
2152        })
2153    }
2154
2155    fn layout_expand_toggles(
2156        &self,
2157        gutter_hitbox: &Hitbox,
2158        gutter_dimensions: GutterDimensions,
2159        em_width: Pixels,
2160        line_height: Pixels,
2161        scroll_position: gpui::Point<f32>,
2162        buffer_rows: &[RowInfo],
2163        window: &mut Window,
2164        cx: &mut App,
2165    ) -> Vec<Option<(AnyElement, gpui::Point<Pixels>)>> {
2166        let editor_font_size = self.style.text.font_size.to_pixels(window.rem_size()) * 1.2;
2167
2168        let scroll_top = scroll_position.y * line_height;
2169
2170        let max_line_number_length = self
2171            .editor
2172            .read(cx)
2173            .buffer()
2174            .read(cx)
2175            .snapshot(cx)
2176            .widest_line_number()
2177            .ilog10()
2178            + 1;
2179
2180        let elements = buffer_rows
2181            .into_iter()
2182            .enumerate()
2183            .map(|(ix, row_info)| {
2184                let ExpandInfo {
2185                    excerpt_id,
2186                    direction,
2187                } = row_info.expand_info?;
2188
2189                let icon_name = match direction {
2190                    ExpandExcerptDirection::Up => IconName::ExpandUp,
2191                    ExpandExcerptDirection::Down => IconName::ExpandDown,
2192                    ExpandExcerptDirection::UpAndDown => IconName::ExpandVertical,
2193                };
2194
2195                let git_gutter_width = Self::gutter_strip_width(line_height);
2196                let available_width = gutter_dimensions.left_padding - git_gutter_width;
2197
2198                let editor = self.editor.clone();
2199                let is_wide = max_line_number_length >= MIN_LINE_NUMBER_DIGITS
2200                    && row_info
2201                        .buffer_row
2202                        .is_some_and(|row| (row + 1).ilog10() + 1 == max_line_number_length)
2203                    || gutter_dimensions.right_padding == px(0.);
2204
2205                let width = if is_wide {
2206                    available_width - px(2.)
2207                } else {
2208                    available_width + em_width - px(2.)
2209                };
2210
2211                let toggle = IconButton::new(("expand", ix), icon_name)
2212                    .icon_color(Color::Custom(cx.theme().colors().editor_line_number))
2213                    .selected_icon_color(Color::Custom(cx.theme().colors().editor_foreground))
2214                    .icon_size(IconSize::Custom(rems(editor_font_size / window.rem_size())))
2215                    .width(width.into())
2216                    .on_click(move |_, window, cx| {
2217                        editor.update(cx, |editor, cx| {
2218                            editor.expand_excerpt(excerpt_id, direction, window, cx);
2219                        });
2220                    })
2221                    .tooltip(Tooltip::for_action_title(
2222                        "Expand Excerpt",
2223                        &crate::actions::ExpandExcerpts::default(),
2224                    ))
2225                    .into_any_element();
2226
2227                let position = point(
2228                    git_gutter_width + px(1.),
2229                    ix as f32 * line_height - (scroll_top % line_height) + px(1.),
2230                );
2231                let origin = gutter_hitbox.origin + position;
2232
2233                Some((toggle, origin))
2234            })
2235            .collect();
2236
2237        elements
2238    }
2239
2240    fn layout_code_actions_indicator(
2241        &self,
2242        line_height: Pixels,
2243        newest_selection_head: DisplayPoint,
2244        scroll_pixel_position: gpui::Point<Pixels>,
2245        gutter_dimensions: &GutterDimensions,
2246        gutter_hitbox: &Hitbox,
2247        breakpoint_points: &mut HashMap<DisplayRow, (Anchor, Breakpoint)>,
2248        display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
2249        window: &mut Window,
2250        cx: &mut App,
2251    ) -> Option<AnyElement> {
2252        let mut active = false;
2253        let mut button = None;
2254        let row = newest_selection_head.row();
2255        self.editor.update(cx, |editor, cx| {
2256            if let Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
2257                deployed_from_indicator,
2258                ..
2259            })) = editor.context_menu.borrow().as_ref()
2260            {
2261                active = deployed_from_indicator.map_or(true, |indicator_row| indicator_row == row);
2262            };
2263
2264            let breakpoint = breakpoint_points.get(&row);
2265            button = editor.render_code_actions_indicator(&self.style, row, active, breakpoint, cx);
2266        });
2267
2268        let button = button?;
2269        breakpoint_points.remove(&row);
2270
2271        let button = prepaint_gutter_button(
2272            button,
2273            row,
2274            line_height,
2275            gutter_dimensions,
2276            scroll_pixel_position,
2277            gutter_hitbox,
2278            display_hunks,
2279            window,
2280            cx,
2281        );
2282
2283        Some(button)
2284    }
2285
2286    fn get_participant_color(participant_index: Option<ParticipantIndex>, cx: &App) -> PlayerColor {
2287        if let Some(index) = participant_index {
2288            cx.theme().players().color_for_participant(index.0)
2289        } else {
2290            cx.theme().players().absent()
2291        }
2292    }
2293
2294    fn calculate_relative_line_numbers(
2295        &self,
2296        snapshot: &EditorSnapshot,
2297        rows: &Range<DisplayRow>,
2298        relative_to: Option<DisplayRow>,
2299    ) -> HashMap<DisplayRow, DisplayRowDelta> {
2300        let mut relative_rows: HashMap<DisplayRow, DisplayRowDelta> = Default::default();
2301        let Some(relative_to) = relative_to else {
2302            return relative_rows;
2303        };
2304
2305        let start = rows.start.min(relative_to);
2306        let end = rows.end.max(relative_to);
2307
2308        let buffer_rows = snapshot
2309            .row_infos(start)
2310            .take(1 + end.minus(start) as usize)
2311            .collect::<Vec<_>>();
2312
2313        let head_idx = relative_to.minus(start);
2314        let mut delta = 1;
2315        let mut i = head_idx + 1;
2316        while i < buffer_rows.len() as u32 {
2317            if buffer_rows[i as usize].buffer_row.is_some() {
2318                if rows.contains(&DisplayRow(i + start.0)) {
2319                    relative_rows.insert(DisplayRow(i + start.0), delta);
2320                }
2321                delta += 1;
2322            }
2323            i += 1;
2324        }
2325        delta = 1;
2326        i = head_idx.min(buffer_rows.len() as u32 - 1);
2327        while i > 0 && buffer_rows[i as usize].buffer_row.is_none() {
2328            i -= 1;
2329        }
2330
2331        while i > 0 {
2332            i -= 1;
2333            if buffer_rows[i as usize].buffer_row.is_some() {
2334                if rows.contains(&DisplayRow(i + start.0)) {
2335                    relative_rows.insert(DisplayRow(i + start.0), delta);
2336                }
2337                delta += 1;
2338            }
2339        }
2340
2341        relative_rows
2342    }
2343
2344    fn layout_line_numbers(
2345        &self,
2346        gutter_hitbox: Option<&Hitbox>,
2347        gutter_dimensions: GutterDimensions,
2348        line_height: Pixels,
2349        scroll_position: gpui::Point<f32>,
2350        rows: Range<DisplayRow>,
2351        buffer_rows: &[RowInfo],
2352        active_rows: &BTreeMap<DisplayRow, LineHighlightSpec>,
2353        newest_selection_head: Option<DisplayPoint>,
2354        snapshot: &EditorSnapshot,
2355        window: &mut Window,
2356        cx: &mut App,
2357    ) -> Arc<HashMap<MultiBufferRow, LineNumberLayout>> {
2358        let include_line_numbers = snapshot.show_line_numbers.unwrap_or_else(|| {
2359            EditorSettings::get_global(cx).gutter.line_numbers && snapshot.mode == EditorMode::Full
2360        });
2361        if !include_line_numbers {
2362            return Arc::default();
2363        }
2364
2365        let (newest_selection_head, is_relative) = self.editor.update(cx, |editor, cx| {
2366            let newest_selection_head = newest_selection_head.unwrap_or_else(|| {
2367                let newest = editor.selections.newest::<Point>(cx);
2368                SelectionLayout::new(
2369                    newest,
2370                    editor.selections.line_mode,
2371                    editor.cursor_shape,
2372                    &snapshot.display_snapshot,
2373                    true,
2374                    true,
2375                    None,
2376                )
2377                .head
2378            });
2379            let is_relative = editor.should_use_relative_line_numbers(cx);
2380            (newest_selection_head, is_relative)
2381        });
2382
2383        let relative_to = if is_relative {
2384            Some(newest_selection_head.row())
2385        } else {
2386            None
2387        };
2388        let relative_rows = self.calculate_relative_line_numbers(snapshot, &rows, relative_to);
2389        let mut line_number = String::new();
2390        let line_numbers = buffer_rows
2391            .into_iter()
2392            .enumerate()
2393            .flat_map(|(ix, row_info)| {
2394                let display_row = DisplayRow(rows.start.0 + ix as u32);
2395                line_number.clear();
2396                let non_relative_number = row_info.buffer_row? + 1;
2397                let number = relative_rows
2398                    .get(&display_row)
2399                    .unwrap_or(&non_relative_number);
2400                write!(&mut line_number, "{number}").unwrap();
2401                if row_info
2402                    .diff_status
2403                    .is_some_and(|status| status.is_deleted())
2404                {
2405                    return None;
2406                }
2407
2408                let color = active_rows
2409                    .get(&display_row)
2410                    .map(|spec| {
2411                        if spec.breakpoint {
2412                            cx.theme().colors().debugger_accent
2413                        } else {
2414                            cx.theme().colors().editor_active_line_number
2415                        }
2416                    })
2417                    .unwrap_or_else(|| cx.theme().colors().editor_line_number);
2418                let shaped_line = self
2419                    .shape_line_number(SharedString::from(&line_number), color, window)
2420                    .log_err()?;
2421                let scroll_top = scroll_position.y * line_height;
2422                let line_origin = gutter_hitbox.map(|hitbox| {
2423                    hitbox.origin
2424                        + point(
2425                            hitbox.size.width - shaped_line.width - gutter_dimensions.right_padding,
2426                            ix as f32 * line_height - (scroll_top % line_height),
2427                        )
2428                });
2429
2430                #[cfg(not(test))]
2431                let hitbox = line_origin.map(|line_origin| {
2432                    window.insert_hitbox(
2433                        Bounds::new(line_origin, size(shaped_line.width, line_height)),
2434                        false,
2435                    )
2436                });
2437                #[cfg(test)]
2438                let hitbox = {
2439                    let _ = line_origin;
2440                    None
2441                };
2442
2443                let multi_buffer_row = DisplayPoint::new(display_row, 0).to_point(snapshot).row;
2444                let multi_buffer_row = MultiBufferRow(multi_buffer_row);
2445                let line_number = LineNumberLayout {
2446                    shaped_line,
2447                    hitbox,
2448                };
2449                Some((multi_buffer_row, line_number))
2450            })
2451            .collect();
2452        Arc::new(line_numbers)
2453    }
2454
2455    fn layout_crease_toggles(
2456        &self,
2457        rows: Range<DisplayRow>,
2458        row_infos: &[RowInfo],
2459        active_rows: &BTreeMap<DisplayRow, LineHighlightSpec>,
2460        snapshot: &EditorSnapshot,
2461        window: &mut Window,
2462        cx: &mut App,
2463    ) -> Vec<Option<AnyElement>> {
2464        let include_fold_statuses = EditorSettings::get_global(cx).gutter.folds
2465            && snapshot.mode == EditorMode::Full
2466            && self.editor.read(cx).is_singleton(cx);
2467        if include_fold_statuses {
2468            row_infos
2469                .into_iter()
2470                .enumerate()
2471                .map(|(ix, info)| {
2472                    if info.expand_info.is_some() {
2473                        return None;
2474                    }
2475                    let row = info.multibuffer_row?;
2476                    let display_row = DisplayRow(rows.start.0 + ix as u32);
2477                    let active = active_rows.contains_key(&display_row);
2478
2479                    snapshot.render_crease_toggle(row, active, self.editor.clone(), window, cx)
2480                })
2481                .collect()
2482        } else {
2483            Vec::new()
2484        }
2485    }
2486
2487    fn layout_crease_trailers(
2488        &self,
2489        buffer_rows: impl IntoIterator<Item = RowInfo>,
2490        snapshot: &EditorSnapshot,
2491        window: &mut Window,
2492        cx: &mut App,
2493    ) -> Vec<Option<AnyElement>> {
2494        buffer_rows
2495            .into_iter()
2496            .map(|row_info| {
2497                if row_info.expand_info.is_some() {
2498                    return None;
2499                }
2500                if let Some(row) = row_info.multibuffer_row {
2501                    snapshot.render_crease_trailer(row, window, cx)
2502                } else {
2503                    None
2504                }
2505            })
2506            .collect()
2507    }
2508
2509    fn layout_lines(
2510        rows: Range<DisplayRow>,
2511        snapshot: &EditorSnapshot,
2512        style: &EditorStyle,
2513        editor_width: Pixels,
2514        is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
2515        window: &mut Window,
2516        cx: &mut App,
2517    ) -> Vec<LineWithInvisibles> {
2518        if rows.start >= rows.end {
2519            return Vec::new();
2520        }
2521
2522        // Show the placeholder when the editor is empty
2523        if snapshot.is_empty() {
2524            let font_size = style.text.font_size.to_pixels(window.rem_size());
2525            let placeholder_color = cx.theme().colors().text_placeholder;
2526            let placeholder_text = snapshot.placeholder_text();
2527
2528            let placeholder_lines = placeholder_text
2529                .as_ref()
2530                .map_or("", AsRef::as_ref)
2531                .split('\n')
2532                .skip(rows.start.0 as usize)
2533                .chain(iter::repeat(""))
2534                .take(rows.len());
2535            placeholder_lines
2536                .filter_map(move |line| {
2537                    let run = TextRun {
2538                        len: line.len(),
2539                        font: style.text.font(),
2540                        color: placeholder_color,
2541                        background_color: None,
2542                        underline: Default::default(),
2543                        strikethrough: None,
2544                    };
2545                    window
2546                        .text_system()
2547                        .shape_line(line.to_string().into(), font_size, &[run])
2548                        .log_err()
2549                })
2550                .map(|line| LineWithInvisibles {
2551                    width: line.width,
2552                    len: line.len,
2553                    fragments: smallvec![LineFragment::Text(line)],
2554                    invisibles: Vec::new(),
2555                    font_size,
2556                })
2557                .collect()
2558        } else {
2559            let chunks = snapshot.highlighted_chunks(rows.clone(), true, style);
2560            LineWithInvisibles::from_chunks(
2561                chunks,
2562                &style,
2563                MAX_LINE_LEN,
2564                rows.len(),
2565                snapshot.mode,
2566                editor_width,
2567                is_row_soft_wrapped,
2568                window,
2569                cx,
2570            )
2571        }
2572    }
2573
2574    fn prepaint_lines(
2575        &self,
2576        start_row: DisplayRow,
2577        line_layouts: &mut [LineWithInvisibles],
2578        line_height: Pixels,
2579        scroll_pixel_position: gpui::Point<Pixels>,
2580        content_origin: gpui::Point<Pixels>,
2581        window: &mut Window,
2582        cx: &mut App,
2583    ) -> SmallVec<[AnyElement; 1]> {
2584        let mut line_elements = SmallVec::new();
2585        for (ix, line) in line_layouts.iter_mut().enumerate() {
2586            let row = start_row + DisplayRow(ix as u32);
2587            line.prepaint(
2588                line_height,
2589                scroll_pixel_position,
2590                row,
2591                content_origin,
2592                &mut line_elements,
2593                window,
2594                cx,
2595            );
2596        }
2597        line_elements
2598    }
2599
2600    fn render_block(
2601        &self,
2602        block: &Block,
2603        available_width: AvailableSpace,
2604        block_id: BlockId,
2605        block_row_start: DisplayRow,
2606        snapshot: &EditorSnapshot,
2607        text_x: Pixels,
2608        rows: &Range<DisplayRow>,
2609        line_layouts: &[LineWithInvisibles],
2610        gutter_dimensions: &GutterDimensions,
2611        line_height: Pixels,
2612        em_width: Pixels,
2613        text_hitbox: &Hitbox,
2614        editor_width: Pixels,
2615        scroll_width: &mut Pixels,
2616        resized_blocks: &mut HashMap<CustomBlockId, u32>,
2617        selections: &[Selection<Point>],
2618        selected_buffer_ids: &Vec<BufferId>,
2619        is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
2620        sticky_header_excerpt_id: Option<ExcerptId>,
2621        window: &mut Window,
2622        cx: &mut App,
2623    ) -> (AnyElement, Size<Pixels>) {
2624        let mut element = match block {
2625            Block::Custom(block) => {
2626                let block_start = block.start().to_point(&snapshot.buffer_snapshot);
2627                let block_end = block.end().to_point(&snapshot.buffer_snapshot);
2628                let align_to = block_start.to_display_point(snapshot);
2629                let anchor_x = text_x
2630                    + if rows.contains(&align_to.row()) {
2631                        line_layouts[align_to.row().minus(rows.start) as usize]
2632                            .x_for_index(align_to.column() as usize)
2633                    } else {
2634                        layout_line(
2635                            align_to.row(),
2636                            snapshot,
2637                            &self.style,
2638                            editor_width,
2639                            is_row_soft_wrapped,
2640                            window,
2641                            cx,
2642                        )
2643                        .x_for_index(align_to.column() as usize)
2644                    };
2645
2646                let selected = selections
2647                    .binary_search_by(|selection| {
2648                        if selection.end <= block_start {
2649                            Ordering::Less
2650                        } else if selection.start >= block_end {
2651                            Ordering::Greater
2652                        } else {
2653                            Ordering::Equal
2654                        }
2655                    })
2656                    .is_ok();
2657
2658                div()
2659                    .size_full()
2660                    .child(block.render(&mut BlockContext {
2661                        window,
2662                        app: cx,
2663                        anchor_x,
2664                        gutter_dimensions,
2665                        line_height,
2666                        em_width,
2667                        block_id,
2668                        selected,
2669                        max_width: text_hitbox.size.width.max(*scroll_width),
2670                        editor_style: &self.style,
2671                    }))
2672                    .into_any()
2673            }
2674
2675            Block::FoldedBuffer {
2676                first_excerpt,
2677                height,
2678                ..
2679            } => {
2680                let selected = selected_buffer_ids.contains(&first_excerpt.buffer_id);
2681                let result = v_flex().id(block_id).w_full();
2682
2683                let jump_data = header_jump_data(snapshot, block_row_start, *height, first_excerpt);
2684                result
2685                    .child(self.render_buffer_header(
2686                        first_excerpt,
2687                        true,
2688                        selected,
2689                        false,
2690                        jump_data,
2691                        window,
2692                        cx,
2693                    ))
2694                    .into_any_element()
2695            }
2696
2697            Block::ExcerptBoundary {
2698                excerpt,
2699                height,
2700                starts_new_buffer,
2701                ..
2702            } => {
2703                let color = cx.theme().colors().clone();
2704                let mut result = v_flex().id(block_id).w_full();
2705
2706                let jump_data = header_jump_data(snapshot, block_row_start, *height, excerpt);
2707
2708                if *starts_new_buffer {
2709                    if sticky_header_excerpt_id != Some(excerpt.id) {
2710                        let selected = selected_buffer_ids.contains(&excerpt.buffer_id);
2711
2712                        result = result.child(self.render_buffer_header(
2713                            excerpt, false, selected, false, jump_data, window, cx,
2714                        ));
2715                    } else {
2716                        result =
2717                            result.child(div().h(FILE_HEADER_HEIGHT as f32 * window.line_height()));
2718                    }
2719                } else {
2720                    result = result.child(
2721                        h_flex().relative().child(
2722                            div()
2723                                .top(line_height / 2.)
2724                                .absolute()
2725                                .w_full()
2726                                .h_px()
2727                                .bg(color.border_variant),
2728                        ),
2729                    );
2730                };
2731
2732                result.into_any()
2733            }
2734        };
2735
2736        // Discover the element's content height, then round up to the nearest multiple of line height.
2737        let preliminary_size = element.layout_as_root(
2738            size(available_width, AvailableSpace::MinContent),
2739            window,
2740            cx,
2741        );
2742        let quantized_height = (preliminary_size.height / line_height).ceil() * line_height;
2743        let final_size = if preliminary_size.height == quantized_height {
2744            preliminary_size
2745        } else {
2746            element.layout_as_root(size(available_width, quantized_height.into()), window, cx)
2747        };
2748
2749        if let BlockId::Custom(custom_block_id) = block_id {
2750            if block.height() > 0 {
2751                let element_height_in_lines =
2752                    ((final_size.height / line_height).ceil() as u32).max(1);
2753                if element_height_in_lines != block.height() {
2754                    resized_blocks.insert(custom_block_id, element_height_in_lines);
2755                }
2756            }
2757        }
2758
2759        (element, final_size)
2760    }
2761
2762    fn render_buffer_header(
2763        &self,
2764        for_excerpt: &ExcerptInfo,
2765        is_folded: bool,
2766        is_selected: bool,
2767        is_sticky: bool,
2768        jump_data: JumpData,
2769        window: &mut Window,
2770        cx: &mut App,
2771    ) -> Div {
2772        let editor = self.editor.read(cx);
2773        let file_status = editor
2774            .buffer
2775            .read(cx)
2776            .all_diff_hunks_expanded()
2777            .then(|| {
2778                editor
2779                    .project
2780                    .as_ref()?
2781                    .read(cx)
2782                    .status_for_buffer_id(for_excerpt.buffer_id, cx)
2783            })
2784            .flatten();
2785
2786        let include_root = editor
2787            .project
2788            .as_ref()
2789            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
2790            .unwrap_or_default();
2791        let can_open_excerpts = Editor::can_open_excerpts_in_file(for_excerpt.buffer.file());
2792        let path = for_excerpt.buffer.resolve_file_path(cx, include_root);
2793        let filename = path
2794            .as_ref()
2795            .and_then(|path| Some(path.file_name()?.to_string_lossy().to_string()));
2796        let parent_path = path.as_ref().and_then(|path| {
2797            Some(path.parent()?.to_string_lossy().to_string() + std::path::MAIN_SEPARATOR_STR)
2798        });
2799        let focus_handle = editor.focus_handle(cx);
2800        let colors = cx.theme().colors();
2801
2802        div()
2803            .p_1()
2804            .w_full()
2805            .h(FILE_HEADER_HEIGHT as f32 * window.line_height())
2806            .child(
2807                h_flex()
2808                    .size_full()
2809                    .gap_2()
2810                    .flex_basis(Length::Definite(DefiniteLength::Fraction(0.667)))
2811                    .pl_0p5()
2812                    .pr_5()
2813                    .rounded_sm()
2814                    .when(is_sticky, |el| el.shadow_md())
2815                    .border_1()
2816                    .map(|div| {
2817                        let border_color = if is_selected
2818                            && is_folded
2819                            && focus_handle.contains_focused(window, cx)
2820                        {
2821                            colors.border_focused
2822                        } else {
2823                            colors.border
2824                        };
2825                        div.border_color(border_color)
2826                    })
2827                    .bg(colors.editor_subheader_background)
2828                    .hover(|style| style.bg(colors.element_hover))
2829                    .map(|header| {
2830                        let editor = self.editor.clone();
2831                        let buffer_id = for_excerpt.buffer_id;
2832                        let toggle_chevron_icon =
2833                            FileIcons::get_chevron_icon(!is_folded, cx).map(Icon::from_path);
2834                        header.child(
2835                            div()
2836                                .hover(|style| style.bg(colors.element_selected))
2837                                .rounded_xs()
2838                                .child(
2839                                    ButtonLike::new("toggle-buffer-fold")
2840                                        .style(ui::ButtonStyle::Transparent)
2841                                        .height(px(28.).into())
2842                                        .width(px(28.).into())
2843                                        .children(toggle_chevron_icon)
2844                                        .tooltip({
2845                                            let focus_handle = focus_handle.clone();
2846                                            move |window, cx| {
2847                                                Tooltip::for_action_in(
2848                                                    "Toggle Excerpt Fold",
2849                                                    &ToggleFold,
2850                                                    &focus_handle,
2851                                                    window,
2852                                                    cx,
2853                                                )
2854                                            }
2855                                        })
2856                                        .on_click(move |_, _, cx| {
2857                                            if is_folded {
2858                                                editor.update(cx, |editor, cx| {
2859                                                    editor.unfold_buffer(buffer_id, cx);
2860                                                });
2861                                            } else {
2862                                                editor.update(cx, |editor, cx| {
2863                                                    editor.fold_buffer(buffer_id, cx);
2864                                                });
2865                                            }
2866                                        }),
2867                                ),
2868                        )
2869                    })
2870                    .children(
2871                        editor
2872                            .addons
2873                            .values()
2874                            .filter_map(|addon| {
2875                                addon.render_buffer_header_controls(for_excerpt, window, cx)
2876                            })
2877                            .take(1),
2878                    )
2879                    .child(
2880                        h_flex()
2881                            .cursor_pointer()
2882                            .id("path header block")
2883                            .size_full()
2884                            .justify_between()
2885                            .child(
2886                                h_flex()
2887                                    .gap_2()
2888                                    .child(
2889                                        Label::new(
2890                                            filename
2891                                                .map(SharedString::from)
2892                                                .unwrap_or_else(|| "untitled".into()),
2893                                        )
2894                                        .single_line()
2895                                        .when_some(
2896                                            file_status,
2897                                            |el, status| {
2898                                                el.color(if status.is_conflicted() {
2899                                                    Color::Conflict
2900                                                } else if status.is_modified() {
2901                                                    Color::Modified
2902                                                } else if status.is_deleted() {
2903                                                    Color::Disabled
2904                                                } else {
2905                                                    Color::Created
2906                                                })
2907                                                .when(status.is_deleted(), |el| el.strikethrough())
2908                                            },
2909                                        ),
2910                                    )
2911                                    .when_some(parent_path, |then, path| {
2912                                        then.child(div().child(path).text_color(
2913                                            if file_status.is_some_and(FileStatus::is_deleted) {
2914                                                colors.text_disabled
2915                                            } else {
2916                                                colors.text_muted
2917                                            },
2918                                        ))
2919                                    }),
2920                            )
2921                            .when(can_open_excerpts && is_selected && path.is_some(), |el| {
2922                                el.child(
2923                                    h_flex()
2924                                        .id("jump-to-file-button")
2925                                        .gap_2p5()
2926                                        .child(Label::new("Jump To File"))
2927                                        .children(
2928                                            KeyBinding::for_action_in(
2929                                                &OpenExcerpts,
2930                                                &focus_handle,
2931                                                window,
2932                                                cx,
2933                                            )
2934                                            .map(|binding| binding.into_any_element()),
2935                                        ),
2936                                )
2937                            })
2938                            .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
2939                            .on_click(window.listener_for(&self.editor, {
2940                                move |editor, e: &ClickEvent, window, cx| {
2941                                    editor.open_excerpts_common(
2942                                        Some(jump_data.clone()),
2943                                        e.down.modifiers.secondary(),
2944                                        window,
2945                                        cx,
2946                                    );
2947                                }
2948                            })),
2949                    ),
2950            )
2951    }
2952
2953    fn render_blocks(
2954        &self,
2955        rows: Range<DisplayRow>,
2956        snapshot: &EditorSnapshot,
2957        hitbox: &Hitbox,
2958        text_hitbox: &Hitbox,
2959        editor_width: Pixels,
2960        scroll_width: &mut Pixels,
2961        gutter_dimensions: &GutterDimensions,
2962        em_width: Pixels,
2963        text_x: Pixels,
2964        line_height: Pixels,
2965        line_layouts: &[LineWithInvisibles],
2966        selections: &[Selection<Point>],
2967        selected_buffer_ids: &Vec<BufferId>,
2968        is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
2969        sticky_header_excerpt_id: Option<ExcerptId>,
2970        window: &mut Window,
2971        cx: &mut App,
2972    ) -> Result<Vec<BlockLayout>, HashMap<CustomBlockId, u32>> {
2973        let (fixed_blocks, non_fixed_blocks) = snapshot
2974            .blocks_in_range(rows.clone())
2975            .partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed);
2976
2977        let mut focused_block = self
2978            .editor
2979            .update(cx, |editor, _| editor.take_focused_block());
2980        let mut fixed_block_max_width = Pixels::ZERO;
2981        let mut blocks = Vec::new();
2982        let mut resized_blocks = HashMap::default();
2983
2984        for (row, block) in fixed_blocks {
2985            let block_id = block.id();
2986
2987            if focused_block.as_ref().map_or(false, |b| b.id == block_id) {
2988                focused_block = None;
2989            }
2990
2991            let (element, element_size) = self.render_block(
2992                block,
2993                AvailableSpace::MinContent,
2994                block_id,
2995                row,
2996                snapshot,
2997                text_x,
2998                &rows,
2999                line_layouts,
3000                gutter_dimensions,
3001                line_height,
3002                em_width,
3003                text_hitbox,
3004                editor_width,
3005                scroll_width,
3006                &mut resized_blocks,
3007                selections,
3008                selected_buffer_ids,
3009                is_row_soft_wrapped,
3010                sticky_header_excerpt_id,
3011                window,
3012                cx,
3013            );
3014            fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
3015            blocks.push(BlockLayout {
3016                id: block_id,
3017                row: Some(row),
3018                element,
3019                available_space: size(AvailableSpace::MinContent, element_size.height.into()),
3020                style: BlockStyle::Fixed,
3021                is_buffer_header: block.is_buffer_header(),
3022            });
3023        }
3024
3025        for (row, block) in non_fixed_blocks {
3026            let style = block.style();
3027            let width = match style {
3028                BlockStyle::Sticky => hitbox.size.width,
3029                BlockStyle::Flex => hitbox
3030                    .size
3031                    .width
3032                    .max(fixed_block_max_width)
3033                    .max(gutter_dimensions.width + *scroll_width),
3034                BlockStyle::Fixed => unreachable!(),
3035            };
3036            let block_id = block.id();
3037
3038            if focused_block.as_ref().map_or(false, |b| b.id == block_id) {
3039                focused_block = None;
3040            }
3041
3042            let (element, element_size) = self.render_block(
3043                block,
3044                width.into(),
3045                block_id,
3046                row,
3047                snapshot,
3048                text_x,
3049                &rows,
3050                line_layouts,
3051                gutter_dimensions,
3052                line_height,
3053                em_width,
3054                text_hitbox,
3055                editor_width,
3056                scroll_width,
3057                &mut resized_blocks,
3058                selections,
3059                selected_buffer_ids,
3060                is_row_soft_wrapped,
3061                sticky_header_excerpt_id,
3062                window,
3063                cx,
3064            );
3065
3066            blocks.push(BlockLayout {
3067                id: block_id,
3068                row: Some(row),
3069                element,
3070                available_space: size(width.into(), element_size.height.into()),
3071                style,
3072                is_buffer_header: block.is_buffer_header(),
3073            });
3074        }
3075
3076        if let Some(focused_block) = focused_block {
3077            if let Some(focus_handle) = focused_block.focus_handle.upgrade() {
3078                if focus_handle.is_focused(window) {
3079                    if let Some(block) = snapshot.block_for_id(focused_block.id) {
3080                        let style = block.style();
3081                        let width = match style {
3082                            BlockStyle::Fixed => AvailableSpace::MinContent,
3083                            BlockStyle::Flex => AvailableSpace::Definite(
3084                                hitbox
3085                                    .size
3086                                    .width
3087                                    .max(fixed_block_max_width)
3088                                    .max(gutter_dimensions.width + *scroll_width),
3089                            ),
3090                            BlockStyle::Sticky => AvailableSpace::Definite(hitbox.size.width),
3091                        };
3092
3093                        let (element, element_size) = self.render_block(
3094                            &block,
3095                            width,
3096                            focused_block.id,
3097                            rows.end,
3098                            snapshot,
3099                            text_x,
3100                            &rows,
3101                            line_layouts,
3102                            gutter_dimensions,
3103                            line_height,
3104                            em_width,
3105                            text_hitbox,
3106                            editor_width,
3107                            scroll_width,
3108                            &mut resized_blocks,
3109                            selections,
3110                            selected_buffer_ids,
3111                            is_row_soft_wrapped,
3112                            sticky_header_excerpt_id,
3113                            window,
3114                            cx,
3115                        );
3116
3117                        blocks.push(BlockLayout {
3118                            id: block.id(),
3119                            row: None,
3120                            element,
3121                            available_space: size(width, element_size.height.into()),
3122                            style,
3123                            is_buffer_header: block.is_buffer_header(),
3124                        });
3125                    }
3126                }
3127            }
3128        }
3129
3130        if resized_blocks.is_empty() {
3131            *scroll_width = (*scroll_width).max(fixed_block_max_width - gutter_dimensions.width);
3132            Ok(blocks)
3133        } else {
3134            Err(resized_blocks)
3135        }
3136    }
3137
3138    /// Returns true if any of the blocks changed size since the previous frame. This will trigger
3139    /// a restart of rendering for the editor based on the new sizes.
3140    fn layout_blocks(
3141        &self,
3142        blocks: &mut Vec<BlockLayout>,
3143        block_starts: &mut HashSet<DisplayRow>,
3144        hitbox: &Hitbox,
3145        line_height: Pixels,
3146        scroll_pixel_position: gpui::Point<Pixels>,
3147        window: &mut Window,
3148        cx: &mut App,
3149    ) {
3150        for block in blocks {
3151            let mut origin = if let Some(row) = block.row {
3152                block_starts.insert(row);
3153                hitbox.origin
3154                    + point(
3155                        Pixels::ZERO,
3156                        row.as_f32() * line_height - scroll_pixel_position.y,
3157                    )
3158            } else {
3159                // Position the block outside the visible area
3160                hitbox.origin + point(Pixels::ZERO, hitbox.size.height)
3161            };
3162
3163            if !matches!(block.style, BlockStyle::Sticky) {
3164                origin += point(-scroll_pixel_position.x, Pixels::ZERO);
3165            }
3166
3167            let focus_handle =
3168                block
3169                    .element
3170                    .prepaint_as_root(origin, block.available_space, window, cx);
3171
3172            if let Some(focus_handle) = focus_handle {
3173                self.editor.update(cx, |editor, _cx| {
3174                    editor.set_focused_block(FocusedBlock {
3175                        id: block.id,
3176                        focus_handle: focus_handle.downgrade(),
3177                    });
3178                });
3179            }
3180        }
3181    }
3182
3183    fn layout_sticky_buffer_header(
3184        &self,
3185        StickyHeaderExcerpt { excerpt }: StickyHeaderExcerpt<'_>,
3186        scroll_position: f32,
3187        line_height: Pixels,
3188        snapshot: &EditorSnapshot,
3189        hitbox: &Hitbox,
3190        selected_buffer_ids: &Vec<BufferId>,
3191        blocks: &[BlockLayout],
3192        window: &mut Window,
3193        cx: &mut App,
3194    ) -> AnyElement {
3195        let jump_data = header_jump_data(
3196            snapshot,
3197            DisplayRow(scroll_position as u32),
3198            FILE_HEADER_HEIGHT + MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
3199            excerpt,
3200        );
3201
3202        let editor_bg_color = cx.theme().colors().editor_background;
3203
3204        let selected = selected_buffer_ids.contains(&excerpt.buffer_id);
3205
3206        let mut header = v_flex()
3207            .relative()
3208            .child(
3209                div()
3210                    .w(hitbox.bounds.size.width)
3211                    .h(FILE_HEADER_HEIGHT as f32 * line_height)
3212                    .bg(linear_gradient(
3213                        0.,
3214                        linear_color_stop(editor_bg_color.opacity(0.), 0.),
3215                        linear_color_stop(editor_bg_color, 0.6),
3216                    ))
3217                    .absolute()
3218                    .top_0(),
3219            )
3220            .child(
3221                self.render_buffer_header(excerpt, false, selected, true, jump_data, window, cx)
3222                    .into_any_element(),
3223            )
3224            .into_any_element();
3225
3226        let mut origin = hitbox.origin;
3227        // Move floating header up to avoid colliding with the next buffer header.
3228        for block in blocks.iter() {
3229            if !block.is_buffer_header {
3230                continue;
3231            }
3232
3233            let Some(display_row) = block.row.filter(|row| row.0 > scroll_position as u32) else {
3234                continue;
3235            };
3236
3237            let max_row = display_row.0.saturating_sub(FILE_HEADER_HEIGHT);
3238            let offset = scroll_position - max_row as f32;
3239
3240            if offset > 0.0 {
3241                origin.y -= offset * line_height;
3242            }
3243            break;
3244        }
3245
3246        let size = size(
3247            AvailableSpace::Definite(hitbox.size.width),
3248            AvailableSpace::MinContent,
3249        );
3250
3251        header.prepaint_as_root(origin, size, window, cx);
3252
3253        header
3254    }
3255
3256    fn layout_cursor_popovers(
3257        &self,
3258        line_height: Pixels,
3259        text_hitbox: &Hitbox,
3260        content_origin: gpui::Point<Pixels>,
3261        start_row: DisplayRow,
3262        scroll_pixel_position: gpui::Point<Pixels>,
3263        line_layouts: &[LineWithInvisibles],
3264        cursor: DisplayPoint,
3265        cursor_point: Point,
3266        style: &EditorStyle,
3267        window: &mut Window,
3268        cx: &mut App,
3269    ) {
3270        let mut min_menu_height = Pixels::ZERO;
3271        let mut max_menu_height = Pixels::ZERO;
3272        let mut height_above_menu = Pixels::ZERO;
3273        let height_below_menu = Pixels::ZERO;
3274        let mut edit_prediction_popover_visible = false;
3275        let mut context_menu_visible = false;
3276        let context_menu_placement;
3277
3278        {
3279            let editor = self.editor.read(cx);
3280            if editor
3281                .edit_prediction_visible_in_cursor_popover(editor.has_active_inline_completion())
3282            {
3283                height_above_menu +=
3284                    editor.edit_prediction_cursor_popover_height() + POPOVER_Y_PADDING;
3285                edit_prediction_popover_visible = true;
3286            }
3287
3288            if editor.context_menu_visible() {
3289                if let Some(crate::ContextMenuOrigin::Cursor) = editor.context_menu_origin() {
3290                    let (min_height_in_lines, max_height_in_lines) = editor
3291                        .context_menu_options
3292                        .as_ref()
3293                        .map_or((3, 12), |options| {
3294                            (options.min_entries_visible, options.max_entries_visible)
3295                        });
3296
3297                    min_menu_height += line_height * min_height_in_lines as f32 + POPOVER_Y_PADDING;
3298                    max_menu_height += line_height * max_height_in_lines as f32 + POPOVER_Y_PADDING;
3299                    context_menu_visible = true;
3300                }
3301            }
3302            context_menu_placement = editor
3303                .context_menu_options
3304                .as_ref()
3305                .and_then(|options| options.placement.clone());
3306        }
3307
3308        let visible = edit_prediction_popover_visible || context_menu_visible;
3309        if !visible {
3310            return;
3311        }
3312
3313        let cursor_row_layout = &line_layouts[cursor.row().minus(start_row) as usize];
3314        let target_position = content_origin
3315            + gpui::Point {
3316                x: cmp::max(
3317                    px(0.),
3318                    cursor_row_layout.x_for_index(cursor.column() as usize)
3319                        - scroll_pixel_position.x,
3320                ),
3321                y: cmp::max(
3322                    px(0.),
3323                    cursor.row().next_row().as_f32() * line_height - scroll_pixel_position.y,
3324                ),
3325            };
3326
3327        let viewport_bounds =
3328            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
3329                right: -Self::SCROLLBAR_WIDTH - MENU_GAP,
3330                ..Default::default()
3331            });
3332
3333        let min_height = height_above_menu + min_menu_height + height_below_menu;
3334        let max_height = height_above_menu + max_menu_height + height_below_menu;
3335        let Some((laid_out_popovers, y_flipped)) = self.layout_popovers_above_or_below_line(
3336            target_position,
3337            line_height,
3338            min_height,
3339            max_height,
3340            context_menu_placement,
3341            text_hitbox,
3342            viewport_bounds,
3343            window,
3344            cx,
3345            |height, max_width_for_stable_x, y_flipped, window, cx| {
3346                // First layout the menu to get its size - others can be at least this wide.
3347                let context_menu = if context_menu_visible {
3348                    let menu_height = if y_flipped {
3349                        height - height_below_menu
3350                    } else {
3351                        height - height_above_menu
3352                    };
3353                    let mut element = self
3354                        .render_context_menu(line_height, menu_height, y_flipped, window, cx)
3355                        .expect("Visible context menu should always render.");
3356                    let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
3357                    Some((CursorPopoverType::CodeContextMenu, element, size))
3358                } else {
3359                    None
3360                };
3361                let min_width = context_menu
3362                    .as_ref()
3363                    .map_or(px(0.), |(_, _, size)| size.width);
3364                let max_width = max_width_for_stable_x.max(
3365                    context_menu
3366                        .as_ref()
3367                        .map_or(px(0.), |(_, _, size)| size.width),
3368                );
3369
3370                let edit_prediction = if edit_prediction_popover_visible {
3371                    self.editor.update(cx, move |editor, cx| {
3372                        let accept_binding = editor.accept_edit_prediction_keybind(window, cx);
3373                        let mut element = editor.render_edit_prediction_cursor_popover(
3374                            min_width,
3375                            max_width,
3376                            cursor_point,
3377                            style,
3378                            accept_binding.keystroke(),
3379                            window,
3380                            cx,
3381                        )?;
3382                        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
3383                        Some((CursorPopoverType::EditPrediction, element, size))
3384                    })
3385                } else {
3386                    None
3387                };
3388                vec![edit_prediction, context_menu]
3389                    .into_iter()
3390                    .flatten()
3391                    .collect::<Vec<_>>()
3392            },
3393        ) else {
3394            return;
3395        };
3396
3397        let Some((menu_ix, (_, menu_bounds))) = laid_out_popovers
3398            .iter()
3399            .find_position(|(x, _)| matches!(x, CursorPopoverType::CodeContextMenu))
3400        else {
3401            return;
3402        };
3403        let last_ix = laid_out_popovers.len() - 1;
3404        let menu_is_last = menu_ix == last_ix;
3405        let first_popover_bounds = laid_out_popovers[0].1;
3406        let last_popover_bounds = laid_out_popovers[last_ix].1;
3407
3408        // Bounds to layout the aside around. When y_flipped, the aside goes either above or to the
3409        // right, and otherwise it goes below or to the right.
3410        let mut target_bounds = Bounds::from_corners(
3411            first_popover_bounds.origin,
3412            last_popover_bounds.bottom_right(),
3413        );
3414        target_bounds.size.width = menu_bounds.size.width;
3415
3416        // Like `target_bounds`, but with the max height it could occupy. Choosing an aside position
3417        // based on this is preferred for layout stability.
3418        let mut max_target_bounds = target_bounds;
3419        max_target_bounds.size.height = max_height;
3420        if y_flipped {
3421            max_target_bounds.origin.y -= max_height - target_bounds.size.height;
3422        }
3423
3424        // Add spacing around `target_bounds` and `max_target_bounds`.
3425        let mut extend_amount = Edges::all(MENU_GAP);
3426        if y_flipped {
3427            extend_amount.bottom = line_height;
3428        } else {
3429            extend_amount.top = line_height;
3430        }
3431        let target_bounds = target_bounds.extend(extend_amount);
3432        let max_target_bounds = max_target_bounds.extend(extend_amount);
3433
3434        let must_place_above_or_below =
3435            if y_flipped && !menu_is_last && menu_bounds.size.height < max_menu_height {
3436                laid_out_popovers[menu_ix + 1..]
3437                    .iter()
3438                    .any(|(_, popover_bounds)| popover_bounds.size.width > menu_bounds.size.width)
3439            } else {
3440                false
3441            };
3442
3443        self.layout_context_menu_aside(
3444            y_flipped,
3445            *menu_bounds,
3446            target_bounds,
3447            max_target_bounds,
3448            max_menu_height,
3449            must_place_above_or_below,
3450            text_hitbox,
3451            viewport_bounds,
3452            window,
3453            cx,
3454        );
3455    }
3456
3457    fn layout_gutter_menu(
3458        &self,
3459        line_height: Pixels,
3460        text_hitbox: &Hitbox,
3461        content_origin: gpui::Point<Pixels>,
3462        scroll_pixel_position: gpui::Point<Pixels>,
3463        gutter_overshoot: Pixels,
3464        window: &mut Window,
3465        cx: &mut App,
3466    ) {
3467        let editor = self.editor.read(cx);
3468        if !editor.context_menu_visible() {
3469            return;
3470        }
3471        let Some(crate::ContextMenuOrigin::GutterIndicator(gutter_row)) =
3472            editor.context_menu_origin()
3473        else {
3474            return;
3475        };
3476        // Context menu was spawned via a click on a gutter. Ensure it's a bit closer to the
3477        // indicator than just a plain first column of the text field.
3478        let target_position = content_origin
3479            + gpui::Point {
3480                x: -gutter_overshoot,
3481                y: gutter_row.next_row().as_f32() * line_height - scroll_pixel_position.y,
3482            };
3483
3484        let (min_height_in_lines, max_height_in_lines) = editor
3485            .context_menu_options
3486            .as_ref()
3487            .map_or((3, 12), |options| {
3488                (options.min_entries_visible, options.max_entries_visible)
3489            });
3490
3491        let min_height = line_height * min_height_in_lines as f32 + POPOVER_Y_PADDING;
3492        let max_height = line_height * max_height_in_lines as f32 + POPOVER_Y_PADDING;
3493        let viewport_bounds =
3494            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
3495                right: -Self::SCROLLBAR_WIDTH - MENU_GAP,
3496                ..Default::default()
3497            });
3498        self.layout_popovers_above_or_below_line(
3499            target_position,
3500            line_height,
3501            min_height,
3502            max_height,
3503            editor
3504                .context_menu_options
3505                .as_ref()
3506                .and_then(|options| options.placement.clone()),
3507            text_hitbox,
3508            viewport_bounds,
3509            window,
3510            cx,
3511            move |height, _max_width_for_stable_x, y_flipped, window, cx| {
3512                let mut element = self
3513                    .render_context_menu(line_height, height, y_flipped, window, cx)
3514                    .expect("Visible context menu should always render.");
3515                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
3516                vec![(CursorPopoverType::CodeContextMenu, element, size)]
3517            },
3518        );
3519    }
3520
3521    fn layout_popovers_above_or_below_line(
3522        &self,
3523        target_position: gpui::Point<Pixels>,
3524        line_height: Pixels,
3525        min_height: Pixels,
3526        max_height: Pixels,
3527        placement: Option<ContextMenuPlacement>,
3528        text_hitbox: &Hitbox,
3529        viewport_bounds: Bounds<Pixels>,
3530        window: &mut Window,
3531        cx: &mut App,
3532        make_sized_popovers: impl FnOnce(
3533            Pixels,
3534            Pixels,
3535            bool,
3536            &mut Window,
3537            &mut App,
3538        ) -> Vec<(CursorPopoverType, AnyElement, Size<Pixels>)>,
3539    ) -> Option<(Vec<(CursorPopoverType, Bounds<Pixels>)>, bool)> {
3540        let text_style = TextStyleRefinement {
3541            line_height: Some(DefiniteLength::Fraction(
3542                BufferLineHeight::Comfortable.value(),
3543            )),
3544            ..Default::default()
3545        };
3546        window.with_text_style(Some(text_style), |window| {
3547            // If the max height won't fit below and there is more space above, put it above the line.
3548            let bottom_y_when_flipped = target_position.y - line_height;
3549            let available_above = bottom_y_when_flipped - text_hitbox.top();
3550            let available_below = text_hitbox.bottom() - target_position.y;
3551            let y_overflows_below = max_height > available_below;
3552            let mut y_flipped = match placement {
3553                Some(ContextMenuPlacement::Above) => true,
3554                Some(ContextMenuPlacement::Below) => false,
3555                None => y_overflows_below && available_above > available_below,
3556            };
3557            let mut height = cmp::min(
3558                max_height,
3559                if y_flipped {
3560                    available_above
3561                } else {
3562                    available_below
3563                },
3564            );
3565
3566            // If the min height doesn't fit within text bounds, instead fit within the window.
3567            if height < min_height {
3568                let available_above = bottom_y_when_flipped;
3569                let available_below = viewport_bounds.bottom() - target_position.y;
3570                let (y_flipped_override, height_override) = match placement {
3571                    Some(ContextMenuPlacement::Above) => {
3572                        (true, cmp::min(available_above, min_height))
3573                    }
3574                    Some(ContextMenuPlacement::Below) => {
3575                        (false, cmp::min(available_below, min_height))
3576                    }
3577                    None => {
3578                        if available_below > min_height {
3579                            (false, min_height)
3580                        } else if available_above > min_height {
3581                            (true, min_height)
3582                        } else if available_above > available_below {
3583                            (true, available_above)
3584                        } else {
3585                            (false, available_below)
3586                        }
3587                    }
3588                };
3589                y_flipped = y_flipped_override;
3590                height = height_override;
3591            }
3592
3593            let max_width_for_stable_x = viewport_bounds.right() - target_position.x;
3594
3595            // TODO: Use viewport_bounds.width as a max width so that it doesn't get clipped on the left
3596            // for very narrow windows.
3597            let popovers =
3598                make_sized_popovers(height, max_width_for_stable_x, y_flipped, window, cx);
3599            if popovers.is_empty() {
3600                return None;
3601            }
3602
3603            let max_width = popovers
3604                .iter()
3605                .map(|(_, _, size)| size.width)
3606                .max()
3607                .unwrap_or_default();
3608
3609            let mut current_position = gpui::Point {
3610                // Snap the right edge of the list to the right edge of the window if its horizontal bounds
3611                // overflow. Include space for the scrollbar.
3612                x: target_position
3613                    .x
3614                    .min((viewport_bounds.right() - max_width).max(Pixels::ZERO)),
3615                y: if y_flipped {
3616                    bottom_y_when_flipped
3617                } else {
3618                    target_position.y
3619                },
3620            };
3621
3622            let mut laid_out_popovers = popovers
3623                .into_iter()
3624                .map(|(popover_type, element, size)| {
3625                    if y_flipped {
3626                        current_position.y -= size.height;
3627                    }
3628                    let position = current_position;
3629                    window.defer_draw(element, current_position, 1);
3630                    if !y_flipped {
3631                        current_position.y += size.height + MENU_GAP;
3632                    } else {
3633                        current_position.y -= MENU_GAP;
3634                    }
3635                    (popover_type, Bounds::new(position, size))
3636                })
3637                .collect::<Vec<_>>();
3638
3639            if y_flipped {
3640                laid_out_popovers.reverse();
3641            }
3642
3643            Some((laid_out_popovers, y_flipped))
3644        })
3645    }
3646
3647    fn layout_context_menu_aside(
3648        &self,
3649        y_flipped: bool,
3650        menu_bounds: Bounds<Pixels>,
3651        target_bounds: Bounds<Pixels>,
3652        max_target_bounds: Bounds<Pixels>,
3653        max_height: Pixels,
3654        must_place_above_or_below: bool,
3655        text_hitbox: &Hitbox,
3656        viewport_bounds: Bounds<Pixels>,
3657        window: &mut Window,
3658        cx: &mut App,
3659    ) {
3660        let available_within_viewport = target_bounds.space_within(&viewport_bounds);
3661        let positioned_aside = if available_within_viewport.right >= MENU_ASIDE_MIN_WIDTH
3662            && !must_place_above_or_below
3663        {
3664            let max_width = cmp::min(
3665                available_within_viewport.right - px(1.),
3666                MENU_ASIDE_MAX_WIDTH,
3667            );
3668            let Some(mut aside) = self.render_context_menu_aside(
3669                size(max_width, max_height - POPOVER_Y_PADDING),
3670                window,
3671                cx,
3672            ) else {
3673                return;
3674            };
3675            aside.layout_as_root(AvailableSpace::min_size(), window, cx);
3676            let right_position = point(target_bounds.right(), menu_bounds.origin.y);
3677            Some((aside, right_position))
3678        } else {
3679            let max_size = size(
3680                // TODO(mgsloan): Once the menu is bounded by viewport width the bound on viewport
3681                // won't be needed here.
3682                cmp::min(
3683                    cmp::max(menu_bounds.size.width - px(2.), MENU_ASIDE_MIN_WIDTH),
3684                    viewport_bounds.right(),
3685                ),
3686                cmp::min(
3687                    max_height,
3688                    cmp::max(
3689                        available_within_viewport.top,
3690                        available_within_viewport.bottom,
3691                    ),
3692                ) - POPOVER_Y_PADDING,
3693            );
3694            let Some(mut aside) = self.render_context_menu_aside(max_size, window, cx) else {
3695                return;
3696            };
3697            let actual_size = aside.layout_as_root(AvailableSpace::min_size(), window, cx);
3698
3699            let top_position = point(
3700                menu_bounds.origin.x,
3701                target_bounds.top() - actual_size.height,
3702            );
3703            let bottom_position = point(menu_bounds.origin.x, target_bounds.bottom());
3704
3705            let fit_within = |available: Edges<Pixels>, wanted: Size<Pixels>| {
3706                // Prefer to fit on the same side of the line as the menu, then on the other side of
3707                // the line.
3708                if !y_flipped && wanted.height < available.bottom {
3709                    Some(bottom_position)
3710                } else if !y_flipped && wanted.height < available.top {
3711                    Some(top_position)
3712                } else if y_flipped && wanted.height < available.top {
3713                    Some(top_position)
3714                } else if y_flipped && wanted.height < available.bottom {
3715                    Some(bottom_position)
3716                } else {
3717                    None
3718                }
3719            };
3720
3721            // Prefer choosing a direction using max sizes rather than actual size for stability.
3722            let available_within_text = max_target_bounds.space_within(&text_hitbox.bounds);
3723            let wanted = size(MENU_ASIDE_MAX_WIDTH, max_height);
3724            let aside_position = fit_within(available_within_text, wanted)
3725                // Fallback: fit max size in window.
3726                .or_else(|| fit_within(max_target_bounds.space_within(&viewport_bounds), wanted))
3727                // Fallback: fit actual size in window.
3728                .or_else(|| fit_within(available_within_viewport, actual_size));
3729
3730            aside_position.map(|position| (aside, position))
3731        };
3732
3733        // Skip drawing if it doesn't fit anywhere.
3734        if let Some((aside, position)) = positioned_aside {
3735            window.defer_draw(aside, position, 2);
3736        }
3737    }
3738
3739    fn render_context_menu(
3740        &self,
3741        line_height: Pixels,
3742        height: Pixels,
3743        y_flipped: bool,
3744        window: &mut Window,
3745        cx: &mut App,
3746    ) -> Option<AnyElement> {
3747        let max_height_in_lines = ((height - POPOVER_Y_PADDING) / line_height).floor() as u32;
3748        self.editor.update(cx, |editor, cx| {
3749            editor.render_context_menu(&self.style, max_height_in_lines, y_flipped, window, cx)
3750        })
3751    }
3752
3753    fn render_context_menu_aside(
3754        &self,
3755        max_size: Size<Pixels>,
3756        window: &mut Window,
3757        cx: &mut App,
3758    ) -> Option<AnyElement> {
3759        if max_size.width < px(100.) || max_size.height < px(12.) {
3760            None
3761        } else {
3762            self.editor.update(cx, |editor, cx| {
3763                editor.render_context_menu_aside(max_size, window, cx)
3764            })
3765        }
3766    }
3767
3768    fn layout_mouse_context_menu(
3769        &self,
3770        editor_snapshot: &EditorSnapshot,
3771        visible_range: Range<DisplayRow>,
3772        content_origin: gpui::Point<Pixels>,
3773        window: &mut Window,
3774        cx: &mut App,
3775    ) -> Option<AnyElement> {
3776        let position = self.editor.update(cx, |editor, _cx| {
3777            let visible_start_point = editor.display_to_pixel_point(
3778                DisplayPoint::new(visible_range.start, 0),
3779                editor_snapshot,
3780                window,
3781            )?;
3782            let visible_end_point = editor.display_to_pixel_point(
3783                DisplayPoint::new(visible_range.end, 0),
3784                editor_snapshot,
3785                window,
3786            )?;
3787
3788            let mouse_context_menu = editor.mouse_context_menu.as_ref()?;
3789            let (source_display_point, position) = match mouse_context_menu.position {
3790                MenuPosition::PinnedToScreen(point) => (None, point),
3791                MenuPosition::PinnedToEditor { source, offset } => {
3792                    let source_display_point = source.to_display_point(editor_snapshot);
3793                    let source_point = editor.to_pixel_point(source, editor_snapshot, window)?;
3794                    let position = content_origin + source_point + offset;
3795                    (Some(source_display_point), position)
3796                }
3797            };
3798
3799            let source_included = source_display_point.map_or(true, |source_display_point| {
3800                visible_range
3801                    .to_inclusive()
3802                    .contains(&source_display_point.row())
3803            });
3804            let position_included =
3805                visible_start_point.y <= position.y && position.y <= visible_end_point.y;
3806            if !source_included && !position_included {
3807                None
3808            } else {
3809                Some(position)
3810            }
3811        })?;
3812
3813        let text_style = TextStyleRefinement {
3814            line_height: Some(DefiniteLength::Fraction(
3815                BufferLineHeight::Comfortable.value(),
3816            )),
3817            ..Default::default()
3818        };
3819        window.with_text_style(Some(text_style), |window| {
3820            let mut element = self.editor.update(cx, |editor, _| {
3821                let mouse_context_menu = editor.mouse_context_menu.as_ref()?;
3822                let context_menu = mouse_context_menu.context_menu.clone();
3823
3824                Some(
3825                    deferred(
3826                        anchored()
3827                            .position(position)
3828                            .child(context_menu)
3829                            .anchor(Corner::TopLeft)
3830                            .snap_to_window_with_margin(px(8.)),
3831                    )
3832                    .with_priority(1)
3833                    .into_any(),
3834                )
3835            })?;
3836
3837            element.prepaint_as_root(position, AvailableSpace::min_size(), window, cx);
3838            Some(element)
3839        })
3840    }
3841
3842    fn layout_hover_popovers(
3843        &self,
3844        snapshot: &EditorSnapshot,
3845        hitbox: &Hitbox,
3846        text_hitbox: &Hitbox,
3847        visible_display_row_range: Range<DisplayRow>,
3848        content_origin: gpui::Point<Pixels>,
3849        scroll_pixel_position: gpui::Point<Pixels>,
3850        line_layouts: &[LineWithInvisibles],
3851        line_height: Pixels,
3852        em_width: Pixels,
3853        window: &mut Window,
3854        cx: &mut App,
3855    ) {
3856        struct MeasuredHoverPopover {
3857            element: AnyElement,
3858            size: Size<Pixels>,
3859            horizontal_offset: Pixels,
3860        }
3861
3862        let max_size = size(
3863            (120. * em_width) // Default size
3864                .min(hitbox.size.width / 2.) // Shrink to half of the editor width
3865                .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
3866            (16. * line_height) // Default size
3867                .min(hitbox.size.height / 2.) // Shrink to half of the editor height
3868                .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
3869        );
3870
3871        let hover_popovers = self.editor.update(cx, |editor, cx| {
3872            editor
3873                .hover_state
3874                .render(snapshot, visible_display_row_range.clone(), max_size, cx)
3875        });
3876        let Some((position, hover_popovers)) = hover_popovers else {
3877            return;
3878        };
3879
3880        // This is safe because we check on layout whether the required row is available
3881        let hovered_row_layout =
3882            &line_layouts[position.row().minus(visible_display_row_range.start) as usize];
3883
3884        // Compute Hovered Point
3885        let x =
3886            hovered_row_layout.x_for_index(position.column() as usize) - scroll_pixel_position.x;
3887        let y = position.row().as_f32() * line_height - scroll_pixel_position.y;
3888        let hovered_point = content_origin + point(x, y);
3889
3890        let mut overall_height = Pixels::ZERO;
3891        let mut measured_hover_popovers = Vec::new();
3892        for mut hover_popover in hover_popovers {
3893            let size = hover_popover.layout_as_root(AvailableSpace::min_size(), window, cx);
3894            let horizontal_offset =
3895                (text_hitbox.top_right().x - (hovered_point.x + size.width)).min(Pixels::ZERO);
3896
3897            overall_height += HOVER_POPOVER_GAP + size.height;
3898
3899            measured_hover_popovers.push(MeasuredHoverPopover {
3900                element: hover_popover,
3901                size,
3902                horizontal_offset,
3903            });
3904        }
3905        overall_height += HOVER_POPOVER_GAP;
3906
3907        fn draw_occluder(
3908            width: Pixels,
3909            origin: gpui::Point<Pixels>,
3910            window: &mut Window,
3911            cx: &mut App,
3912        ) {
3913            let mut occlusion = div()
3914                .size_full()
3915                .occlude()
3916                .on_mouse_move(|_, _, cx| cx.stop_propagation())
3917                .into_any_element();
3918            occlusion.layout_as_root(size(width, HOVER_POPOVER_GAP).into(), window, cx);
3919            window.defer_draw(occlusion, origin, 2);
3920        }
3921
3922        if hovered_point.y > overall_height {
3923            // There is enough space above. Render popovers above the hovered point
3924            let mut current_y = hovered_point.y;
3925            for (position, popover) in measured_hover_popovers.into_iter().with_position() {
3926                let size = popover.size;
3927                let popover_origin = point(
3928                    hovered_point.x + popover.horizontal_offset,
3929                    current_y - size.height,
3930                );
3931
3932                window.defer_draw(popover.element, popover_origin, 2);
3933                if position != itertools::Position::Last {
3934                    let origin = point(popover_origin.x, popover_origin.y - HOVER_POPOVER_GAP);
3935                    draw_occluder(size.width, origin, window, cx);
3936                }
3937
3938                current_y = popover_origin.y - HOVER_POPOVER_GAP;
3939            }
3940        } else {
3941            // There is not enough space above. Render popovers below the hovered point
3942            let mut current_y = hovered_point.y + line_height;
3943            for (position, popover) in measured_hover_popovers.into_iter().with_position() {
3944                let size = popover.size;
3945                let popover_origin = point(hovered_point.x + popover.horizontal_offset, current_y);
3946
3947                window.defer_draw(popover.element, popover_origin, 2);
3948                if position != itertools::Position::Last {
3949                    let origin = point(popover_origin.x, popover_origin.y + size.height);
3950                    draw_occluder(size.width, origin, window, cx);
3951                }
3952
3953                current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP;
3954            }
3955        }
3956    }
3957
3958    fn layout_diff_hunk_controls(
3959        &self,
3960        row_range: Range<DisplayRow>,
3961        row_infos: &[RowInfo],
3962        text_hitbox: &Hitbox,
3963        position_map: &PositionMap,
3964        newest_cursor_position: Option<DisplayPoint>,
3965        line_height: Pixels,
3966        scroll_pixel_position: gpui::Point<Pixels>,
3967        display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
3968        editor: Entity<Editor>,
3969        window: &mut Window,
3970        cx: &mut App,
3971    ) -> Vec<AnyElement> {
3972        let render_diff_hunk_controls = editor.read(cx).render_diff_hunk_controls.clone();
3973        let point_for_position = position_map.point_for_position(window.mouse_position());
3974
3975        let mut controls = vec![];
3976
3977        let active_positions = [
3978            Some(point_for_position.previous_valid),
3979            newest_cursor_position,
3980        ];
3981
3982        for (hunk, _) in display_hunks {
3983            if let DisplayDiffHunk::Unfolded {
3984                display_row_range,
3985                multi_buffer_range,
3986                status,
3987                is_created_file,
3988                ..
3989            } = &hunk
3990            {
3991                if display_row_range.start < row_range.start
3992                    || display_row_range.start >= row_range.end
3993                {
3994                    continue;
3995                }
3996                let row_ix = (display_row_range.start - row_range.start).0 as usize;
3997                if row_infos[row_ix].diff_status.is_none() {
3998                    continue;
3999                }
4000                if row_infos[row_ix]
4001                    .diff_status
4002                    .is_some_and(|status| status.is_added())
4003                    && !status.is_added()
4004                {
4005                    continue;
4006                }
4007                if active_positions
4008                    .iter()
4009                    .any(|p| p.map_or(false, |p| display_row_range.contains(&p.row())))
4010                {
4011                    let y = display_row_range.start.as_f32() * line_height
4012                        + text_hitbox.bounds.top()
4013                        - scroll_pixel_position.y;
4014
4015                    let mut element = render_diff_hunk_controls(
4016                        display_row_range.start.0,
4017                        status,
4018                        multi_buffer_range.clone(),
4019                        *is_created_file,
4020                        line_height,
4021                        &editor,
4022                        window,
4023                        cx,
4024                    );
4025                    let size =
4026                        element.layout_as_root(size(px(100.0), line_height).into(), window, cx);
4027
4028                    let x = text_hitbox.bounds.right()
4029                        - self.style.scrollbar_width
4030                        - px(10.)
4031                        - size.width;
4032
4033                    window.with_absolute_element_offset(gpui::Point::new(x, y), |window| {
4034                        element.prepaint(window, cx)
4035                    });
4036                    controls.push(element);
4037                }
4038            }
4039        }
4040
4041        controls
4042    }
4043
4044    fn layout_signature_help(
4045        &self,
4046        hitbox: &Hitbox,
4047        content_origin: gpui::Point<Pixels>,
4048        scroll_pixel_position: gpui::Point<Pixels>,
4049        newest_selection_head: Option<DisplayPoint>,
4050        start_row: DisplayRow,
4051        line_layouts: &[LineWithInvisibles],
4052        line_height: Pixels,
4053        em_width: Pixels,
4054        window: &mut Window,
4055        cx: &mut App,
4056    ) {
4057        if !self.editor.focus_handle(cx).is_focused(window) {
4058            return;
4059        }
4060        let Some(newest_selection_head) = newest_selection_head else {
4061            return;
4062        };
4063        let selection_row = newest_selection_head.row();
4064        if selection_row < start_row {
4065            return;
4066        }
4067        let Some(cursor_row_layout) = line_layouts.get(selection_row.minus(start_row) as usize)
4068        else {
4069            return;
4070        };
4071
4072        let start_x = cursor_row_layout.x_for_index(newest_selection_head.column() as usize)
4073            - scroll_pixel_position.x
4074            + content_origin.x;
4075        let start_y =
4076            selection_row.as_f32() * line_height + content_origin.y - scroll_pixel_position.y;
4077
4078        let max_size = size(
4079            (120. * em_width) // Default size
4080                .min(hitbox.size.width / 2.) // Shrink to half of the editor width
4081                .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
4082            (16. * line_height) // Default size
4083                .min(hitbox.size.height / 2.) // Shrink to half of the editor height
4084                .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
4085        );
4086
4087        let maybe_element = self.editor.update(cx, |editor, cx| {
4088            if let Some(popover) = editor.signature_help_state.popover_mut() {
4089                let element = popover.render(max_size, cx);
4090                Some(element)
4091            } else {
4092                None
4093            }
4094        });
4095        if let Some(mut element) = maybe_element {
4096            let window_size = window.viewport_size();
4097            let size = element.layout_as_root(Size::<AvailableSpace>::default(), window, cx);
4098            let mut point = point(start_x, start_y - size.height);
4099
4100            // Adjusting to ensure the popover does not overflow in the X-axis direction.
4101            if point.x + size.width >= window_size.width {
4102                point.x = window_size.width - size.width;
4103            }
4104
4105            window.defer_draw(element, point, 1)
4106        }
4107    }
4108
4109    fn paint_background(&self, layout: &EditorLayout, window: &mut Window, cx: &mut App) {
4110        window.paint_layer(layout.hitbox.bounds, |window| {
4111            let scroll_top = layout.position_map.snapshot.scroll_position().y;
4112            let gutter_bg = cx.theme().colors().editor_gutter_background;
4113            window.paint_quad(fill(layout.gutter_hitbox.bounds, gutter_bg));
4114            window.paint_quad(fill(
4115                layout.position_map.text_hitbox.bounds,
4116                self.style.background,
4117            ));
4118
4119            if let EditorMode::Full = layout.mode {
4120                let mut active_rows = layout.active_rows.iter().peekable();
4121                while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
4122                    let mut end_row = start_row.0;
4123                    while active_rows
4124                        .peek()
4125                        .map_or(false, |(active_row, has_selection)| {
4126                            active_row.0 == end_row + 1
4127                                && has_selection.selection == contains_non_empty_selection.selection
4128                        })
4129                    {
4130                        active_rows.next().unwrap();
4131                        end_row += 1;
4132                    }
4133
4134                    if !contains_non_empty_selection.selection {
4135                        let highlight_h_range =
4136                            match layout.position_map.snapshot.current_line_highlight {
4137                                CurrentLineHighlight::Gutter => Some(Range {
4138                                    start: layout.hitbox.left(),
4139                                    end: layout.gutter_hitbox.right(),
4140                                }),
4141                                CurrentLineHighlight::Line => Some(Range {
4142                                    start: layout.position_map.text_hitbox.bounds.left(),
4143                                    end: layout.position_map.text_hitbox.bounds.right(),
4144                                }),
4145                                CurrentLineHighlight::All => Some(Range {
4146                                    start: layout.hitbox.left(),
4147                                    end: layout.hitbox.right(),
4148                                }),
4149                                CurrentLineHighlight::None => None,
4150                            };
4151                        if let Some(range) = highlight_h_range {
4152                            let active_line_bg = cx.theme().colors().editor_active_line_background;
4153                            let bounds = Bounds {
4154                                origin: point(
4155                                    range.start,
4156                                    layout.hitbox.origin.y
4157                                        + (start_row.as_f32() - scroll_top)
4158                                            * layout.position_map.line_height,
4159                                ),
4160                                size: size(
4161                                    range.end - range.start,
4162                                    layout.position_map.line_height
4163                                        * (end_row - start_row.0 + 1) as f32,
4164                                ),
4165                            };
4166                            window.paint_quad(fill(bounds, active_line_bg));
4167                        }
4168                    }
4169                }
4170
4171                let mut paint_highlight = |highlight_row_start: DisplayRow,
4172                                           highlight_row_end: DisplayRow,
4173                                           highlight: crate::LineHighlight,
4174                                           edges| {
4175                    let origin = point(
4176                        layout.hitbox.origin.x,
4177                        layout.hitbox.origin.y
4178                            + (highlight_row_start.as_f32() - scroll_top)
4179                                * layout.position_map.line_height,
4180                    );
4181                    let size = size(
4182                        layout.hitbox.size.width,
4183                        layout.position_map.line_height
4184                            * highlight_row_end.next_row().minus(highlight_row_start) as f32,
4185                    );
4186                    let mut quad = fill(Bounds { origin, size }, highlight.background);
4187                    if let Some(border_color) = highlight.border {
4188                        quad.border_color = border_color;
4189                        quad.border_widths = edges
4190                    }
4191                    window.paint_quad(quad);
4192                };
4193
4194                let mut current_paint: Option<(LineHighlight, Range<DisplayRow>, Edges<Pixels>)> =
4195                    None;
4196                for (&new_row, &new_background) in &layout.highlighted_rows {
4197                    match &mut current_paint {
4198                        &mut Some((current_background, ref mut current_range, mut edges)) => {
4199                            let new_range_started = current_background != new_background
4200                                || current_range.end.next_row() != new_row;
4201                            if new_range_started {
4202                                if current_range.end.next_row() == new_row {
4203                                    edges.bottom = px(0.);
4204                                };
4205                                paint_highlight(
4206                                    current_range.start,
4207                                    current_range.end,
4208                                    current_background,
4209                                    edges,
4210                                );
4211                                let edges = Edges {
4212                                    top: if current_range.end.next_row() != new_row {
4213                                        px(1.)
4214                                    } else {
4215                                        px(0.)
4216                                    },
4217                                    bottom: px(1.),
4218                                    ..Default::default()
4219                                };
4220                                current_paint = Some((new_background, new_row..new_row, edges));
4221                                continue;
4222                            } else {
4223                                current_range.end = current_range.end.next_row();
4224                            }
4225                        }
4226                        None => {
4227                            let edges = Edges {
4228                                top: px(1.),
4229                                bottom: px(1.),
4230                                ..Default::default()
4231                            };
4232                            current_paint = Some((new_background, new_row..new_row, edges))
4233                        }
4234                    };
4235                }
4236                if let Some((color, range, edges)) = current_paint {
4237                    paint_highlight(range.start, range.end, color, edges);
4238                }
4239
4240                let scroll_left =
4241                    layout.position_map.snapshot.scroll_position().x * layout.position_map.em_width;
4242
4243                for (wrap_position, active) in layout.wrap_guides.iter() {
4244                    let x = (layout.position_map.text_hitbox.origin.x
4245                        + *wrap_position
4246                        + layout.position_map.em_width / 2.)
4247                        - scroll_left;
4248
4249                    let show_scrollbars = layout
4250                        .scrollbars_layout
4251                        .as_ref()
4252                        .map_or(false, |layout| layout.visible);
4253
4254                    if x < layout.position_map.text_hitbox.origin.x
4255                        || (show_scrollbars && x > self.scrollbar_left(&layout.hitbox.bounds))
4256                    {
4257                        continue;
4258                    }
4259
4260                    let color = if *active {
4261                        cx.theme().colors().editor_active_wrap_guide
4262                    } else {
4263                        cx.theme().colors().editor_wrap_guide
4264                    };
4265                    window.paint_quad(fill(
4266                        Bounds {
4267                            origin: point(x, layout.position_map.text_hitbox.origin.y),
4268                            size: size(px(1.), layout.position_map.text_hitbox.size.height),
4269                        },
4270                        color,
4271                    ));
4272                }
4273            }
4274        })
4275    }
4276
4277    fn paint_indent_guides(
4278        &mut self,
4279        layout: &mut EditorLayout,
4280        window: &mut Window,
4281        cx: &mut App,
4282    ) {
4283        let Some(indent_guides) = &layout.indent_guides else {
4284            return;
4285        };
4286
4287        let faded_color = |color: Hsla, alpha: f32| {
4288            let mut faded = color;
4289            faded.a = alpha;
4290            faded
4291        };
4292
4293        for indent_guide in indent_guides {
4294            let indent_accent_colors = cx.theme().accents().color_for_index(indent_guide.depth);
4295            let settings = indent_guide.settings;
4296
4297            // TODO fixed for now, expose them through themes later
4298            const INDENT_AWARE_ALPHA: f32 = 0.2;
4299            const INDENT_AWARE_ACTIVE_ALPHA: f32 = 0.4;
4300            const INDENT_AWARE_BACKGROUND_ALPHA: f32 = 0.1;
4301            const INDENT_AWARE_BACKGROUND_ACTIVE_ALPHA: f32 = 0.2;
4302
4303            let line_color = match (settings.coloring, indent_guide.active) {
4304                (IndentGuideColoring::Disabled, _) => None,
4305                (IndentGuideColoring::Fixed, false) => {
4306                    Some(cx.theme().colors().editor_indent_guide)
4307                }
4308                (IndentGuideColoring::Fixed, true) => {
4309                    Some(cx.theme().colors().editor_indent_guide_active)
4310                }
4311                (IndentGuideColoring::IndentAware, false) => {
4312                    Some(faded_color(indent_accent_colors, INDENT_AWARE_ALPHA))
4313                }
4314                (IndentGuideColoring::IndentAware, true) => {
4315                    Some(faded_color(indent_accent_colors, INDENT_AWARE_ACTIVE_ALPHA))
4316                }
4317            };
4318
4319            let background_color = match (settings.background_coloring, indent_guide.active) {
4320                (IndentGuideBackgroundColoring::Disabled, _) => None,
4321                (IndentGuideBackgroundColoring::IndentAware, false) => Some(faded_color(
4322                    indent_accent_colors,
4323                    INDENT_AWARE_BACKGROUND_ALPHA,
4324                )),
4325                (IndentGuideBackgroundColoring::IndentAware, true) => Some(faded_color(
4326                    indent_accent_colors,
4327                    INDENT_AWARE_BACKGROUND_ACTIVE_ALPHA,
4328                )),
4329            };
4330
4331            let requested_line_width = if indent_guide.active {
4332                settings.active_line_width
4333            } else {
4334                settings.line_width
4335            }
4336            .clamp(1, 10);
4337            let mut line_indicator_width = 0.;
4338            if let Some(color) = line_color {
4339                window.paint_quad(fill(
4340                    Bounds {
4341                        origin: indent_guide.origin,
4342                        size: size(px(requested_line_width as f32), indent_guide.length),
4343                    },
4344                    color,
4345                ));
4346                line_indicator_width = requested_line_width as f32;
4347            }
4348
4349            if let Some(color) = background_color {
4350                let width = indent_guide.single_indent_width - px(line_indicator_width);
4351                window.paint_quad(fill(
4352                    Bounds {
4353                        origin: point(
4354                            indent_guide.origin.x + px(line_indicator_width),
4355                            indent_guide.origin.y,
4356                        ),
4357                        size: size(width, indent_guide.length),
4358                    },
4359                    color,
4360                ));
4361            }
4362        }
4363    }
4364
4365    fn paint_line_numbers(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
4366        let is_singleton = self.editor.read(cx).is_singleton(cx);
4367
4368        let line_height = layout.position_map.line_height;
4369        window.set_cursor_style(CursorStyle::Arrow, Some(&layout.gutter_hitbox));
4370
4371        for LineNumberLayout {
4372            shaped_line,
4373            hitbox,
4374        } in layout.line_numbers.values()
4375        {
4376            let Some(hitbox) = hitbox else {
4377                continue;
4378            };
4379
4380            let Some(()) = (if !is_singleton && hitbox.is_hovered(window) {
4381                let color = cx.theme().colors().editor_hover_line_number;
4382
4383                let Some(line) = self
4384                    .shape_line_number(shaped_line.text.clone(), color, window)
4385                    .log_err()
4386                else {
4387                    continue;
4388                };
4389
4390                line.paint(hitbox.origin, line_height, window, cx).log_err()
4391            } else {
4392                shaped_line
4393                    .paint(hitbox.origin, line_height, window, cx)
4394                    .log_err()
4395            }) else {
4396                continue;
4397            };
4398
4399            // In singleton buffers, we select corresponding lines on the line number click, so use | -like cursor.
4400            // In multi buffers, we open file at the line number clicked, so use a pointing hand cursor.
4401            if is_singleton {
4402                window.set_cursor_style(CursorStyle::IBeam, Some(&hitbox));
4403            } else {
4404                window.set_cursor_style(CursorStyle::PointingHand, Some(&hitbox));
4405            }
4406        }
4407    }
4408
4409    fn paint_gutter_diff_hunks(layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
4410        if layout.display_hunks.is_empty() {
4411            return;
4412        }
4413
4414        let line_height = layout.position_map.line_height;
4415        window.paint_layer(layout.gutter_hitbox.bounds, |window| {
4416            for (hunk, hitbox) in &layout.display_hunks {
4417                let hunk_to_paint = match hunk {
4418                    DisplayDiffHunk::Folded { .. } => {
4419                        let hunk_bounds = Self::diff_hunk_bounds(
4420                            &layout.position_map.snapshot,
4421                            line_height,
4422                            layout.gutter_hitbox.bounds,
4423                            &hunk,
4424                        );
4425                        Some((
4426                            hunk_bounds,
4427                            cx.theme().colors().version_control_modified,
4428                            Corners::all(px(0.)),
4429                            DiffHunkStatus::modified_none(),
4430                        ))
4431                    }
4432                    DisplayDiffHunk::Unfolded {
4433                        status,
4434                        display_row_range,
4435                        ..
4436                    } => hitbox.as_ref().map(|hunk_hitbox| match status.kind {
4437                        DiffHunkStatusKind::Added => (
4438                            hunk_hitbox.bounds,
4439                            cx.theme().colors().version_control_added,
4440                            Corners::all(px(0.)),
4441                            *status,
4442                        ),
4443                        DiffHunkStatusKind::Modified => (
4444                            hunk_hitbox.bounds,
4445                            cx.theme().colors().version_control_modified,
4446                            Corners::all(px(0.)),
4447                            *status,
4448                        ),
4449                        DiffHunkStatusKind::Deleted if !display_row_range.is_empty() => (
4450                            hunk_hitbox.bounds,
4451                            cx.theme().colors().version_control_deleted,
4452                            Corners::all(px(0.)),
4453                            *status,
4454                        ),
4455                        DiffHunkStatusKind::Deleted => (
4456                            Bounds::new(
4457                                point(
4458                                    hunk_hitbox.origin.x - hunk_hitbox.size.width,
4459                                    hunk_hitbox.origin.y,
4460                                ),
4461                                size(hunk_hitbox.size.width * 2., hunk_hitbox.size.height),
4462                            ),
4463                            cx.theme().colors().version_control_deleted,
4464                            Corners::all(1. * line_height),
4465                            *status,
4466                        ),
4467                    }),
4468                };
4469
4470                if let Some((hunk_bounds, background_color, corner_radii, status)) = hunk_to_paint {
4471                    // Flatten the background color with the editor color to prevent
4472                    // elements below transparent hunks from showing through
4473                    let flattened_background_color = cx
4474                        .theme()
4475                        .colors()
4476                        .editor_background
4477                        .blend(background_color);
4478
4479                    if !Self::diff_hunk_hollow(status, cx) {
4480                        window.paint_quad(quad(
4481                            hunk_bounds,
4482                            corner_radii,
4483                            flattened_background_color,
4484                            Edges::default(),
4485                            transparent_black(),
4486                            BorderStyle::default(),
4487                        ));
4488                    } else {
4489                        let flattened_unstaged_background_color = cx
4490                            .theme()
4491                            .colors()
4492                            .editor_background
4493                            .blend(background_color.opacity(0.3));
4494
4495                        window.paint_quad(quad(
4496                            hunk_bounds,
4497                            corner_radii,
4498                            flattened_unstaged_background_color,
4499                            Edges::all(Pixels(1.0)),
4500                            flattened_background_color,
4501                            BorderStyle::Solid,
4502                        ));
4503                    }
4504                }
4505            }
4506        });
4507    }
4508
4509    fn gutter_strip_width(line_height: Pixels) -> Pixels {
4510        (0.275 * line_height).floor()
4511    }
4512
4513    fn diff_hunk_bounds(
4514        snapshot: &EditorSnapshot,
4515        line_height: Pixels,
4516        gutter_bounds: Bounds<Pixels>,
4517        hunk: &DisplayDiffHunk,
4518    ) -> Bounds<Pixels> {
4519        let scroll_position = snapshot.scroll_position();
4520        let scroll_top = scroll_position.y * line_height;
4521        let gutter_strip_width = Self::gutter_strip_width(line_height);
4522
4523        match hunk {
4524            DisplayDiffHunk::Folded { display_row, .. } => {
4525                let start_y = display_row.as_f32() * line_height - scroll_top;
4526                let end_y = start_y + line_height;
4527                let highlight_origin = gutter_bounds.origin + point(px(0.), start_y);
4528                let highlight_size = size(gutter_strip_width, end_y - start_y);
4529                Bounds::new(highlight_origin, highlight_size)
4530            }
4531            DisplayDiffHunk::Unfolded {
4532                display_row_range,
4533                status,
4534                ..
4535            } => {
4536                if status.is_deleted() && display_row_range.is_empty() {
4537                    let row = display_row_range.start;
4538
4539                    let offset = line_height / 2.;
4540                    let start_y = row.as_f32() * line_height - offset - scroll_top;
4541                    let end_y = start_y + line_height;
4542
4543                    let width = (0.35 * line_height).floor();
4544                    let highlight_origin = gutter_bounds.origin + point(px(0.), start_y);
4545                    let highlight_size = size(width, end_y - start_y);
4546                    Bounds::new(highlight_origin, highlight_size)
4547                } else {
4548                    let start_row = display_row_range.start;
4549                    let end_row = display_row_range.end;
4550                    // If we're in a multibuffer, row range span might include an
4551                    // excerpt header, so if we were to draw the marker straight away,
4552                    // the hunk might include the rows of that header.
4553                    // Making the range inclusive doesn't quite cut it, as we rely on the exclusivity for the soft wrap.
4554                    // Instead, we simply check whether the range we're dealing with includes
4555                    // any excerpt headers and if so, we stop painting the diff hunk on the first row of that header.
4556                    let end_row_in_current_excerpt = snapshot
4557                        .blocks_in_range(start_row..end_row)
4558                        .find_map(|(start_row, block)| {
4559                            if matches!(block, Block::ExcerptBoundary { .. }) {
4560                                Some(start_row)
4561                            } else {
4562                                None
4563                            }
4564                        })
4565                        .unwrap_or(end_row);
4566
4567                    let start_y = start_row.as_f32() * line_height - scroll_top;
4568                    let end_y = end_row_in_current_excerpt.as_f32() * line_height - scroll_top;
4569
4570                    let highlight_origin = gutter_bounds.origin + point(px(0.), start_y);
4571                    let highlight_size = size(gutter_strip_width, end_y - start_y);
4572                    Bounds::new(highlight_origin, highlight_size)
4573                }
4574            }
4575        }
4576    }
4577
4578    fn paint_gutter_indicators(
4579        &self,
4580        layout: &mut EditorLayout,
4581        window: &mut Window,
4582        cx: &mut App,
4583    ) {
4584        window.paint_layer(layout.gutter_hitbox.bounds, |window| {
4585            window.with_element_namespace("crease_toggles", |window| {
4586                for crease_toggle in layout.crease_toggles.iter_mut().flatten() {
4587                    crease_toggle.paint(window, cx);
4588                }
4589            });
4590
4591            window.with_element_namespace("expand_toggles", |window| {
4592                for (expand_toggle, _) in layout.expand_toggles.iter_mut().flatten() {
4593                    expand_toggle.paint(window, cx);
4594                }
4595            });
4596
4597            for breakpoint in layout.breakpoints.iter_mut() {
4598                breakpoint.paint(window, cx);
4599            }
4600
4601            for test_indicator in layout.test_indicators.iter_mut() {
4602                test_indicator.paint(window, cx);
4603            }
4604
4605            if let Some(indicator) = layout.code_actions_indicator.as_mut() {
4606                indicator.paint(window, cx);
4607            }
4608        });
4609    }
4610
4611    fn paint_gutter_highlights(
4612        &self,
4613        layout: &mut EditorLayout,
4614        window: &mut Window,
4615        cx: &mut App,
4616    ) {
4617        for (_, hunk_hitbox) in &layout.display_hunks {
4618            if let Some(hunk_hitbox) = hunk_hitbox {
4619                if !self
4620                    .editor
4621                    .read(cx)
4622                    .buffer()
4623                    .read(cx)
4624                    .all_diff_hunks_expanded()
4625                {
4626                    window.set_cursor_style(CursorStyle::PointingHand, Some(hunk_hitbox));
4627                }
4628            }
4629        }
4630
4631        let show_git_gutter = layout
4632            .position_map
4633            .snapshot
4634            .show_git_diff_gutter
4635            .unwrap_or_else(|| {
4636                matches!(
4637                    ProjectSettings::get_global(cx).git.git_gutter,
4638                    Some(GitGutterSetting::TrackedFiles)
4639                )
4640            });
4641        if show_git_gutter {
4642            Self::paint_gutter_diff_hunks(layout, window, cx)
4643        }
4644
4645        let highlight_width = 0.275 * layout.position_map.line_height;
4646        let highlight_corner_radii = Corners::all(0.05 * layout.position_map.line_height);
4647        window.paint_layer(layout.gutter_hitbox.bounds, |window| {
4648            for (range, color) in &layout.highlighted_gutter_ranges {
4649                let start_row = if range.start.row() < layout.visible_display_row_range.start {
4650                    layout.visible_display_row_range.start - DisplayRow(1)
4651                } else {
4652                    range.start.row()
4653                };
4654                let end_row = if range.end.row() > layout.visible_display_row_range.end {
4655                    layout.visible_display_row_range.end + DisplayRow(1)
4656                } else {
4657                    range.end.row()
4658                };
4659
4660                let start_y = layout.gutter_hitbox.top()
4661                    + start_row.0 as f32 * layout.position_map.line_height
4662                    - layout.position_map.scroll_pixel_position.y;
4663                let end_y = layout.gutter_hitbox.top()
4664                    + (end_row.0 + 1) as f32 * layout.position_map.line_height
4665                    - layout.position_map.scroll_pixel_position.y;
4666                let bounds = Bounds::from_corners(
4667                    point(layout.gutter_hitbox.left(), start_y),
4668                    point(layout.gutter_hitbox.left() + highlight_width, end_y),
4669                );
4670                window.paint_quad(fill(bounds, *color).corner_radii(highlight_corner_radii));
4671            }
4672        });
4673    }
4674
4675    fn paint_blamed_display_rows(
4676        &self,
4677        layout: &mut EditorLayout,
4678        window: &mut Window,
4679        cx: &mut App,
4680    ) {
4681        let Some(blamed_display_rows) = layout.blamed_display_rows.take() else {
4682            return;
4683        };
4684
4685        window.paint_layer(layout.gutter_hitbox.bounds, |window| {
4686            for mut blame_element in blamed_display_rows.into_iter() {
4687                blame_element.paint(window, cx);
4688            }
4689        })
4690    }
4691
4692    fn paint_text(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
4693        window.with_content_mask(
4694            Some(ContentMask {
4695                bounds: layout.position_map.text_hitbox.bounds,
4696            }),
4697            |window| {
4698                let editor = self.editor.read(cx);
4699                if editor.mouse_cursor_hidden {
4700                    window.set_cursor_style(CursorStyle::None, None);
4701                } else if editor
4702                    .hovered_link_state
4703                    .as_ref()
4704                    .is_some_and(|hovered_link_state| !hovered_link_state.links.is_empty())
4705                {
4706                    window.set_cursor_style(
4707                        CursorStyle::PointingHand,
4708                        Some(&layout.position_map.text_hitbox),
4709                    );
4710                } else {
4711                    window.set_cursor_style(
4712                        CursorStyle::IBeam,
4713                        Some(&layout.position_map.text_hitbox),
4714                    );
4715                };
4716
4717                self.paint_lines_background(layout, window, cx);
4718                let invisible_display_ranges = self.paint_highlights(layout, window);
4719                self.paint_lines(&invisible_display_ranges, layout, window, cx);
4720                self.paint_redactions(layout, window);
4721                self.paint_cursors(layout, window, cx);
4722                self.paint_inline_diagnostics(layout, window, cx);
4723                self.paint_inline_blame(layout, window, cx);
4724                self.paint_diff_hunk_controls(layout, window, cx);
4725                window.with_element_namespace("crease_trailers", |window| {
4726                    for trailer in layout.crease_trailers.iter_mut().flatten() {
4727                        trailer.element.paint(window, cx);
4728                    }
4729                });
4730            },
4731        )
4732    }
4733
4734    fn paint_highlights(
4735        &mut self,
4736        layout: &mut EditorLayout,
4737        window: &mut Window,
4738    ) -> SmallVec<[Range<DisplayPoint>; 32]> {
4739        window.paint_layer(layout.position_map.text_hitbox.bounds, |window| {
4740            let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
4741            let line_end_overshoot = 0.15 * layout.position_map.line_height;
4742            for (range, color) in &layout.highlighted_ranges {
4743                self.paint_highlighted_range(
4744                    range.clone(),
4745                    *color,
4746                    Pixels::ZERO,
4747                    line_end_overshoot,
4748                    layout,
4749                    window,
4750                );
4751            }
4752
4753            let corner_radius = 0.15 * layout.position_map.line_height;
4754
4755            for (player_color, selections) in &layout.selections {
4756                for selection in selections.iter() {
4757                    self.paint_highlighted_range(
4758                        selection.range.clone(),
4759                        player_color.selection,
4760                        corner_radius,
4761                        corner_radius * 2.,
4762                        layout,
4763                        window,
4764                    );
4765
4766                    if selection.is_local && !selection.range.is_empty() {
4767                        invisible_display_ranges.push(selection.range.clone());
4768                    }
4769                }
4770            }
4771            invisible_display_ranges
4772        })
4773    }
4774
4775    fn paint_lines(
4776        &mut self,
4777        invisible_display_ranges: &[Range<DisplayPoint>],
4778        layout: &mut EditorLayout,
4779        window: &mut Window,
4780        cx: &mut App,
4781    ) {
4782        let whitespace_setting = self
4783            .editor
4784            .read(cx)
4785            .buffer
4786            .read(cx)
4787            .language_settings(cx)
4788            .show_whitespaces;
4789
4790        for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
4791            let row = DisplayRow(layout.visible_display_row_range.start.0 + ix as u32);
4792            line_with_invisibles.draw(
4793                layout,
4794                row,
4795                layout.content_origin,
4796                whitespace_setting,
4797                invisible_display_ranges,
4798                window,
4799                cx,
4800            )
4801        }
4802
4803        for line_element in &mut layout.line_elements {
4804            line_element.paint(window, cx);
4805        }
4806    }
4807
4808    fn paint_lines_background(
4809        &mut self,
4810        layout: &mut EditorLayout,
4811        window: &mut Window,
4812        cx: &mut App,
4813    ) {
4814        for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
4815            let row = DisplayRow(layout.visible_display_row_range.start.0 + ix as u32);
4816            line_with_invisibles.draw_background(layout, row, layout.content_origin, window, cx);
4817        }
4818    }
4819
4820    fn paint_redactions(&mut self, layout: &EditorLayout, window: &mut Window) {
4821        if layout.redacted_ranges.is_empty() {
4822            return;
4823        }
4824
4825        let line_end_overshoot = layout.line_end_overshoot();
4826
4827        // A softer than perfect black
4828        let redaction_color = gpui::rgb(0x0e1111);
4829
4830        window.paint_layer(layout.position_map.text_hitbox.bounds, |window| {
4831            for range in layout.redacted_ranges.iter() {
4832                self.paint_highlighted_range(
4833                    range.clone(),
4834                    redaction_color.into(),
4835                    Pixels::ZERO,
4836                    line_end_overshoot,
4837                    layout,
4838                    window,
4839                );
4840            }
4841        });
4842    }
4843
4844    fn paint_cursors(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
4845        for cursor in &mut layout.visible_cursors {
4846            cursor.paint(layout.content_origin, window, cx);
4847        }
4848    }
4849
4850    fn paint_scrollbars(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
4851        let Some(scrollbars_layout) = &layout.scrollbars_layout else {
4852            return;
4853        };
4854
4855        for (scrollbar_layout, axis) in scrollbars_layout.iter_scrollbars() {
4856            let hitbox = &scrollbar_layout.hitbox;
4857            let thumb_bounds = scrollbar_layout.thumb_bounds();
4858
4859            if scrollbars_layout.visible {
4860                let scrollbar_edges = match axis {
4861                    ScrollbarAxis::Horizontal => Edges {
4862                        top: Pixels::ZERO,
4863                        right: Pixels::ZERO,
4864                        bottom: Pixels::ZERO,
4865                        left: Pixels::ZERO,
4866                    },
4867                    ScrollbarAxis::Vertical => Edges {
4868                        top: Pixels::ZERO,
4869                        right: Pixels::ZERO,
4870                        bottom: Pixels::ZERO,
4871                        left: ScrollbarLayout::BORDER_WIDTH,
4872                    },
4873                };
4874
4875                window.paint_layer(hitbox.bounds, |window| {
4876                    window.paint_quad(quad(
4877                        hitbox.bounds,
4878                        Corners::default(),
4879                        cx.theme().colors().scrollbar_track_background,
4880                        scrollbar_edges,
4881                        cx.theme().colors().scrollbar_track_border,
4882                        BorderStyle::Solid,
4883                    ));
4884
4885                    if axis == ScrollbarAxis::Vertical {
4886                        let fast_markers =
4887                            self.collect_fast_scrollbar_markers(layout, &scrollbar_layout, cx);
4888                        // Refresh slow scrollbar markers in the background. Below, we
4889                        // paint whatever markers have already been computed.
4890                        self.refresh_slow_scrollbar_markers(layout, &scrollbar_layout, window, cx);
4891
4892                        let markers = self.editor.read(cx).scrollbar_marker_state.markers.clone();
4893                        for marker in markers.iter().chain(&fast_markers) {
4894                            let mut marker = marker.clone();
4895                            marker.bounds.origin += hitbox.origin;
4896                            window.paint_quad(marker);
4897                        }
4898                    }
4899
4900                    window.paint_quad(quad(
4901                        thumb_bounds,
4902                        Corners::default(),
4903                        cx.theme().colors().scrollbar_thumb_background,
4904                        scrollbar_edges,
4905                        cx.theme().colors().scrollbar_thumb_border,
4906                        BorderStyle::Solid,
4907                    ));
4908                })
4909            }
4910            window.set_cursor_style(CursorStyle::Arrow, Some(&hitbox));
4911        }
4912
4913        window.on_mouse_event({
4914            let editor = self.editor.clone();
4915            let scrollbars_layout = scrollbars_layout.clone();
4916
4917            let mut mouse_position = window.mouse_position();
4918            move |event: &MouseMoveEvent, phase, window, cx| {
4919                if phase == DispatchPhase::Capture {
4920                    return;
4921                }
4922
4923                editor.update(cx, |editor, cx| {
4924                    if let Some((scrollbar_layout, axis)) = event
4925                        .pressed_button
4926                        .filter(|button| *button == MouseButton::Left)
4927                        .and(editor.scroll_manager.dragging_scrollbar_axis())
4928                        .and_then(|axis| {
4929                            scrollbars_layout
4930                                .iter_scrollbars()
4931                                .find(|(_, a)| *a == axis)
4932                        })
4933                    {
4934                        let ScrollbarLayout {
4935                            hitbox,
4936                            text_unit_size,
4937                            ..
4938                        } = scrollbar_layout;
4939
4940                        let old_position = mouse_position.along(axis);
4941                        let new_position = event.position.along(axis);
4942                        if (hitbox.origin.along(axis)..hitbox.bottom_right().along(axis))
4943                            .contains(&old_position)
4944                        {
4945                            let position = editor.scroll_position(cx).apply_along(axis, |p| {
4946                                (p + (new_position - old_position) / *text_unit_size).max(0.)
4947                            });
4948                            editor.set_scroll_position(position, window, cx);
4949                        }
4950                        cx.stop_propagation();
4951                    } else {
4952                        editor.scroll_manager.reset_scrollbar_dragging_state(cx);
4953                    }
4954
4955                    if scrollbars_layout.get_hovered_axis(window).is_some() {
4956                        editor.scroll_manager.show_scrollbars(window, cx);
4957                    }
4958
4959                    mouse_position = event.position;
4960                })
4961            }
4962        });
4963
4964        if self.editor.read(cx).scroll_manager.any_scrollbar_dragged() {
4965            window.on_mouse_event({
4966                let editor = self.editor.clone();
4967                move |_: &MouseUpEvent, phase, _, cx| {
4968                    if phase == DispatchPhase::Capture {
4969                        return;
4970                    }
4971
4972                    editor.update(cx, |editor, cx| {
4973                        editor.scroll_manager.reset_scrollbar_dragging_state(cx);
4974                        cx.stop_propagation();
4975                    });
4976                }
4977            });
4978        } else {
4979            window.on_mouse_event({
4980                let editor = self.editor.clone();
4981                let scrollbars_layout = scrollbars_layout.clone();
4982
4983                move |event: &MouseDownEvent, phase, window, cx| {
4984                    if phase == DispatchPhase::Capture {
4985                        return;
4986                    }
4987                    let Some((scrollbar_layout, axis)) = scrollbars_layout.get_hovered_axis(window)
4988                    else {
4989                        return;
4990                    };
4991
4992                    let ScrollbarLayout {
4993                        hitbox,
4994                        visible_range,
4995                        text_unit_size,
4996                        ..
4997                    } = scrollbar_layout;
4998
4999                    let thumb_bounds = scrollbar_layout.thumb_bounds();
5000
5001                    editor.update(cx, |editor, cx| {
5002                        editor.scroll_manager.set_dragged_scrollbar_axis(axis, cx);
5003
5004                        let event_position = event.position.along(axis);
5005
5006                        if event_position < thumb_bounds.origin.along(axis)
5007                            || thumb_bounds.bottom_right().along(axis) < event_position
5008                        {
5009                            let center_position = ((event_position - hitbox.origin.along(axis))
5010                                / *text_unit_size)
5011                                .round() as u32;
5012                            let start_position = center_position.saturating_sub(
5013                                (visible_range.end - visible_range.start) as u32 / 2,
5014                            );
5015
5016                            let position = editor
5017                                .scroll_position(cx)
5018                                .apply_along(axis, |_| start_position as f32);
5019
5020                            editor.set_scroll_position(position, window, cx);
5021                        } else {
5022                            editor.scroll_manager.show_scrollbars(window, cx);
5023                        }
5024
5025                        cx.stop_propagation();
5026                    });
5027                }
5028            });
5029        }
5030    }
5031
5032    fn collect_fast_scrollbar_markers(
5033        &self,
5034        layout: &EditorLayout,
5035        scrollbar_layout: &ScrollbarLayout,
5036        cx: &mut App,
5037    ) -> Vec<PaintQuad> {
5038        const LIMIT: usize = 100;
5039        if !EditorSettings::get_global(cx).scrollbar.cursors || layout.cursors.len() > LIMIT {
5040            return vec![];
5041        }
5042        let cursor_ranges = layout
5043            .cursors
5044            .iter()
5045            .map(|(point, color)| ColoredRange {
5046                start: point.row(),
5047                end: point.row(),
5048                color: *color,
5049            })
5050            .collect_vec();
5051        scrollbar_layout.marker_quads_for_ranges(cursor_ranges, None)
5052    }
5053
5054    fn refresh_slow_scrollbar_markers(
5055        &self,
5056        layout: &EditorLayout,
5057        scrollbar_layout: &ScrollbarLayout,
5058        window: &mut Window,
5059        cx: &mut App,
5060    ) {
5061        self.editor.update(cx, |editor, cx| {
5062            if !editor.is_singleton(cx)
5063                || !editor
5064                    .scrollbar_marker_state
5065                    .should_refresh(scrollbar_layout.hitbox.size)
5066            {
5067                return;
5068            }
5069
5070            let scrollbar_layout = scrollbar_layout.clone();
5071            let background_highlights = editor.background_highlights.clone();
5072            let snapshot = layout.position_map.snapshot.clone();
5073            let theme = cx.theme().clone();
5074            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
5075
5076            editor.scrollbar_marker_state.dirty = false;
5077            editor.scrollbar_marker_state.pending_refresh =
5078                Some(cx.spawn_in(window, async move |editor, cx| {
5079                    let scrollbar_size = scrollbar_layout.hitbox.size;
5080                    let scrollbar_markers = cx
5081                        .background_spawn(async move {
5082                            let max_point = snapshot.display_snapshot.buffer_snapshot.max_point();
5083                            let mut marker_quads = Vec::new();
5084                            if scrollbar_settings.git_diff {
5085                                let marker_row_ranges =
5086                                    snapshot.buffer_snapshot.diff_hunks().map(|hunk| {
5087                                        let start_display_row =
5088                                            MultiBufferPoint::new(hunk.row_range.start.0, 0)
5089                                                .to_display_point(&snapshot.display_snapshot)
5090                                                .row();
5091                                        let mut end_display_row =
5092                                            MultiBufferPoint::new(hunk.row_range.end.0, 0)
5093                                                .to_display_point(&snapshot.display_snapshot)
5094                                                .row();
5095                                        if end_display_row != start_display_row {
5096                                            end_display_row.0 -= 1;
5097                                        }
5098                                        let color = match &hunk.status().kind {
5099                                            DiffHunkStatusKind::Added => {
5100                                                theme.colors().version_control_added
5101                                            }
5102                                            DiffHunkStatusKind::Modified => {
5103                                                theme.colors().version_control_modified
5104                                            }
5105                                            DiffHunkStatusKind::Deleted => {
5106                                                theme.colors().version_control_deleted
5107                                            }
5108                                        };
5109                                        ColoredRange {
5110                                            start: start_display_row,
5111                                            end: end_display_row,
5112                                            color,
5113                                        }
5114                                    });
5115
5116                                marker_quads.extend(
5117                                    scrollbar_layout
5118                                        .marker_quads_for_ranges(marker_row_ranges, Some(0)),
5119                                );
5120                            }
5121
5122                            for (background_highlight_id, (_, background_ranges)) in
5123                                background_highlights.iter()
5124                            {
5125                                let is_search_highlights = *background_highlight_id
5126                                    == TypeId::of::<BufferSearchHighlights>();
5127                                let is_text_highlights = *background_highlight_id
5128                                    == TypeId::of::<SelectedTextHighlight>();
5129                                let is_symbol_occurrences = *background_highlight_id
5130                                    == TypeId::of::<DocumentHighlightRead>()
5131                                    || *background_highlight_id
5132                                        == TypeId::of::<DocumentHighlightWrite>();
5133                                if (is_search_highlights && scrollbar_settings.search_results)
5134                                    || (is_text_highlights && scrollbar_settings.selected_text)
5135                                    || (is_symbol_occurrences && scrollbar_settings.selected_symbol)
5136                                {
5137                                    let mut color = theme.status().info;
5138                                    if is_symbol_occurrences {
5139                                        color.fade_out(0.5);
5140                                    }
5141                                    let marker_row_ranges = background_ranges.iter().map(|range| {
5142                                        let display_start = range
5143                                            .start
5144                                            .to_display_point(&snapshot.display_snapshot);
5145                                        let display_end =
5146                                            range.end.to_display_point(&snapshot.display_snapshot);
5147                                        ColoredRange {
5148                                            start: display_start.row(),
5149                                            end: display_end.row(),
5150                                            color,
5151                                        }
5152                                    });
5153                                    marker_quads.extend(
5154                                        scrollbar_layout
5155                                            .marker_quads_for_ranges(marker_row_ranges, Some(1)),
5156                                    );
5157                                }
5158                            }
5159
5160                            if scrollbar_settings.diagnostics != ScrollbarDiagnostics::None {
5161                                let diagnostics = snapshot
5162                                    .buffer_snapshot
5163                                    .diagnostics_in_range::<Point>(Point::zero()..max_point)
5164                                    // Don't show diagnostics the user doesn't care about
5165                                    .filter(|diagnostic| {
5166                                        match (
5167                                            scrollbar_settings.diagnostics,
5168                                            diagnostic.diagnostic.severity,
5169                                        ) {
5170                                            (ScrollbarDiagnostics::All, _) => true,
5171                                            (
5172                                                ScrollbarDiagnostics::Error,
5173                                                DiagnosticSeverity::ERROR,
5174                                            ) => true,
5175                                            (
5176                                                ScrollbarDiagnostics::Warning,
5177                                                DiagnosticSeverity::ERROR
5178                                                | DiagnosticSeverity::WARNING,
5179                                            ) => true,
5180                                            (
5181                                                ScrollbarDiagnostics::Information,
5182                                                DiagnosticSeverity::ERROR
5183                                                | DiagnosticSeverity::WARNING
5184                                                | DiagnosticSeverity::INFORMATION,
5185                                            ) => true,
5186                                            (_, _) => false,
5187                                        }
5188                                    })
5189                                    // We want to sort by severity, in order to paint the most severe diagnostics last.
5190                                    .sorted_by_key(|diagnostic| {
5191                                        std::cmp::Reverse(diagnostic.diagnostic.severity)
5192                                    });
5193
5194                                let marker_row_ranges = diagnostics.into_iter().map(|diagnostic| {
5195                                    let start_display = diagnostic
5196                                        .range
5197                                        .start
5198                                        .to_display_point(&snapshot.display_snapshot);
5199                                    let end_display = diagnostic
5200                                        .range
5201                                        .end
5202                                        .to_display_point(&snapshot.display_snapshot);
5203                                    let color = match diagnostic.diagnostic.severity {
5204                                        DiagnosticSeverity::ERROR => theme.status().error,
5205                                        DiagnosticSeverity::WARNING => theme.status().warning,
5206                                        DiagnosticSeverity::INFORMATION => theme.status().info,
5207                                        _ => theme.status().hint,
5208                                    };
5209                                    ColoredRange {
5210                                        start: start_display.row(),
5211                                        end: end_display.row(),
5212                                        color,
5213                                    }
5214                                });
5215                                marker_quads.extend(
5216                                    scrollbar_layout
5217                                        .marker_quads_for_ranges(marker_row_ranges, Some(2)),
5218                                );
5219                            }
5220
5221                            Arc::from(marker_quads)
5222                        })
5223                        .await;
5224
5225                    editor.update(cx, |editor, cx| {
5226                        editor.scrollbar_marker_state.markers = scrollbar_markers;
5227                        editor.scrollbar_marker_state.scrollbar_size = scrollbar_size;
5228                        editor.scrollbar_marker_state.pending_refresh = None;
5229                        cx.notify();
5230                    })?;
5231
5232                    Ok(())
5233                }));
5234        });
5235    }
5236
5237    fn paint_highlighted_range(
5238        &self,
5239        range: Range<DisplayPoint>,
5240        color: Hsla,
5241        corner_radius: Pixels,
5242        line_end_overshoot: Pixels,
5243        layout: &EditorLayout,
5244        window: &mut Window,
5245    ) {
5246        let start_row = layout.visible_display_row_range.start;
5247        let end_row = layout.visible_display_row_range.end;
5248        if range.start != range.end {
5249            let row_range = if range.end.column() == 0 {
5250                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
5251            } else {
5252                cmp::max(range.start.row(), start_row)
5253                    ..cmp::min(range.end.row().next_row(), end_row)
5254            };
5255
5256            let highlighted_range = HighlightedRange {
5257                color,
5258                line_height: layout.position_map.line_height,
5259                corner_radius,
5260                start_y: layout.content_origin.y
5261                    + row_range.start.as_f32() * layout.position_map.line_height
5262                    - layout.position_map.scroll_pixel_position.y,
5263                lines: row_range
5264                    .iter_rows()
5265                    .map(|row| {
5266                        let line_layout =
5267                            &layout.position_map.line_layouts[row.minus(start_row) as usize];
5268                        HighlightedRangeLine {
5269                            start_x: if row == range.start.row() {
5270                                layout.content_origin.x
5271                                    + line_layout.x_for_index(range.start.column() as usize)
5272                                    - layout.position_map.scroll_pixel_position.x
5273                            } else {
5274                                layout.content_origin.x
5275                                    - layout.position_map.scroll_pixel_position.x
5276                            },
5277                            end_x: if row == range.end.row() {
5278                                layout.content_origin.x
5279                                    + line_layout.x_for_index(range.end.column() as usize)
5280                                    - layout.position_map.scroll_pixel_position.x
5281                            } else {
5282                                layout.content_origin.x + line_layout.width + line_end_overshoot
5283                                    - layout.position_map.scroll_pixel_position.x
5284                            },
5285                        }
5286                    })
5287                    .collect(),
5288            };
5289
5290            highlighted_range.paint(layout.position_map.text_hitbox.bounds, window);
5291        }
5292    }
5293
5294    fn paint_inline_diagnostics(
5295        &mut self,
5296        layout: &mut EditorLayout,
5297        window: &mut Window,
5298        cx: &mut App,
5299    ) {
5300        for mut inline_diagnostic in layout.inline_diagnostics.drain() {
5301            inline_diagnostic.1.paint(window, cx);
5302        }
5303    }
5304
5305    fn paint_inline_blame(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
5306        if let Some(mut inline_blame) = layout.inline_blame.take() {
5307            window.paint_layer(layout.position_map.text_hitbox.bounds, |window| {
5308                inline_blame.paint(window, cx);
5309            })
5310        }
5311    }
5312
5313    fn paint_diff_hunk_controls(
5314        &mut self,
5315        layout: &mut EditorLayout,
5316        window: &mut Window,
5317        cx: &mut App,
5318    ) {
5319        for mut diff_hunk_control in layout.diff_hunk_controls.drain(..) {
5320            diff_hunk_control.paint(window, cx);
5321        }
5322    }
5323
5324    fn paint_blocks(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
5325        for mut block in layout.blocks.drain(..) {
5326            block.element.paint(window, cx);
5327        }
5328    }
5329
5330    fn paint_inline_completion_popover(
5331        &mut self,
5332        layout: &mut EditorLayout,
5333        window: &mut Window,
5334        cx: &mut App,
5335    ) {
5336        if let Some(inline_completion_popover) = layout.inline_completion_popover.as_mut() {
5337            inline_completion_popover.paint(window, cx);
5338        }
5339    }
5340
5341    fn paint_mouse_context_menu(
5342        &mut self,
5343        layout: &mut EditorLayout,
5344        window: &mut Window,
5345        cx: &mut App,
5346    ) {
5347        if let Some(mouse_context_menu) = layout.mouse_context_menu.as_mut() {
5348            mouse_context_menu.paint(window, cx);
5349        }
5350    }
5351
5352    fn paint_scroll_wheel_listener(
5353        &mut self,
5354        layout: &EditorLayout,
5355        window: &mut Window,
5356        cx: &mut App,
5357    ) {
5358        window.on_mouse_event({
5359            let position_map = layout.position_map.clone();
5360            let editor = self.editor.clone();
5361            let hitbox = layout.hitbox.clone();
5362            let mut delta = ScrollDelta::default();
5363
5364            // Set a minimum scroll_sensitivity of 0.01 to make sure the user doesn't
5365            // accidentally turn off their scrolling.
5366            let scroll_sensitivity = EditorSettings::get_global(cx).scroll_sensitivity.max(0.01);
5367
5368            move |event: &ScrollWheelEvent, phase, window, cx| {
5369                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
5370                    delta = delta.coalesce(event.delta);
5371                    editor.update(cx, |editor, cx| {
5372                        let position_map: &PositionMap = &position_map;
5373
5374                        let line_height = position_map.line_height;
5375                        let max_glyph_width = position_map.em_width;
5376                        let (delta, axis) = match delta {
5377                            gpui::ScrollDelta::Pixels(mut pixels) => {
5378                                //Trackpad
5379                                let axis = position_map.snapshot.ongoing_scroll.filter(&mut pixels);
5380                                (pixels, axis)
5381                            }
5382
5383                            gpui::ScrollDelta::Lines(lines) => {
5384                                //Not trackpad
5385                                let pixels =
5386                                    point(lines.x * max_glyph_width, lines.y * line_height);
5387                                (pixels, None)
5388                            }
5389                        };
5390
5391                        let current_scroll_position = position_map.snapshot.scroll_position();
5392                        let x = (current_scroll_position.x * max_glyph_width
5393                            - (delta.x * scroll_sensitivity))
5394                            / max_glyph_width;
5395                        let y = (current_scroll_position.y * line_height
5396                            - (delta.y * scroll_sensitivity))
5397                            / line_height;
5398                        let mut scroll_position =
5399                            point(x, y).clamp(&point(0., 0.), &position_map.scroll_max);
5400                        let forbid_vertical_scroll = editor.scroll_manager.forbid_vertical_scroll();
5401                        if forbid_vertical_scroll {
5402                            scroll_position.y = current_scroll_position.y;
5403                        }
5404
5405                        if scroll_position != current_scroll_position {
5406                            editor.scroll(scroll_position, axis, window, cx);
5407                            cx.stop_propagation();
5408                        } else if y < 0. {
5409                            // Due to clamping, we may fail to detect cases of overscroll to the top;
5410                            // We want the scroll manager to get an update in such cases and detect the change of direction
5411                            // on the next frame.
5412                            cx.notify();
5413                        }
5414                    });
5415                }
5416            }
5417        });
5418    }
5419
5420    fn paint_mouse_listeners(&mut self, layout: &EditorLayout, window: &mut Window, cx: &mut App) {
5421        self.paint_scroll_wheel_listener(layout, window, cx);
5422
5423        window.on_mouse_event({
5424            let position_map = layout.position_map.clone();
5425            let editor = self.editor.clone();
5426            let diff_hunk_range =
5427                layout
5428                    .display_hunks
5429                    .iter()
5430                    .find_map(|(hunk, hunk_hitbox)| match hunk {
5431                        DisplayDiffHunk::Folded { .. } => None,
5432                        DisplayDiffHunk::Unfolded {
5433                            multi_buffer_range, ..
5434                        } => {
5435                            if hunk_hitbox
5436                                .as_ref()
5437                                .map(|hitbox| hitbox.is_hovered(window))
5438                                .unwrap_or(false)
5439                            {
5440                                Some(multi_buffer_range.clone())
5441                            } else {
5442                                None
5443                            }
5444                        }
5445                    });
5446            let line_numbers = layout.line_numbers.clone();
5447
5448            move |event: &MouseDownEvent, phase, window, cx| {
5449                if phase == DispatchPhase::Bubble {
5450                    match event.button {
5451                        MouseButton::Left => editor.update(cx, |editor, cx| {
5452                            let pending_mouse_down = editor
5453                                .pending_mouse_down
5454                                .get_or_insert_with(Default::default)
5455                                .clone();
5456
5457                            *pending_mouse_down.borrow_mut() = Some(event.clone());
5458
5459                            Self::mouse_left_down(
5460                                editor,
5461                                event,
5462                                diff_hunk_range.clone(),
5463                                &position_map,
5464                                line_numbers.as_ref(),
5465                                window,
5466                                cx,
5467                            );
5468                        }),
5469                        MouseButton::Right => editor.update(cx, |editor, cx| {
5470                            Self::mouse_right_down(editor, event, &position_map, window, cx);
5471                        }),
5472                        MouseButton::Middle => editor.update(cx, |editor, cx| {
5473                            Self::mouse_middle_down(editor, event, &position_map, window, cx);
5474                        }),
5475                        _ => {}
5476                    };
5477                }
5478            }
5479        });
5480
5481        window.on_mouse_event({
5482            let editor = self.editor.clone();
5483            let position_map = layout.position_map.clone();
5484
5485            move |event: &MouseUpEvent, phase, window, cx| {
5486                if phase == DispatchPhase::Bubble {
5487                    editor.update(cx, |editor, cx| {
5488                        Self::mouse_up(editor, event, &position_map, window, cx)
5489                    });
5490                }
5491            }
5492        });
5493
5494        window.on_mouse_event({
5495            let editor = self.editor.clone();
5496            let position_map = layout.position_map.clone();
5497            let mut captured_mouse_down = None;
5498
5499            move |event: &MouseUpEvent, phase, window, cx| match phase {
5500                // Clear the pending mouse down during the capture phase,
5501                // so that it happens even if another event handler stops
5502                // propagation.
5503                DispatchPhase::Capture => editor.update(cx, |editor, _cx| {
5504                    let pending_mouse_down = editor
5505                        .pending_mouse_down
5506                        .get_or_insert_with(Default::default)
5507                        .clone();
5508
5509                    let mut pending_mouse_down = pending_mouse_down.borrow_mut();
5510                    if pending_mouse_down.is_some() && position_map.text_hitbox.is_hovered(window) {
5511                        captured_mouse_down = pending_mouse_down.take();
5512                        window.refresh();
5513                    }
5514                }),
5515                // Fire click handlers during the bubble phase.
5516                DispatchPhase::Bubble => editor.update(cx, |editor, cx| {
5517                    if let Some(mouse_down) = captured_mouse_down.take() {
5518                        let event = ClickEvent {
5519                            down: mouse_down,
5520                            up: event.clone(),
5521                        };
5522                        Self::click(editor, &event, &position_map, window, cx);
5523                    }
5524                }),
5525            }
5526        });
5527
5528        window.on_mouse_event({
5529            let position_map = layout.position_map.clone();
5530            let editor = self.editor.clone();
5531
5532            move |event: &MouseMoveEvent, phase, window, cx| {
5533                if phase == DispatchPhase::Bubble {
5534                    editor.update(cx, |editor, cx| {
5535                        if editor.hover_state.focused(window, cx) {
5536                            return;
5537                        }
5538                        if event.pressed_button == Some(MouseButton::Left)
5539                            || event.pressed_button == Some(MouseButton::Middle)
5540                        {
5541                            Self::mouse_dragged(editor, event, &position_map, window, cx)
5542                        }
5543
5544                        Self::mouse_moved(editor, event, &position_map, window, cx)
5545                    });
5546                }
5547            }
5548        });
5549    }
5550
5551    fn scrollbar_left(&self, bounds: &Bounds<Pixels>) -> Pixels {
5552        bounds.top_right().x - self.style.scrollbar_width
5553    }
5554
5555    fn column_pixels(&self, column: usize, window: &mut Window, _: &mut App) -> Pixels {
5556        let style = &self.style;
5557        let font_size = style.text.font_size.to_pixels(window.rem_size());
5558        let layout = window
5559            .text_system()
5560            .shape_line(
5561                SharedString::from(" ".repeat(column)),
5562                font_size,
5563                &[TextRun {
5564                    len: column,
5565                    font: style.text.font(),
5566                    color: Hsla::default(),
5567                    background_color: None,
5568                    underline: None,
5569                    strikethrough: None,
5570                }],
5571            )
5572            .unwrap();
5573
5574        layout.width
5575    }
5576
5577    fn max_line_number_width(
5578        &self,
5579        snapshot: &EditorSnapshot,
5580        window: &mut Window,
5581        cx: &mut App,
5582    ) -> Pixels {
5583        let digit_count = snapshot.widest_line_number().ilog10() + 1;
5584        self.column_pixels(digit_count as usize, window, cx)
5585    }
5586
5587    fn shape_line_number(
5588        &self,
5589        text: SharedString,
5590        color: Hsla,
5591        window: &mut Window,
5592    ) -> anyhow::Result<ShapedLine> {
5593        let run = TextRun {
5594            len: text.len(),
5595            font: self.style.text.font(),
5596            color,
5597            background_color: None,
5598            underline: None,
5599            strikethrough: None,
5600        };
5601        window.text_system().shape_line(
5602            text,
5603            self.style.text.font_size.to_pixels(window.rem_size()),
5604            &[run],
5605        )
5606    }
5607
5608    fn diff_hunk_hollow(status: DiffHunkStatus, cx: &mut App) -> bool {
5609        let unstaged = status.has_secondary_hunk();
5610        let unstaged_hollow = ProjectSettings::get_global(cx)
5611            .git
5612            .hunk_style
5613            .map_or(false, |style| {
5614                matches!(style, GitHunkStyleSetting::UnstagedHollow)
5615            });
5616
5617        unstaged == unstaged_hollow
5618    }
5619}
5620
5621fn header_jump_data(
5622    snapshot: &EditorSnapshot,
5623    block_row_start: DisplayRow,
5624    height: u32,
5625    for_excerpt: &ExcerptInfo,
5626) -> JumpData {
5627    let range = &for_excerpt.range;
5628    let buffer = &for_excerpt.buffer;
5629    let jump_anchor = range.primary.start;
5630
5631    let excerpt_start = range.context.start;
5632    let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
5633    let rows_from_excerpt_start = if jump_anchor == excerpt_start {
5634        0
5635    } else {
5636        let excerpt_start_point = language::ToPoint::to_point(&excerpt_start, buffer);
5637        jump_position.row.saturating_sub(excerpt_start_point.row)
5638    };
5639
5640    let line_offset_from_top = (block_row_start.0 + height + rows_from_excerpt_start)
5641        .saturating_sub(
5642            snapshot
5643                .scroll_anchor
5644                .scroll_position(&snapshot.display_snapshot)
5645                .y as u32,
5646        );
5647
5648    JumpData::MultiBufferPoint {
5649        excerpt_id: for_excerpt.id,
5650        anchor: jump_anchor,
5651        position: jump_position,
5652        line_offset_from_top,
5653    }
5654}
5655
5656pub struct AcceptEditPredictionBinding(pub(crate) Option<gpui::KeyBinding>);
5657
5658impl AcceptEditPredictionBinding {
5659    pub fn keystroke(&self) -> Option<&Keystroke> {
5660        if let Some(binding) = self.0.as_ref() {
5661            match &binding.keystrokes() {
5662                [keystroke] => Some(keystroke),
5663                _ => None,
5664            }
5665        } else {
5666            None
5667        }
5668    }
5669}
5670
5671fn prepaint_gutter_button(
5672    button: IconButton,
5673    row: DisplayRow,
5674    line_height: Pixels,
5675    gutter_dimensions: &GutterDimensions,
5676    scroll_pixel_position: gpui::Point<Pixels>,
5677    gutter_hitbox: &Hitbox,
5678    display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
5679    window: &mut Window,
5680    cx: &mut App,
5681) -> AnyElement {
5682    let mut button = button.into_any_element();
5683
5684    let available_space = size(
5685        AvailableSpace::MinContent,
5686        AvailableSpace::Definite(line_height),
5687    );
5688    let indicator_size = button.layout_as_root(available_space, window, cx);
5689
5690    let blame_width = gutter_dimensions.git_blame_entries_width;
5691    let gutter_width = display_hunks
5692        .binary_search_by(|(hunk, _)| match hunk {
5693            DisplayDiffHunk::Folded { display_row } => display_row.cmp(&row),
5694            DisplayDiffHunk::Unfolded {
5695                display_row_range, ..
5696            } => {
5697                if display_row_range.end <= row {
5698                    Ordering::Less
5699                } else if display_row_range.start > row {
5700                    Ordering::Greater
5701                } else {
5702                    Ordering::Equal
5703                }
5704            }
5705        })
5706        .ok()
5707        .and_then(|ix| Some(display_hunks[ix].1.as_ref()?.size.width));
5708    let left_offset = blame_width.max(gutter_width).unwrap_or_default();
5709
5710    let mut x = left_offset;
5711    let available_width = gutter_dimensions.margin + gutter_dimensions.left_padding
5712        - indicator_size.width
5713        - left_offset;
5714    x += available_width / 2.;
5715
5716    let mut y = row.as_f32() * line_height - scroll_pixel_position.y;
5717    y += (line_height - indicator_size.height) / 2.;
5718
5719    button.prepaint_as_root(
5720        gutter_hitbox.origin + point(x, y),
5721        available_space,
5722        window,
5723        cx,
5724    );
5725    button
5726}
5727
5728fn render_inline_blame_entry(
5729    editor: Entity<Editor>,
5730    workspace: WeakEntity<Workspace>,
5731    blame: &Entity<GitBlame>,
5732    blame_entry: BlameEntry,
5733    style: &EditorStyle,
5734    cx: &mut App,
5735) -> Option<AnyElement> {
5736    let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
5737    let blame = blame.read(cx);
5738    let details = blame.details_for_entry(&blame_entry);
5739    let repository = blame.repository(cx)?.clone();
5740    renderer.render_inline_blame_entry(
5741        &style.text,
5742        blame_entry,
5743        details,
5744        repository,
5745        workspace,
5746        editor,
5747        cx,
5748    )
5749}
5750
5751fn render_blame_entry(
5752    ix: usize,
5753    blame: &Entity<GitBlame>,
5754    blame_entry: BlameEntry,
5755    style: &EditorStyle,
5756    last_used_color: &mut Option<(PlayerColor, Oid)>,
5757    editor: Entity<Editor>,
5758    workspace: Entity<Workspace>,
5759    renderer: Arc<dyn BlameRenderer>,
5760    cx: &mut App,
5761) -> Option<AnyElement> {
5762    let mut sha_color = cx
5763        .theme()
5764        .players()
5765        .color_for_participant(blame_entry.sha.into());
5766
5767    // If the last color we used is the same as the one we get for this line, but
5768    // the commit SHAs are different, then we try again to get a different color.
5769    match *last_used_color {
5770        Some((color, sha)) if sha != blame_entry.sha && color.cursor == sha_color.cursor => {
5771            let index: u32 = blame_entry.sha.into();
5772            sha_color = cx.theme().players().color_for_participant(index + 1);
5773        }
5774        _ => {}
5775    };
5776    last_used_color.replace((sha_color, blame_entry.sha));
5777
5778    let blame = blame.read(cx);
5779    let details = blame.details_for_entry(&blame_entry);
5780    let repository = blame.repository(cx)?;
5781    renderer.render_blame_entry(
5782        &style.text,
5783        blame_entry,
5784        details,
5785        repository,
5786        workspace.downgrade(),
5787        editor,
5788        ix,
5789        sha_color.cursor,
5790        cx,
5791    )
5792}
5793
5794#[derive(Debug)]
5795pub(crate) struct LineWithInvisibles {
5796    fragments: SmallVec<[LineFragment; 1]>,
5797    invisibles: Vec<Invisible>,
5798    len: usize,
5799    pub(crate) width: Pixels,
5800    font_size: Pixels,
5801}
5802
5803#[allow(clippy::large_enum_variant)]
5804enum LineFragment {
5805    Text(ShapedLine),
5806    Element {
5807        id: FoldId,
5808        element: Option<AnyElement>,
5809        size: Size<Pixels>,
5810        len: usize,
5811    },
5812}
5813
5814impl fmt::Debug for LineFragment {
5815    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5816        match self {
5817            LineFragment::Text(shaped_line) => f.debug_tuple("Text").field(shaped_line).finish(),
5818            LineFragment::Element { size, len, .. } => f
5819                .debug_struct("Element")
5820                .field("size", size)
5821                .field("len", len)
5822                .finish(),
5823        }
5824    }
5825}
5826
5827impl LineWithInvisibles {
5828    fn from_chunks<'a>(
5829        chunks: impl Iterator<Item = HighlightedChunk<'a>>,
5830        editor_style: &EditorStyle,
5831        max_line_len: usize,
5832        max_line_count: usize,
5833        editor_mode: EditorMode,
5834        text_width: Pixels,
5835        is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
5836        window: &mut Window,
5837        cx: &mut App,
5838    ) -> Vec<Self> {
5839        let text_style = &editor_style.text;
5840        let mut layouts = Vec::with_capacity(max_line_count);
5841        let mut fragments: SmallVec<[LineFragment; 1]> = SmallVec::new();
5842        let mut line = String::new();
5843        let mut invisibles = Vec::new();
5844        let mut width = Pixels::ZERO;
5845        let mut len = 0;
5846        let mut styles = Vec::new();
5847        let mut non_whitespace_added = false;
5848        let mut row = 0;
5849        let mut line_exceeded_max_len = false;
5850        let font_size = text_style.font_size.to_pixels(window.rem_size());
5851
5852        let ellipsis = SharedString::from("");
5853
5854        for highlighted_chunk in chunks.chain([HighlightedChunk {
5855            text: "\n",
5856            style: None,
5857            is_tab: false,
5858            replacement: None,
5859        }]) {
5860            if let Some(replacement) = highlighted_chunk.replacement {
5861                if !line.is_empty() {
5862                    let shaped_line = window
5863                        .text_system()
5864                        .shape_line(line.clone().into(), font_size, &styles)
5865                        .unwrap();
5866                    width += shaped_line.width;
5867                    len += shaped_line.len;
5868                    fragments.push(LineFragment::Text(shaped_line));
5869                    line.clear();
5870                    styles.clear();
5871                }
5872
5873                match replacement {
5874                    ChunkReplacement::Renderer(renderer) => {
5875                        let available_width = if renderer.constrain_width {
5876                            let chunk = if highlighted_chunk.text == ellipsis.as_ref() {
5877                                ellipsis.clone()
5878                            } else {
5879                                SharedString::from(Arc::from(highlighted_chunk.text))
5880                            };
5881                            let shaped_line = window
5882                                .text_system()
5883                                .shape_line(
5884                                    chunk,
5885                                    font_size,
5886                                    &[text_style.to_run(highlighted_chunk.text.len())],
5887                                )
5888                                .unwrap();
5889                            AvailableSpace::Definite(shaped_line.width)
5890                        } else {
5891                            AvailableSpace::MinContent
5892                        };
5893
5894                        let mut element = (renderer.render)(&mut ChunkRendererContext {
5895                            context: cx,
5896                            window,
5897                            max_width: text_width,
5898                        });
5899                        let line_height = text_style.line_height_in_pixels(window.rem_size());
5900                        let size = element.layout_as_root(
5901                            size(available_width, AvailableSpace::Definite(line_height)),
5902                            window,
5903                            cx,
5904                        );
5905
5906                        width += size.width;
5907                        len += highlighted_chunk.text.len();
5908                        fragments.push(LineFragment::Element {
5909                            id: renderer.id,
5910                            element: Some(element),
5911                            size,
5912                            len: highlighted_chunk.text.len(),
5913                        });
5914                    }
5915                    ChunkReplacement::Str(x) => {
5916                        let text_style = if let Some(style) = highlighted_chunk.style {
5917                            Cow::Owned(text_style.clone().highlight(style))
5918                        } else {
5919                            Cow::Borrowed(text_style)
5920                        };
5921
5922                        let run = TextRun {
5923                            len: x.len(),
5924                            font: text_style.font(),
5925                            color: text_style.color,
5926                            background_color: text_style.background_color,
5927                            underline: text_style.underline,
5928                            strikethrough: text_style.strikethrough,
5929                        };
5930                        let line_layout = window
5931                            .text_system()
5932                            .shape_line(x, font_size, &[run])
5933                            .unwrap()
5934                            .with_len(highlighted_chunk.text.len());
5935
5936                        width += line_layout.width;
5937                        len += highlighted_chunk.text.len();
5938                        fragments.push(LineFragment::Text(line_layout))
5939                    }
5940                }
5941            } else {
5942                for (ix, mut line_chunk) in highlighted_chunk.text.split('\n').enumerate() {
5943                    if ix > 0 {
5944                        let shaped_line = window
5945                            .text_system()
5946                            .shape_line(line.clone().into(), font_size, &styles)
5947                            .unwrap();
5948                        width += shaped_line.width;
5949                        len += shaped_line.len;
5950                        fragments.push(LineFragment::Text(shaped_line));
5951                        layouts.push(Self {
5952                            width: mem::take(&mut width),
5953                            len: mem::take(&mut len),
5954                            fragments: mem::take(&mut fragments),
5955                            invisibles: std::mem::take(&mut invisibles),
5956                            font_size,
5957                        });
5958
5959                        line.clear();
5960                        styles.clear();
5961                        row += 1;
5962                        line_exceeded_max_len = false;
5963                        non_whitespace_added = false;
5964                        if row == max_line_count {
5965                            return layouts;
5966                        }
5967                    }
5968
5969                    if !line_chunk.is_empty() && !line_exceeded_max_len {
5970                        let text_style = if let Some(style) = highlighted_chunk.style {
5971                            Cow::Owned(text_style.clone().highlight(style))
5972                        } else {
5973                            Cow::Borrowed(text_style)
5974                        };
5975
5976                        if line.len() + line_chunk.len() > max_line_len {
5977                            let mut chunk_len = max_line_len - line.len();
5978                            while !line_chunk.is_char_boundary(chunk_len) {
5979                                chunk_len -= 1;
5980                            }
5981                            line_chunk = &line_chunk[..chunk_len];
5982                            line_exceeded_max_len = true;
5983                        }
5984
5985                        styles.push(TextRun {
5986                            len: line_chunk.len(),
5987                            font: text_style.font(),
5988                            color: text_style.color,
5989                            background_color: text_style.background_color,
5990                            underline: text_style.underline,
5991                            strikethrough: text_style.strikethrough,
5992                        });
5993
5994                        if editor_mode == EditorMode::Full {
5995                            // Line wrap pads its contents with fake whitespaces,
5996                            // avoid printing them
5997                            let is_soft_wrapped = is_row_soft_wrapped(row);
5998                            if highlighted_chunk.is_tab {
5999                                if non_whitespace_added || !is_soft_wrapped {
6000                                    invisibles.push(Invisible::Tab {
6001                                        line_start_offset: line.len(),
6002                                        line_end_offset: line.len() + line_chunk.len(),
6003                                    });
6004                                }
6005                            } else {
6006                                invisibles.extend(line_chunk.char_indices().filter_map(
6007                                    |(index, c)| {
6008                                        let is_whitespace = c.is_whitespace();
6009                                        non_whitespace_added |= !is_whitespace;
6010                                        if is_whitespace
6011                                            && (non_whitespace_added || !is_soft_wrapped)
6012                                        {
6013                                            Some(Invisible::Whitespace {
6014                                                line_offset: line.len() + index,
6015                                            })
6016                                        } else {
6017                                            None
6018                                        }
6019                                    },
6020                                ))
6021                            }
6022                        }
6023
6024                        line.push_str(line_chunk);
6025                    }
6026                }
6027            }
6028        }
6029
6030        layouts
6031    }
6032
6033    fn prepaint(
6034        &mut self,
6035        line_height: Pixels,
6036        scroll_pixel_position: gpui::Point<Pixels>,
6037        row: DisplayRow,
6038        content_origin: gpui::Point<Pixels>,
6039        line_elements: &mut SmallVec<[AnyElement; 1]>,
6040        window: &mut Window,
6041        cx: &mut App,
6042    ) {
6043        let line_y = line_height * (row.as_f32() - scroll_pixel_position.y / line_height);
6044        let mut fragment_origin = content_origin + gpui::point(-scroll_pixel_position.x, line_y);
6045        for fragment in &mut self.fragments {
6046            match fragment {
6047                LineFragment::Text(line) => {
6048                    fragment_origin.x += line.width;
6049                }
6050                LineFragment::Element { element, size, .. } => {
6051                    let mut element = element
6052                        .take()
6053                        .expect("you can't prepaint LineWithInvisibles twice");
6054
6055                    // Center the element vertically within the line.
6056                    let mut element_origin = fragment_origin;
6057                    element_origin.y += (line_height - size.height) / 2.;
6058                    element.prepaint_at(element_origin, window, cx);
6059                    line_elements.push(element);
6060
6061                    fragment_origin.x += size.width;
6062                }
6063            }
6064        }
6065    }
6066
6067    fn draw(
6068        &self,
6069        layout: &EditorLayout,
6070        row: DisplayRow,
6071        content_origin: gpui::Point<Pixels>,
6072        whitespace_setting: ShowWhitespaceSetting,
6073        selection_ranges: &[Range<DisplayPoint>],
6074        window: &mut Window,
6075        cx: &mut App,
6076    ) {
6077        let line_height = layout.position_map.line_height;
6078        let line_y = line_height
6079            * (row.as_f32() - layout.position_map.scroll_pixel_position.y / line_height);
6080
6081        let mut fragment_origin =
6082            content_origin + gpui::point(-layout.position_map.scroll_pixel_position.x, line_y);
6083
6084        for fragment in &self.fragments {
6085            match fragment {
6086                LineFragment::Text(line) => {
6087                    line.paint(fragment_origin, line_height, window, cx)
6088                        .log_err();
6089                    fragment_origin.x += line.width;
6090                }
6091                LineFragment::Element { size, .. } => {
6092                    fragment_origin.x += size.width;
6093                }
6094            }
6095        }
6096
6097        self.draw_invisibles(
6098            selection_ranges,
6099            layout,
6100            content_origin,
6101            line_y,
6102            row,
6103            line_height,
6104            whitespace_setting,
6105            window,
6106            cx,
6107        );
6108    }
6109
6110    fn draw_background(
6111        &self,
6112        layout: &EditorLayout,
6113        row: DisplayRow,
6114        content_origin: gpui::Point<Pixels>,
6115        window: &mut Window,
6116        cx: &mut App,
6117    ) {
6118        let line_height = layout.position_map.line_height;
6119        let line_y = line_height
6120            * (row.as_f32() - layout.position_map.scroll_pixel_position.y / line_height);
6121
6122        let mut fragment_origin =
6123            content_origin + gpui::point(-layout.position_map.scroll_pixel_position.x, line_y);
6124
6125        for fragment in &self.fragments {
6126            match fragment {
6127                LineFragment::Text(line) => {
6128                    line.paint_background(fragment_origin, line_height, window, cx)
6129                        .log_err();
6130                    fragment_origin.x += line.width;
6131                }
6132                LineFragment::Element { size, .. } => {
6133                    fragment_origin.x += size.width;
6134                }
6135            }
6136        }
6137    }
6138
6139    fn draw_invisibles(
6140        &self,
6141        selection_ranges: &[Range<DisplayPoint>],
6142        layout: &EditorLayout,
6143        content_origin: gpui::Point<Pixels>,
6144        line_y: Pixels,
6145        row: DisplayRow,
6146        line_height: Pixels,
6147        whitespace_setting: ShowWhitespaceSetting,
6148        window: &mut Window,
6149        cx: &mut App,
6150    ) {
6151        let extract_whitespace_info = |invisible: &Invisible| {
6152            let (token_offset, token_end_offset, invisible_symbol) = match invisible {
6153                Invisible::Tab {
6154                    line_start_offset,
6155                    line_end_offset,
6156                } => (*line_start_offset, *line_end_offset, &layout.tab_invisible),
6157                Invisible::Whitespace { line_offset } => {
6158                    (*line_offset, line_offset + 1, &layout.space_invisible)
6159                }
6160            };
6161
6162            let x_offset = self.x_for_index(token_offset);
6163            let invisible_offset =
6164                (layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0;
6165            let origin = content_origin
6166                + gpui::point(
6167                    x_offset + invisible_offset - layout.position_map.scroll_pixel_position.x,
6168                    line_y,
6169                );
6170
6171            (
6172                [token_offset, token_end_offset],
6173                Box::new(move |window: &mut Window, cx: &mut App| {
6174                    invisible_symbol
6175                        .paint(origin, line_height, window, cx)
6176                        .log_err();
6177                }),
6178            )
6179        };
6180
6181        let invisible_iter = self.invisibles.iter().map(extract_whitespace_info);
6182        match whitespace_setting {
6183            ShowWhitespaceSetting::None => (),
6184            ShowWhitespaceSetting::All => invisible_iter.for_each(|(_, paint)| paint(window, cx)),
6185            ShowWhitespaceSetting::Selection => invisible_iter.for_each(|([start, _], paint)| {
6186                let invisible_point = DisplayPoint::new(row, start as u32);
6187                if !selection_ranges
6188                    .iter()
6189                    .any(|region| region.start <= invisible_point && invisible_point < region.end)
6190                {
6191                    return;
6192                }
6193
6194                paint(window, cx);
6195            }),
6196
6197            // For a whitespace to be on a boundary, any of the following conditions need to be met:
6198            // - It is a tab
6199            // - It is adjacent to an edge (start or end)
6200            // - It is adjacent to a whitespace (left or right)
6201            ShowWhitespaceSetting::Boundary => {
6202                // We'll need to keep track of the last invisible we've seen and then check if we are adjacent to it for some of
6203                // the above cases.
6204                // Note: We zip in the original `invisibles` to check for tab equality
6205                let mut last_seen: Option<(bool, usize, Box<dyn Fn(&mut Window, &mut App)>)> = None;
6206                for (([start, end], paint), invisible) in
6207                    invisible_iter.zip_eq(self.invisibles.iter())
6208                {
6209                    let should_render = match (&last_seen, invisible) {
6210                        (_, Invisible::Tab { .. }) => true,
6211                        (Some((_, last_end, _)), _) => *last_end == start,
6212                        _ => false,
6213                    };
6214
6215                    if should_render || start == 0 || end == self.len {
6216                        paint(window, cx);
6217
6218                        // Since we are scanning from the left, we will skip over the first available whitespace that is part
6219                        // of a boundary between non-whitespace segments, so we correct by manually redrawing it if needed.
6220                        if let Some((should_render_last, last_end, paint_last)) = last_seen {
6221                            // Note that we need to make sure that the last one is actually adjacent
6222                            if !should_render_last && last_end == start {
6223                                paint_last(window, cx);
6224                            }
6225                        }
6226                    }
6227
6228                    // Manually render anything within a selection
6229                    let invisible_point = DisplayPoint::new(row, start as u32);
6230                    if selection_ranges.iter().any(|region| {
6231                        region.start <= invisible_point && invisible_point < region.end
6232                    }) {
6233                        paint(window, cx);
6234                    }
6235
6236                    last_seen = Some((should_render, end, paint));
6237                }
6238            }
6239        }
6240    }
6241
6242    pub fn x_for_index(&self, index: usize) -> Pixels {
6243        let mut fragment_start_x = Pixels::ZERO;
6244        let mut fragment_start_index = 0;
6245
6246        for fragment in &self.fragments {
6247            match fragment {
6248                LineFragment::Text(shaped_line) => {
6249                    let fragment_end_index = fragment_start_index + shaped_line.len;
6250                    if index < fragment_end_index {
6251                        return fragment_start_x
6252                            + shaped_line.x_for_index(index - fragment_start_index);
6253                    }
6254                    fragment_start_x += shaped_line.width;
6255                    fragment_start_index = fragment_end_index;
6256                }
6257                LineFragment::Element { len, size, .. } => {
6258                    let fragment_end_index = fragment_start_index + len;
6259                    if index < fragment_end_index {
6260                        return fragment_start_x;
6261                    }
6262                    fragment_start_x += size.width;
6263                    fragment_start_index = fragment_end_index;
6264                }
6265            }
6266        }
6267
6268        fragment_start_x
6269    }
6270
6271    pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
6272        let mut fragment_start_x = Pixels::ZERO;
6273        let mut fragment_start_index = 0;
6274
6275        for fragment in &self.fragments {
6276            match fragment {
6277                LineFragment::Text(shaped_line) => {
6278                    let fragment_end_x = fragment_start_x + shaped_line.width;
6279                    if x < fragment_end_x {
6280                        return Some(
6281                            fragment_start_index + shaped_line.index_for_x(x - fragment_start_x)?,
6282                        );
6283                    }
6284                    fragment_start_x = fragment_end_x;
6285                    fragment_start_index += shaped_line.len;
6286                }
6287                LineFragment::Element { len, size, .. } => {
6288                    let fragment_end_x = fragment_start_x + size.width;
6289                    if x < fragment_end_x {
6290                        return Some(fragment_start_index);
6291                    }
6292                    fragment_start_index += len;
6293                    fragment_start_x = fragment_end_x;
6294                }
6295            }
6296        }
6297
6298        None
6299    }
6300
6301    pub fn font_id_for_index(&self, index: usize) -> Option<FontId> {
6302        let mut fragment_start_index = 0;
6303
6304        for fragment in &self.fragments {
6305            match fragment {
6306                LineFragment::Text(shaped_line) => {
6307                    let fragment_end_index = fragment_start_index + shaped_line.len;
6308                    if index < fragment_end_index {
6309                        return shaped_line.font_id_for_index(index - fragment_start_index);
6310                    }
6311                    fragment_start_index = fragment_end_index;
6312                }
6313                LineFragment::Element { len, .. } => {
6314                    let fragment_end_index = fragment_start_index + len;
6315                    if index < fragment_end_index {
6316                        return None;
6317                    }
6318                    fragment_start_index = fragment_end_index;
6319                }
6320            }
6321        }
6322
6323        None
6324    }
6325}
6326
6327#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6328enum Invisible {
6329    /// A tab character
6330    ///
6331    /// A tab character is internally represented by spaces (configured by the user's tab width)
6332    /// aligned to the nearest column, so it's necessary to store the start and end offset for
6333    /// adjacency checks.
6334    Tab {
6335        line_start_offset: usize,
6336        line_end_offset: usize,
6337    },
6338    Whitespace {
6339        line_offset: usize,
6340    },
6341}
6342
6343impl EditorElement {
6344    /// Returns the rem size to use when rendering the [`EditorElement`].
6345    ///
6346    /// This allows UI elements to scale based on the `buffer_font_size`.
6347    fn rem_size(&self, cx: &mut App) -> Option<Pixels> {
6348        match self.editor.read(cx).mode {
6349            EditorMode::Full => {
6350                let buffer_font_size = self.style.text.font_size;
6351                match buffer_font_size {
6352                    AbsoluteLength::Pixels(pixels) => {
6353                        let rem_size_scale = {
6354                            // Our default UI font size is 14px on a 16px base scale.
6355                            // This means the default UI font size is 0.875rems.
6356                            let default_font_size_scale = 14. / ui::BASE_REM_SIZE_IN_PX;
6357
6358                            // We then determine the delta between a single rem and the default font
6359                            // size scale.
6360                            let default_font_size_delta = 1. - default_font_size_scale;
6361
6362                            // Finally, we add this delta to 1rem to get the scale factor that
6363                            // should be used to scale up the UI.
6364                            1. + default_font_size_delta
6365                        };
6366
6367                        Some(pixels * rem_size_scale)
6368                    }
6369                    AbsoluteLength::Rems(rems) => {
6370                        Some(rems.to_pixels(ui::BASE_REM_SIZE_IN_PX.into()))
6371                    }
6372                }
6373            }
6374            // We currently use single-line and auto-height editors in UI contexts,
6375            // so we don't want to scale everything with the buffer font size, as it
6376            // ends up looking off.
6377            EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => None,
6378        }
6379    }
6380}
6381
6382impl Element for EditorElement {
6383    type RequestLayoutState = ();
6384    type PrepaintState = EditorLayout;
6385
6386    fn id(&self) -> Option<ElementId> {
6387        None
6388    }
6389
6390    fn request_layout(
6391        &mut self,
6392        _: Option<&GlobalElementId>,
6393        window: &mut Window,
6394        cx: &mut App,
6395    ) -> (gpui::LayoutId, ()) {
6396        let rem_size = self.rem_size(cx);
6397        window.with_rem_size(rem_size, |window| {
6398            self.editor.update(cx, |editor, cx| {
6399                editor.set_style(self.style.clone(), window, cx);
6400
6401                let layout_id = match editor.mode {
6402                    EditorMode::SingleLine { auto_width } => {
6403                        let rem_size = window.rem_size();
6404
6405                        let height = self.style.text.line_height_in_pixels(rem_size);
6406                        if auto_width {
6407                            let editor_handle = cx.entity().clone();
6408                            let style = self.style.clone();
6409                            window.request_measured_layout(
6410                                Style::default(),
6411                                move |_, _, window, cx| {
6412                                    let editor_snapshot = editor_handle
6413                                        .update(cx, |editor, cx| editor.snapshot(window, cx));
6414                                    let line = Self::layout_lines(
6415                                        DisplayRow(0)..DisplayRow(1),
6416                                        &editor_snapshot,
6417                                        &style,
6418                                        px(f32::MAX),
6419                                        |_| false, // Single lines never soft wrap
6420                                        window,
6421                                        cx,
6422                                    )
6423                                    .pop()
6424                                    .unwrap();
6425
6426                                    let font_id =
6427                                        window.text_system().resolve_font(&style.text.font());
6428                                    let font_size =
6429                                        style.text.font_size.to_pixels(window.rem_size());
6430                                    let em_width =
6431                                        window.text_system().em_width(font_id, font_size).unwrap();
6432
6433                                    size(line.width + em_width, height)
6434                                },
6435                            )
6436                        } else {
6437                            let mut style = Style::default();
6438                            style.size.height = height.into();
6439                            style.size.width = relative(1.).into();
6440                            window.request_layout(style, None, cx)
6441                        }
6442                    }
6443                    EditorMode::AutoHeight { max_lines } => {
6444                        let editor_handle = cx.entity().clone();
6445                        let max_line_number_width =
6446                            self.max_line_number_width(&editor.snapshot(window, cx), window, cx);
6447                        window.request_measured_layout(
6448                            Style::default(),
6449                            move |known_dimensions, available_space, window, cx| {
6450                                editor_handle
6451                                    .update(cx, |editor, cx| {
6452                                        compute_auto_height_layout(
6453                                            editor,
6454                                            max_lines,
6455                                            max_line_number_width,
6456                                            known_dimensions,
6457                                            available_space.width,
6458                                            window,
6459                                            cx,
6460                                        )
6461                                    })
6462                                    .unwrap_or_default()
6463                            },
6464                        )
6465                    }
6466                    EditorMode::Full => {
6467                        let mut style = Style::default();
6468                        style.size.width = relative(1.).into();
6469                        style.size.height = relative(1.).into();
6470                        window.request_layout(style, None, cx)
6471                    }
6472                };
6473
6474                (layout_id, ())
6475            })
6476        })
6477    }
6478
6479    fn prepaint(
6480        &mut self,
6481        _: Option<&GlobalElementId>,
6482        bounds: Bounds<Pixels>,
6483        _: &mut Self::RequestLayoutState,
6484        window: &mut Window,
6485        cx: &mut App,
6486    ) -> Self::PrepaintState {
6487        let text_style = TextStyleRefinement {
6488            font_size: Some(self.style.text.font_size),
6489            line_height: Some(self.style.text.line_height),
6490            ..Default::default()
6491        };
6492        let focus_handle = self.editor.focus_handle(cx);
6493        window.set_view_id(self.editor.entity_id());
6494        window.set_focus_handle(&focus_handle, cx);
6495
6496        let rem_size = self.rem_size(cx);
6497        window.with_rem_size(rem_size, |window| {
6498            window.with_text_style(Some(text_style), |window| {
6499                window.with_content_mask(Some(ContentMask { bounds }), |window| {
6500                    let (mut snapshot, is_read_only) = self.editor.update(cx, |editor, cx| {
6501                        (editor.snapshot(window, cx), editor.read_only(cx))
6502                    });
6503                    let style = self.style.clone();
6504
6505                    let font_id = window.text_system().resolve_font(&style.text.font());
6506                    let font_size = style.text.font_size.to_pixels(window.rem_size());
6507                    let line_height = style.text.line_height_in_pixels(window.rem_size());
6508                    let em_width = window.text_system().em_width(font_id, font_size).unwrap();
6509                    let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
6510
6511                    let glyph_grid_cell = size(em_width, line_height);
6512
6513                    let gutter_dimensions = snapshot
6514                        .gutter_dimensions(
6515                            font_id,
6516                            font_size,
6517                            self.max_line_number_width(&snapshot, window, cx),
6518                            cx,
6519                        )
6520                        .unwrap_or_default();
6521                    let text_width = bounds.size.width - gutter_dimensions.width;
6522
6523                    let editor_width =
6524                        text_width - gutter_dimensions.margin - em_width - style.scrollbar_width;
6525
6526                    snapshot = self.editor.update(cx, |editor, cx| {
6527                        editor.last_bounds = Some(bounds);
6528                        editor.gutter_dimensions = gutter_dimensions;
6529                        editor.set_visible_line_count(bounds.size.height / line_height, window, cx);
6530
6531                        if matches!(editor.mode, EditorMode::AutoHeight { .. }) {
6532                            snapshot
6533                        } else {
6534                            let wrap_width = match editor.soft_wrap_mode(cx) {
6535                                SoftWrap::GitDiff => None,
6536                                SoftWrap::None => Some((MAX_LINE_LEN / 2) as f32 * em_advance),
6537                                SoftWrap::EditorWidth => Some(editor_width),
6538                                SoftWrap::Column(column) => Some(column as f32 * em_advance),
6539                                SoftWrap::Bounded(column) => {
6540                                    Some(editor_width.min(column as f32 * em_advance))
6541                                }
6542                            };
6543
6544                            if editor.set_wrap_width(wrap_width, cx) {
6545                                editor.snapshot(window, cx)
6546                            } else {
6547                                snapshot
6548                            }
6549                        }
6550                    });
6551
6552                    let wrap_guides = self
6553                        .editor
6554                        .read(cx)
6555                        .wrap_guides(cx)
6556                        .iter()
6557                        .map(|(guide, active)| (self.column_pixels(*guide, window, cx), *active))
6558                        .collect::<SmallVec<[_; 2]>>();
6559
6560                    let hitbox = window.insert_hitbox(bounds, false);
6561                    let gutter_hitbox =
6562                        window.insert_hitbox(gutter_bounds(bounds, gutter_dimensions), false);
6563                    let text_hitbox = window.insert_hitbox(
6564                        Bounds {
6565                            origin: gutter_hitbox.top_right(),
6566                            size: size(text_width, bounds.size.height),
6567                        },
6568                        false,
6569                    );
6570
6571                    // Offset the content_bounds from the text_bounds by the gutter margin (which
6572                    // is roughly half a character wide) to make hit testing work more like how we want.
6573                    let content_offset = point(gutter_dimensions.margin, Pixels::ZERO);
6574                    let content_origin = text_hitbox.origin + content_offset;
6575
6576                    let editor_text_bounds =
6577                        Bounds::from_corners(content_origin, bounds.bottom_right());
6578
6579                    let height_in_lines = editor_text_bounds.size.height / line_height;
6580
6581                    let max_row = snapshot.max_point().row().as_f32();
6582
6583                    // The max scroll position for the top of the window
6584                    let max_scroll_top = if matches!(snapshot.mode, EditorMode::AutoHeight { .. }) {
6585                        (max_row - height_in_lines + 1.).max(0.)
6586                    } else {
6587                        let settings = EditorSettings::get_global(cx);
6588                        match settings.scroll_beyond_last_line {
6589                            ScrollBeyondLastLine::OnePage => max_row,
6590                            ScrollBeyondLastLine::Off => (max_row - height_in_lines + 1.).max(0.),
6591                            ScrollBeyondLastLine::VerticalScrollMargin => {
6592                                (max_row - height_in_lines + 1. + settings.vertical_scroll_margin)
6593                                    .max(0.)
6594                            }
6595                        }
6596                    };
6597
6598                    // TODO: Autoscrolling for both axes
6599                    let mut autoscroll_request = None;
6600                    let mut autoscroll_containing_element = false;
6601                    let mut autoscroll_horizontally = false;
6602                    self.editor.update(cx, |editor, cx| {
6603                        autoscroll_request = editor.autoscroll_request();
6604                        autoscroll_containing_element =
6605                            autoscroll_request.is_some() || editor.has_pending_selection();
6606                        // TODO: Is this horizontal or vertical?!
6607                        autoscroll_horizontally = editor.autoscroll_vertically(
6608                            bounds,
6609                            line_height,
6610                            max_scroll_top,
6611                            window,
6612                            cx,
6613                        );
6614                        snapshot = editor.snapshot(window, cx);
6615                    });
6616
6617                    let mut scroll_position = snapshot.scroll_position();
6618                    // The scroll position is a fractional point, the whole number of which represents
6619                    // the top of the window in terms of display rows.
6620                    let start_row = DisplayRow(scroll_position.y as u32);
6621                    let max_row = snapshot.max_point().row();
6622                    let end_row = cmp::min(
6623                        (scroll_position.y + height_in_lines).ceil() as u32,
6624                        max_row.next_row().0,
6625                    );
6626                    let end_row = DisplayRow(end_row);
6627
6628                    let row_infos = snapshot
6629                        .row_infos(start_row)
6630                        .take((start_row..end_row).len())
6631                        .collect::<Vec<RowInfo>>();
6632                    let is_row_soft_wrapped = |row: usize| {
6633                        row_infos
6634                            .get(row)
6635                            .map_or(true, |info| info.buffer_row.is_none())
6636                    };
6637
6638                    let start_anchor = if start_row == Default::default() {
6639                        Anchor::min()
6640                    } else {
6641                        snapshot.buffer_snapshot.anchor_before(
6642                            DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left),
6643                        )
6644                    };
6645                    let end_anchor = if end_row > max_row {
6646                        Anchor::max()
6647                    } else {
6648                        snapshot.buffer_snapshot.anchor_before(
6649                            DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right),
6650                        )
6651                    };
6652
6653                    let mut highlighted_rows = self
6654                        .editor
6655                        .update(cx, |editor, cx| editor.highlighted_display_rows(window, cx));
6656
6657                    let is_light = cx.theme().appearance().is_light();
6658
6659                    for (ix, row_info) in row_infos.iter().enumerate() {
6660                        let Some(diff_status) = row_info.diff_status else {
6661                            continue;
6662                        };
6663
6664                        let background_color = match diff_status.kind {
6665                            DiffHunkStatusKind::Added => cx.theme().colors().version_control_added,
6666                            DiffHunkStatusKind::Deleted => {
6667                                cx.theme().colors().version_control_deleted
6668                            }
6669                            DiffHunkStatusKind::Modified => {
6670                                debug_panic!("modified diff status for row info");
6671                                continue;
6672                            }
6673                        };
6674
6675                        let hunk_opacity = if is_light { 0.16 } else { 0.12 };
6676
6677                        let hollow_highlight = LineHighlight {
6678                            background: (background_color.opacity(if is_light {
6679                                0.08
6680                            } else {
6681                                0.06
6682                            }))
6683                            .into(),
6684                            border: Some(if is_light {
6685                                background_color.opacity(0.48)
6686                            } else {
6687                                background_color.opacity(0.36)
6688                            }),
6689                        };
6690
6691                        let filled_highlight =
6692                            solid_background(background_color.opacity(hunk_opacity)).into();
6693
6694                        let background = if Self::diff_hunk_hollow(diff_status, cx) {
6695                            hollow_highlight
6696                        } else {
6697                            filled_highlight
6698                        };
6699
6700                        highlighted_rows
6701                            .entry(start_row + DisplayRow(ix as u32))
6702                            .or_insert(background);
6703                    }
6704
6705                    let highlighted_ranges = self.editor.read(cx).background_highlights_in_range(
6706                        start_anchor..end_anchor,
6707                        &snapshot.display_snapshot,
6708                        cx.theme().colors(),
6709                    );
6710                    let highlighted_gutter_ranges =
6711                        self.editor.read(cx).gutter_highlights_in_range(
6712                            start_anchor..end_anchor,
6713                            &snapshot.display_snapshot,
6714                            cx,
6715                        );
6716
6717                    let redacted_ranges = self.editor.read(cx).redacted_ranges(
6718                        start_anchor..end_anchor,
6719                        &snapshot.display_snapshot,
6720                        cx,
6721                    );
6722
6723                    let (local_selections, selected_buffer_ids): (
6724                        Vec<Selection<Point>>,
6725                        Vec<BufferId>,
6726                    ) = self.editor.update(cx, |editor, cx| {
6727                        let all_selections = editor.selections.all::<Point>(cx);
6728                        let selected_buffer_ids = if editor.is_singleton(cx) {
6729                            Vec::new()
6730                        } else {
6731                            let mut selected_buffer_ids = Vec::with_capacity(all_selections.len());
6732
6733                            for selection in all_selections {
6734                                for buffer_id in snapshot
6735                                    .buffer_snapshot
6736                                    .buffer_ids_for_range(selection.range())
6737                                {
6738                                    if selected_buffer_ids.last() != Some(&buffer_id) {
6739                                        selected_buffer_ids.push(buffer_id);
6740                                    }
6741                                }
6742                            }
6743
6744                            selected_buffer_ids
6745                        };
6746
6747                        let mut selections = editor
6748                            .selections
6749                            .disjoint_in_range(start_anchor..end_anchor, cx);
6750                        selections.extend(editor.selections.pending(cx));
6751
6752                        (selections, selected_buffer_ids)
6753                    });
6754
6755                    let (selections, mut active_rows, newest_selection_head) = self
6756                        .layout_selections(
6757                            start_anchor,
6758                            end_anchor,
6759                            &local_selections,
6760                            &snapshot,
6761                            start_row,
6762                            end_row,
6763                            window,
6764                            cx,
6765                        );
6766                    let mut breakpoint_rows = self.editor.update(cx, |editor, cx| {
6767                        editor.active_breakpoints(start_row..end_row, window, cx)
6768                    });
6769                    if cx.has_flag::<Debugger>() {
6770                        for display_row in breakpoint_rows.keys() {
6771                            active_rows.entry(*display_row).or_default().breakpoint = true;
6772                        }
6773                    }
6774
6775                    let line_numbers = self.layout_line_numbers(
6776                        Some(&gutter_hitbox),
6777                        gutter_dimensions,
6778                        line_height,
6779                        scroll_position,
6780                        start_row..end_row,
6781                        &row_infos,
6782                        &active_rows,
6783                        newest_selection_head,
6784                        &snapshot,
6785                        window,
6786                        cx,
6787                    );
6788
6789                    // We add the gutter breakpoint indicator to breakpoint_rows after painting
6790                    // line numbers so we don't paint a line number debug accent color if a user
6791                    // has their mouse over that line when a breakpoint isn't there
6792                    if cx.has_flag::<Debugger>() {
6793                        let gutter_breakpoint_indicator =
6794                            self.editor.read(cx).gutter_breakpoint_indicator.0;
6795                        if let Some((gutter_breakpoint_point, _)) =
6796                            gutter_breakpoint_indicator.filter(|(_, is_active)| *is_active)
6797                        {
6798                            breakpoint_rows
6799                                .entry(gutter_breakpoint_point.row())
6800                                .or_insert_with(|| {
6801                                    let position = snapshot.display_point_to_anchor(
6802                                        gutter_breakpoint_point,
6803                                        Bias::Right,
6804                                    );
6805                                    let breakpoint = Breakpoint::new_standard();
6806
6807                                    (position, breakpoint)
6808                                });
6809                        }
6810                    }
6811
6812                    let mut expand_toggles =
6813                        window.with_element_namespace("expand_toggles", |window| {
6814                            self.layout_expand_toggles(
6815                                &gutter_hitbox,
6816                                gutter_dimensions,
6817                                em_width,
6818                                line_height,
6819                                scroll_position,
6820                                &row_infos,
6821                                window,
6822                                cx,
6823                            )
6824                        });
6825
6826                    let mut crease_toggles =
6827                        window.with_element_namespace("crease_toggles", |window| {
6828                            self.layout_crease_toggles(
6829                                start_row..end_row,
6830                                &row_infos,
6831                                &active_rows,
6832                                &snapshot,
6833                                window,
6834                                cx,
6835                            )
6836                        });
6837                    let crease_trailers =
6838                        window.with_element_namespace("crease_trailers", |window| {
6839                            self.layout_crease_trailers(
6840                                row_infos.iter().copied(),
6841                                &snapshot,
6842                                window,
6843                                cx,
6844                            )
6845                        });
6846
6847                    let display_hunks = self.layout_gutter_diff_hunks(
6848                        line_height,
6849                        &gutter_hitbox,
6850                        start_row..end_row,
6851                        &snapshot,
6852                        window,
6853                        cx,
6854                    );
6855
6856                    let mut line_layouts = Self::layout_lines(
6857                        start_row..end_row,
6858                        &snapshot,
6859                        &self.style,
6860                        editor_width,
6861                        is_row_soft_wrapped,
6862                        window,
6863                        cx,
6864                    );
6865                    let new_fold_widths = line_layouts
6866                        .iter()
6867                        .flat_map(|layout| &layout.fragments)
6868                        .filter_map(|fragment| {
6869                            if let LineFragment::Element { id, size, .. } = fragment {
6870                                Some((*id, size.width))
6871                            } else {
6872                                None
6873                            }
6874                        });
6875                    if self.editor.update(cx, |editor, cx| {
6876                        editor.update_fold_widths(new_fold_widths, cx)
6877                    }) {
6878                        // If the fold widths have changed, we need to prepaint
6879                        // the element again to account for any changes in
6880                        // wrapping.
6881                        return self.prepaint(None, bounds, &mut (), window, cx);
6882                    }
6883
6884                    let longest_line_blame_width = self
6885                        .editor
6886                        .update(cx, |editor, cx| {
6887                            if !editor.show_git_blame_inline {
6888                                return None;
6889                            }
6890                            let blame = editor.blame.as_ref()?;
6891                            let blame_entry = blame
6892                                .update(cx, |blame, cx| {
6893                                    let row_infos =
6894                                        snapshot.row_infos(snapshot.longest_row()).next()?;
6895                                    blame.blame_for_rows(&[row_infos], cx).next()
6896                                })
6897                                .flatten()?;
6898                            let mut element = render_inline_blame_entry(
6899                                self.editor.clone(),
6900                                editor.workspace()?.downgrade(),
6901                                blame,
6902                                blame_entry,
6903                                &style,
6904                                cx,
6905                            )?;
6906                            let inline_blame_padding = INLINE_BLAME_PADDING_EM_WIDTHS * em_advance;
6907                            Some(
6908                                element
6909                                    .layout_as_root(AvailableSpace::min_size(), window, cx)
6910                                    .width
6911                                    + inline_blame_padding,
6912                            )
6913                        })
6914                        .unwrap_or(Pixels::ZERO);
6915
6916                    let longest_line_width = layout_line(
6917                        snapshot.longest_row(),
6918                        &snapshot,
6919                        &style,
6920                        editor_width,
6921                        is_row_soft_wrapped,
6922                        window,
6923                        cx,
6924                    )
6925                    .width;
6926
6927                    let scrollbar_layout_information = ScrollbarLayoutInformation::new(
6928                        text_hitbox.bounds,
6929                        glyph_grid_cell,
6930                        size(longest_line_width, max_row.as_f32() * line_height),
6931                        longest_line_blame_width,
6932                        style.scrollbar_width,
6933                        editor_width,
6934                        EditorSettings::get_global(cx),
6935                    );
6936
6937                    let mut scroll_width = scrollbar_layout_information.scroll_range.width;
6938
6939                    let sticky_header_excerpt = if snapshot.buffer_snapshot.show_headers() {
6940                        snapshot.sticky_header_excerpt(scroll_position.y)
6941                    } else {
6942                        None
6943                    };
6944                    let sticky_header_excerpt_id =
6945                        sticky_header_excerpt.as_ref().map(|top| top.excerpt.id);
6946
6947                    let blocks = window.with_element_namespace("blocks", |window| {
6948                        self.render_blocks(
6949                            start_row..end_row,
6950                            &snapshot,
6951                            &hitbox,
6952                            &text_hitbox,
6953                            editor_width,
6954                            &mut scroll_width,
6955                            &gutter_dimensions,
6956                            em_width,
6957                            gutter_dimensions.full_width(),
6958                            line_height,
6959                            &line_layouts,
6960                            &local_selections,
6961                            &selected_buffer_ids,
6962                            is_row_soft_wrapped,
6963                            sticky_header_excerpt_id,
6964                            window,
6965                            cx,
6966                        )
6967                    });
6968                    let mut blocks = match blocks {
6969                        Ok(blocks) => blocks,
6970                        Err(resized_blocks) => {
6971                            self.editor.update(cx, |editor, cx| {
6972                                editor.resize_blocks(resized_blocks, autoscroll_request, cx)
6973                            });
6974                            return self.prepaint(None, bounds, &mut (), window, cx);
6975                        }
6976                    };
6977
6978                    let sticky_buffer_header = sticky_header_excerpt.map(|sticky_header_excerpt| {
6979                        window.with_element_namespace("blocks", |window| {
6980                            self.layout_sticky_buffer_header(
6981                                sticky_header_excerpt,
6982                                scroll_position.y,
6983                                line_height,
6984                                &snapshot,
6985                                &hitbox,
6986                                &selected_buffer_ids,
6987                                &blocks,
6988                                window,
6989                                cx,
6990                            )
6991                        })
6992                    });
6993
6994                    let start_buffer_row =
6995                        MultiBufferRow(start_anchor.to_point(&snapshot.buffer_snapshot).row);
6996                    let end_buffer_row =
6997                        MultiBufferRow(end_anchor.to_point(&snapshot.buffer_snapshot).row);
6998
6999                    let scroll_max = point(
7000                        ((scroll_width - editor_text_bounds.size.width) / em_width).max(0.0),
7001                        max_scroll_top,
7002                    );
7003
7004                    self.editor.update(cx, |editor, cx| {
7005                        let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x);
7006
7007                        let autoscrolled = if autoscroll_horizontally {
7008                            editor.autoscroll_horizontally(
7009                                start_row,
7010                                editor_width - (glyph_grid_cell.width / 2.0)
7011                                    + style.scrollbar_width,
7012                                scroll_width,
7013                                em_width,
7014                                &line_layouts,
7015                                cx,
7016                            )
7017                        } else {
7018                            false
7019                        };
7020
7021                        if clamped || autoscrolled {
7022                            snapshot = editor.snapshot(window, cx);
7023                            scroll_position = snapshot.scroll_position();
7024                        }
7025                    });
7026
7027                    let scroll_pixel_position = point(
7028                        scroll_position.x * em_width,
7029                        scroll_position.y * line_height,
7030                    );
7031
7032                    let indent_guides = self.layout_indent_guides(
7033                        content_origin,
7034                        text_hitbox.origin,
7035                        start_buffer_row..end_buffer_row,
7036                        scroll_pixel_position,
7037                        line_height,
7038                        &snapshot,
7039                        window,
7040                        cx,
7041                    );
7042
7043                    let crease_trailers =
7044                        window.with_element_namespace("crease_trailers", |window| {
7045                            self.prepaint_crease_trailers(
7046                                crease_trailers,
7047                                &line_layouts,
7048                                line_height,
7049                                content_origin,
7050                                scroll_pixel_position,
7051                                em_width,
7052                                window,
7053                                cx,
7054                            )
7055                        });
7056
7057                    let (inline_completion_popover, inline_completion_popover_origin) = self
7058                        .editor
7059                        .update(cx, |editor, cx| {
7060                            editor.render_edit_prediction_popover(
7061                                &text_hitbox.bounds,
7062                                content_origin,
7063                                &snapshot,
7064                                start_row..end_row,
7065                                scroll_position.y,
7066                                scroll_position.y + height_in_lines,
7067                                &line_layouts,
7068                                line_height,
7069                                scroll_pixel_position,
7070                                newest_selection_head,
7071                                editor_width,
7072                                &style,
7073                                window,
7074                                cx,
7075                            )
7076                        })
7077                        .unzip();
7078
7079                    let mut inline_diagnostics = self.layout_inline_diagnostics(
7080                        &line_layouts,
7081                        &crease_trailers,
7082                        content_origin,
7083                        scroll_pixel_position,
7084                        inline_completion_popover_origin,
7085                        start_row,
7086                        end_row,
7087                        line_height,
7088                        em_width,
7089                        &style,
7090                        window,
7091                        cx,
7092                    );
7093
7094                    let mut inline_blame = None;
7095                    if let Some(newest_selection_head) = newest_selection_head {
7096                        let display_row = newest_selection_head.row();
7097                        if (start_row..end_row).contains(&display_row) {
7098                            let line_ix = display_row.minus(start_row) as usize;
7099                            let row_info = &row_infos[line_ix];
7100                            let line_layout = &line_layouts[line_ix];
7101                            let crease_trailer_layout = crease_trailers[line_ix].as_ref();
7102                            inline_blame = self.layout_inline_blame(
7103                                display_row,
7104                                row_info,
7105                                line_layout,
7106                                crease_trailer_layout,
7107                                em_width,
7108                                content_origin,
7109                                scroll_pixel_position,
7110                                line_height,
7111                                window,
7112                                cx,
7113                            );
7114                            if inline_blame.is_some() {
7115                                // Blame overrides inline diagnostics
7116                                inline_diagnostics.remove(&display_row);
7117                            }
7118                        }
7119                    }
7120
7121                    let blamed_display_rows = self.layout_blame_entries(
7122                        &row_infos,
7123                        em_width,
7124                        scroll_position,
7125                        line_height,
7126                        &gutter_hitbox,
7127                        gutter_dimensions.git_blame_entries_width,
7128                        window,
7129                        cx,
7130                    );
7131
7132                    self.editor.update(cx, |editor, cx| {
7133                        let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x);
7134
7135                        let autoscrolled = if autoscroll_horizontally {
7136                            editor.autoscroll_horizontally(
7137                                start_row,
7138                                editor_width - (glyph_grid_cell.width / 2.0)
7139                                    + style.scrollbar_width,
7140                                scroll_width,
7141                                em_width,
7142                                &line_layouts,
7143                                cx,
7144                            )
7145                        } else {
7146                            false
7147                        };
7148
7149                        if clamped || autoscrolled {
7150                            snapshot = editor.snapshot(window, cx);
7151                            scroll_position = snapshot.scroll_position();
7152                        }
7153                    });
7154
7155                    let line_elements = self.prepaint_lines(
7156                        start_row,
7157                        &mut line_layouts,
7158                        line_height,
7159                        scroll_pixel_position,
7160                        content_origin,
7161                        window,
7162                        cx,
7163                    );
7164
7165                    let mut block_start_rows = HashSet::default();
7166
7167                    window.with_element_namespace("blocks", |window| {
7168                        self.layout_blocks(
7169                            &mut blocks,
7170                            &mut block_start_rows,
7171                            &hitbox,
7172                            line_height,
7173                            scroll_pixel_position,
7174                            window,
7175                            cx,
7176                        );
7177                    });
7178
7179                    let cursors = self.collect_cursors(&snapshot, cx);
7180                    let visible_row_range = start_row..end_row;
7181                    let non_visible_cursors = cursors
7182                        .iter()
7183                        .any(|c| !visible_row_range.contains(&c.0.row()));
7184
7185                    let visible_cursors = self.layout_visible_cursors(
7186                        &snapshot,
7187                        &selections,
7188                        &block_start_rows,
7189                        start_row..end_row,
7190                        &line_layouts,
7191                        &text_hitbox,
7192                        content_origin,
7193                        scroll_position,
7194                        scroll_pixel_position,
7195                        line_height,
7196                        em_width,
7197                        em_advance,
7198                        autoscroll_containing_element,
7199                        window,
7200                        cx,
7201                    );
7202
7203                    let scrollbars_layout = self.layout_scrollbars(
7204                        &snapshot,
7205                        scrollbar_layout_information,
7206                        content_offset,
7207                        scroll_position,
7208                        non_visible_cursors,
7209                        window,
7210                        cx,
7211                    );
7212
7213                    let gutter_settings = EditorSettings::get_global(cx).gutter;
7214
7215                    let mut code_actions_indicator = None;
7216                    if let Some(newest_selection_head) = newest_selection_head {
7217                        let newest_selection_point =
7218                            newest_selection_head.to_point(&snapshot.display_snapshot);
7219
7220                        if (start_row..end_row).contains(&newest_selection_head.row()) {
7221                            self.layout_cursor_popovers(
7222                                line_height,
7223                                &text_hitbox,
7224                                content_origin,
7225                                start_row,
7226                                scroll_pixel_position,
7227                                &line_layouts,
7228                                newest_selection_head,
7229                                newest_selection_point,
7230                                &style,
7231                                window,
7232                                cx,
7233                            );
7234
7235                            let show_code_actions = snapshot
7236                                .show_code_actions
7237                                .unwrap_or(gutter_settings.code_actions);
7238                            if show_code_actions {
7239                                let newest_selection_point =
7240                                    newest_selection_head.to_point(&snapshot.display_snapshot);
7241                                if !snapshot
7242                                    .is_line_folded(MultiBufferRow(newest_selection_point.row))
7243                                {
7244                                    let buffer = snapshot.buffer_snapshot.buffer_line_for_row(
7245                                        MultiBufferRow(newest_selection_point.row),
7246                                    );
7247                                    if let Some((buffer, range)) = buffer {
7248                                        let buffer_id = buffer.remote_id();
7249                                        let row = range.start.row;
7250                                        let has_test_indicator = self
7251                                            .editor
7252                                            .read(cx)
7253                                            .tasks
7254                                            .contains_key(&(buffer_id, row));
7255
7256                                        let has_expand_indicator = row_infos
7257                                            .get(
7258                                                (newest_selection_head.row() - start_row).0
7259                                                    as usize,
7260                                            )
7261                                            .is_some_and(|row_info| row_info.expand_info.is_some());
7262
7263                                        if !has_test_indicator && !has_expand_indicator {
7264                                            code_actions_indicator = self
7265                                                .layout_code_actions_indicator(
7266                                                    line_height,
7267                                                    newest_selection_head,
7268                                                    scroll_pixel_position,
7269                                                    &gutter_dimensions,
7270                                                    &gutter_hitbox,
7271                                                    &mut breakpoint_rows,
7272                                                    &display_hunks,
7273                                                    window,
7274                                                    cx,
7275                                                );
7276                                        }
7277                                    }
7278                                }
7279                            }
7280                        }
7281                    }
7282
7283                    self.layout_gutter_menu(
7284                        line_height,
7285                        &text_hitbox,
7286                        content_origin,
7287                        scroll_pixel_position,
7288                        gutter_dimensions.width - gutter_dimensions.left_padding,
7289                        window,
7290                        cx,
7291                    );
7292
7293                    let test_indicators = if gutter_settings.runnables {
7294                        self.layout_run_indicators(
7295                            line_height,
7296                            start_row..end_row,
7297                            &row_infos,
7298                            scroll_pixel_position,
7299                            &gutter_dimensions,
7300                            &gutter_hitbox,
7301                            &display_hunks,
7302                            &snapshot,
7303                            &mut breakpoint_rows,
7304                            window,
7305                            cx,
7306                        )
7307                    } else {
7308                        Vec::new()
7309                    };
7310
7311                    let show_breakpoints = snapshot
7312                        .show_breakpoints
7313                        .unwrap_or(gutter_settings.breakpoints);
7314                    let breakpoints = if cx.has_flag::<Debugger>() && show_breakpoints {
7315                        self.layout_breakpoints(
7316                            line_height,
7317                            start_row..end_row,
7318                            scroll_pixel_position,
7319                            &gutter_dimensions,
7320                            &gutter_hitbox,
7321                            &display_hunks,
7322                            &snapshot,
7323                            breakpoint_rows,
7324                            &row_infos,
7325                            window,
7326                            cx,
7327                        )
7328                    } else {
7329                        vec![]
7330                    };
7331
7332                    self.layout_signature_help(
7333                        &hitbox,
7334                        content_origin,
7335                        scroll_pixel_position,
7336                        newest_selection_head,
7337                        start_row,
7338                        &line_layouts,
7339                        line_height,
7340                        em_width,
7341                        window,
7342                        cx,
7343                    );
7344
7345                    if !cx.has_active_drag() {
7346                        self.layout_hover_popovers(
7347                            &snapshot,
7348                            &hitbox,
7349                            &text_hitbox,
7350                            start_row..end_row,
7351                            content_origin,
7352                            scroll_pixel_position,
7353                            &line_layouts,
7354                            line_height,
7355                            em_width,
7356                            window,
7357                            cx,
7358                        );
7359                    }
7360
7361                    let mouse_context_menu = self.layout_mouse_context_menu(
7362                        &snapshot,
7363                        start_row..end_row,
7364                        content_origin,
7365                        window,
7366                        cx,
7367                    );
7368
7369                    window.with_element_namespace("crease_toggles", |window| {
7370                        self.prepaint_crease_toggles(
7371                            &mut crease_toggles,
7372                            line_height,
7373                            &gutter_dimensions,
7374                            gutter_settings,
7375                            scroll_pixel_position,
7376                            &gutter_hitbox,
7377                            window,
7378                            cx,
7379                        )
7380                    });
7381
7382                    window.with_element_namespace("expand_toggles", |window| {
7383                        self.prepaint_expand_toggles(&mut expand_toggles, window, cx)
7384                    });
7385
7386                    let invisible_symbol_font_size = font_size / 2.;
7387                    let tab_invisible = window
7388                        .text_system()
7389                        .shape_line(
7390                            "".into(),
7391                            invisible_symbol_font_size,
7392                            &[TextRun {
7393                                len: "".len(),
7394                                font: self.style.text.font(),
7395                                color: cx.theme().colors().editor_invisible,
7396                                background_color: None,
7397                                underline: None,
7398                                strikethrough: None,
7399                            }],
7400                        )
7401                        .unwrap();
7402                    let space_invisible = window
7403                        .text_system()
7404                        .shape_line(
7405                            "".into(),
7406                            invisible_symbol_font_size,
7407                            &[TextRun {
7408                                len: "".len(),
7409                                font: self.style.text.font(),
7410                                color: cx.theme().colors().editor_invisible,
7411                                background_color: None,
7412                                underline: None,
7413                                strikethrough: None,
7414                            }],
7415                        )
7416                        .unwrap();
7417
7418                    let mode = snapshot.mode;
7419
7420                    let position_map = Rc::new(PositionMap {
7421                        size: bounds.size,
7422                        visible_row_range,
7423                        scroll_pixel_position,
7424                        scroll_max,
7425                        line_layouts,
7426                        line_height,
7427                        em_width,
7428                        em_advance,
7429                        snapshot,
7430                        gutter_hitbox: gutter_hitbox.clone(),
7431                        text_hitbox: text_hitbox.clone(),
7432                    });
7433
7434                    self.editor.update(cx, |editor, _| {
7435                        editor.last_position_map = Some(position_map.clone())
7436                    });
7437
7438                    let diff_hunk_controls = if is_read_only {
7439                        vec![]
7440                    } else {
7441                        self.layout_diff_hunk_controls(
7442                            start_row..end_row,
7443                            &row_infos,
7444                            &text_hitbox,
7445                            &position_map,
7446                            newest_selection_head,
7447                            line_height,
7448                            scroll_pixel_position,
7449                            &display_hunks,
7450                            self.editor.clone(),
7451                            window,
7452                            cx,
7453                        )
7454                    };
7455
7456                    EditorLayout {
7457                        mode,
7458                        position_map,
7459                        visible_display_row_range: start_row..end_row,
7460                        wrap_guides,
7461                        indent_guides,
7462                        hitbox,
7463                        gutter_hitbox,
7464                        display_hunks,
7465                        content_origin,
7466                        scrollbars_layout,
7467                        active_rows,
7468                        highlighted_rows,
7469                        highlighted_ranges,
7470                        highlighted_gutter_ranges,
7471                        redacted_ranges,
7472                        line_elements,
7473                        line_numbers,
7474                        blamed_display_rows,
7475                        inline_diagnostics,
7476                        inline_blame,
7477                        blocks,
7478                        cursors,
7479                        visible_cursors,
7480                        selections,
7481                        inline_completion_popover,
7482                        diff_hunk_controls,
7483                        mouse_context_menu,
7484                        test_indicators,
7485                        breakpoints,
7486                        code_actions_indicator,
7487                        crease_toggles,
7488                        crease_trailers,
7489                        tab_invisible,
7490                        space_invisible,
7491                        sticky_buffer_header,
7492                        expand_toggles,
7493                    }
7494                })
7495            })
7496        })
7497    }
7498
7499    fn paint(
7500        &mut self,
7501        _: Option<&GlobalElementId>,
7502        bounds: Bounds<gpui::Pixels>,
7503        _: &mut Self::RequestLayoutState,
7504        layout: &mut Self::PrepaintState,
7505        window: &mut Window,
7506        cx: &mut App,
7507    ) {
7508        let focus_handle = self.editor.focus_handle(cx);
7509        let key_context = self
7510            .editor
7511            .update(cx, |editor, cx| editor.key_context(window, cx));
7512
7513        window.set_key_context(key_context);
7514        window.handle_input(
7515            &focus_handle,
7516            ElementInputHandler::new(bounds, self.editor.clone()),
7517            cx,
7518        );
7519        self.register_actions(window, cx);
7520        self.register_key_listeners(window, cx, layout);
7521
7522        let text_style = TextStyleRefinement {
7523            font_size: Some(self.style.text.font_size),
7524            line_height: Some(self.style.text.line_height),
7525            ..Default::default()
7526        };
7527        let rem_size = self.rem_size(cx);
7528        window.with_rem_size(rem_size, |window| {
7529            window.with_text_style(Some(text_style), |window| {
7530                window.with_content_mask(Some(ContentMask { bounds }), |window| {
7531                    self.paint_mouse_listeners(layout, window, cx);
7532                    self.paint_background(layout, window, cx);
7533                    self.paint_indent_guides(layout, window, cx);
7534
7535                    if layout.gutter_hitbox.size.width > Pixels::ZERO {
7536                        self.paint_blamed_display_rows(layout, window, cx);
7537                        self.paint_line_numbers(layout, window, cx);
7538                    }
7539
7540                    self.paint_text(layout, window, cx);
7541
7542                    if layout.gutter_hitbox.size.width > Pixels::ZERO {
7543                        self.paint_gutter_highlights(layout, window, cx);
7544                        self.paint_gutter_indicators(layout, window, cx);
7545                    }
7546
7547                    if !layout.blocks.is_empty() {
7548                        window.with_element_namespace("blocks", |window| {
7549                            self.paint_blocks(layout, window, cx);
7550                        });
7551                    }
7552
7553                    window.with_element_namespace("blocks", |window| {
7554                        if let Some(mut sticky_header) = layout.sticky_buffer_header.take() {
7555                            sticky_header.paint(window, cx)
7556                        }
7557                    });
7558
7559                    self.paint_scrollbars(layout, window, cx);
7560                    self.paint_inline_completion_popover(layout, window, cx);
7561                    self.paint_mouse_context_menu(layout, window, cx);
7562                });
7563            })
7564        })
7565    }
7566}
7567
7568pub(super) fn gutter_bounds(
7569    editor_bounds: Bounds<Pixels>,
7570    gutter_dimensions: GutterDimensions,
7571) -> Bounds<Pixels> {
7572    Bounds {
7573        origin: editor_bounds.origin,
7574        size: size(gutter_dimensions.width, editor_bounds.size.height),
7575    }
7576}
7577
7578/// Holds information required for layouting the editor scrollbars.
7579struct ScrollbarLayoutInformation {
7580    /// The bounds of the editor area (excluding the content offset).
7581    editor_bounds: Bounds<Pixels>,
7582    /// The available range to scroll within the document.
7583    scroll_range: Size<Pixels>,
7584    /// The space available for one glyph in the editor.
7585    glyph_grid_cell: Size<Pixels>,
7586}
7587
7588impl ScrollbarLayoutInformation {
7589    pub fn new(
7590        editor_bounds: Bounds<Pixels>,
7591        glyph_grid_cell: Size<Pixels>,
7592        document_size: Size<Pixels>,
7593        longest_line_blame_width: Pixels,
7594        scrollbar_width: Pixels,
7595        editor_width: Pixels,
7596        settings: &EditorSettings,
7597    ) -> Self {
7598        let vertical_overscroll = match settings.scroll_beyond_last_line {
7599            ScrollBeyondLastLine::OnePage => editor_bounds.size.height,
7600            ScrollBeyondLastLine::Off => glyph_grid_cell.height,
7601            ScrollBeyondLastLine::VerticalScrollMargin => {
7602                (1.0 + settings.vertical_scroll_margin) * glyph_grid_cell.height
7603            }
7604        };
7605
7606        let right_margin = if document_size.width + longest_line_blame_width >= editor_width {
7607            glyph_grid_cell.width + scrollbar_width
7608        } else {
7609            px(0.0)
7610        };
7611
7612        let overscroll = size(right_margin + longest_line_blame_width, vertical_overscroll);
7613
7614        let scroll_range = document_size + overscroll;
7615
7616        ScrollbarLayoutInformation {
7617            editor_bounds,
7618            scroll_range,
7619            glyph_grid_cell,
7620        }
7621    }
7622}
7623
7624impl IntoElement for EditorElement {
7625    type Element = Self;
7626
7627    fn into_element(self) -> Self::Element {
7628        self
7629    }
7630}
7631
7632pub struct EditorLayout {
7633    position_map: Rc<PositionMap>,
7634    hitbox: Hitbox,
7635    gutter_hitbox: Hitbox,
7636    content_origin: gpui::Point<Pixels>,
7637    scrollbars_layout: Option<EditorScrollbars>,
7638    mode: EditorMode,
7639    wrap_guides: SmallVec<[(Pixels, bool); 2]>,
7640    indent_guides: Option<Vec<IndentGuideLayout>>,
7641    visible_display_row_range: Range<DisplayRow>,
7642    active_rows: BTreeMap<DisplayRow, LineHighlightSpec>,
7643    highlighted_rows: BTreeMap<DisplayRow, LineHighlight>,
7644    line_elements: SmallVec<[AnyElement; 1]>,
7645    line_numbers: Arc<HashMap<MultiBufferRow, LineNumberLayout>>,
7646    display_hunks: Vec<(DisplayDiffHunk, Option<Hitbox>)>,
7647    blamed_display_rows: Option<Vec<AnyElement>>,
7648    inline_diagnostics: HashMap<DisplayRow, AnyElement>,
7649    inline_blame: Option<AnyElement>,
7650    blocks: Vec<BlockLayout>,
7651    highlighted_ranges: Vec<(Range<DisplayPoint>, Hsla)>,
7652    highlighted_gutter_ranges: Vec<(Range<DisplayPoint>, Hsla)>,
7653    redacted_ranges: Vec<Range<DisplayPoint>>,
7654    cursors: Vec<(DisplayPoint, Hsla)>,
7655    visible_cursors: Vec<CursorLayout>,
7656    selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
7657    code_actions_indicator: Option<AnyElement>,
7658    test_indicators: Vec<AnyElement>,
7659    breakpoints: Vec<AnyElement>,
7660    crease_toggles: Vec<Option<AnyElement>>,
7661    expand_toggles: Vec<Option<(AnyElement, gpui::Point<Pixels>)>>,
7662    diff_hunk_controls: Vec<AnyElement>,
7663    crease_trailers: Vec<Option<CreaseTrailerLayout>>,
7664    inline_completion_popover: Option<AnyElement>,
7665    mouse_context_menu: Option<AnyElement>,
7666    tab_invisible: ShapedLine,
7667    space_invisible: ShapedLine,
7668    sticky_buffer_header: Option<AnyElement>,
7669}
7670
7671impl EditorLayout {
7672    fn line_end_overshoot(&self) -> Pixels {
7673        0.15 * self.position_map.line_height
7674    }
7675}
7676
7677struct LineNumberLayout {
7678    shaped_line: ShapedLine,
7679    hitbox: Option<Hitbox>,
7680}
7681
7682struct ColoredRange<T> {
7683    start: T,
7684    end: T,
7685    color: Hsla,
7686}
7687
7688impl Along for ScrollbarAxes {
7689    type Unit = bool;
7690
7691    fn along(&self, axis: ScrollbarAxis) -> Self::Unit {
7692        match axis {
7693            ScrollbarAxis::Horizontal => self.horizontal,
7694            ScrollbarAxis::Vertical => self.vertical,
7695        }
7696    }
7697
7698    fn apply_along(&self, axis: ScrollbarAxis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self {
7699        match axis {
7700            ScrollbarAxis::Horizontal => ScrollbarAxes {
7701                horizontal: f(self.horizontal),
7702                vertical: self.vertical,
7703            },
7704            ScrollbarAxis::Vertical => ScrollbarAxes {
7705                horizontal: self.horizontal,
7706                vertical: f(self.vertical),
7707            },
7708        }
7709    }
7710}
7711
7712#[derive(Clone)]
7713struct EditorScrollbars {
7714    pub vertical: Option<ScrollbarLayout>,
7715    pub horizontal: Option<ScrollbarLayout>,
7716    pub visible: bool,
7717}
7718
7719impl EditorScrollbars {
7720    pub fn from_scrollbar_axes(
7721        settings_visibility: ScrollbarAxes,
7722        layout_information: &ScrollbarLayoutInformation,
7723        content_offset: gpui::Point<Pixels>,
7724        scroll_position: gpui::Point<f32>,
7725        scrollbar_width: Pixels,
7726        show_scrollbars: bool,
7727        window: &mut Window,
7728    ) -> Self {
7729        let ScrollbarLayoutInformation {
7730            editor_bounds,
7731            scroll_range,
7732            glyph_grid_cell,
7733        } = layout_information;
7734
7735        let scrollbar_bounds_for = |axis: ScrollbarAxis| match axis {
7736            ScrollbarAxis::Horizontal => Bounds::from_corner_and_size(
7737                Corner::BottomLeft,
7738                editor_bounds.bottom_left(),
7739                size(
7740                    if settings_visibility.vertical {
7741                        editor_bounds.size.width - scrollbar_width
7742                    } else {
7743                        editor_bounds.size.width
7744                    },
7745                    scrollbar_width,
7746                ),
7747            ),
7748            ScrollbarAxis::Vertical => Bounds::from_corner_and_size(
7749                Corner::TopRight,
7750                editor_bounds.top_right(),
7751                size(scrollbar_width, editor_bounds.size.height),
7752            ),
7753        };
7754
7755        let mut create_scrollbar_layout = |axis| {
7756            settings_visibility
7757                .along(axis)
7758                .then(|| {
7759                    (
7760                        editor_bounds.size.along(axis) - content_offset.along(axis),
7761                        scroll_range.along(axis),
7762                    )
7763                })
7764                .filter(|(editor_content_size, scroll_range)| {
7765                    // The scrollbar should only be rendered if the content does
7766                    // not entirely fit into the editor
7767                    // However, this only applies to the horizontal scrollbar, as information about the
7768                    // vertical scrollbar layout is always needed for scrollbar diagnostics.
7769                    axis != ScrollbarAxis::Horizontal || editor_content_size < scroll_range
7770                })
7771                .map(|(editor_content_size, scroll_range)| {
7772                    ScrollbarLayout::new(
7773                        window.insert_hitbox(scrollbar_bounds_for(axis), false),
7774                        editor_content_size,
7775                        scroll_range,
7776                        glyph_grid_cell.along(axis),
7777                        content_offset.along(axis),
7778                        scroll_position.along(axis),
7779                        axis,
7780                    )
7781                })
7782        };
7783
7784        Self {
7785            vertical: create_scrollbar_layout(ScrollbarAxis::Vertical),
7786            horizontal: create_scrollbar_layout(ScrollbarAxis::Horizontal),
7787            visible: show_scrollbars,
7788        }
7789    }
7790
7791    pub fn iter_scrollbars(&self) -> impl Iterator<Item = (&ScrollbarLayout, ScrollbarAxis)> + '_ {
7792        [
7793            (&self.vertical, ScrollbarAxis::Vertical),
7794            (&self.horizontal, ScrollbarAxis::Horizontal),
7795        ]
7796        .into_iter()
7797        .filter_map(|(scrollbar, axis)| scrollbar.as_ref().map(|s| (s, axis)))
7798    }
7799
7800    /// Returns the currently hovered scrollbar axis, if any.
7801    pub fn get_hovered_axis(&self, window: &Window) -> Option<(&ScrollbarLayout, ScrollbarAxis)> {
7802        self.iter_scrollbars()
7803            .find(|s| s.0.hitbox.is_hovered(window))
7804    }
7805}
7806
7807#[derive(Clone)]
7808struct ScrollbarLayout {
7809    hitbox: Hitbox,
7810    visible_range: Range<f32>,
7811    text_unit_size: Pixels,
7812    content_offset: Pixels,
7813    thumb_size: Pixels,
7814    axis: ScrollbarAxis,
7815}
7816
7817impl ScrollbarLayout {
7818    const BORDER_WIDTH: Pixels = px(1.0);
7819    const LINE_MARKER_HEIGHT: Pixels = px(2.0);
7820    const MIN_MARKER_HEIGHT: Pixels = px(5.0);
7821    const MIN_THUMB_SIZE: Pixels = px(25.0);
7822
7823    fn new(
7824        scrollbar_track_hitbox: Hitbox,
7825        editor_content_size: Pixels,
7826        scroll_range: Pixels,
7827        glyph_space: Pixels,
7828        content_offset: Pixels,
7829        scroll_position: f32,
7830        axis: ScrollbarAxis,
7831    ) -> Self {
7832        let track_bounds = scrollbar_track_hitbox.bounds;
7833        // The length of the track available to the scrollbar thumb. We deliberately
7834        // exclude the content size here so that the thumb aligns with the content.
7835        let track_length = track_bounds.size.along(axis) - content_offset;
7836
7837        let text_units_per_page = editor_content_size / glyph_space;
7838        let visible_range = scroll_position..scroll_position + text_units_per_page;
7839        let total_text_units = scroll_range / glyph_space;
7840
7841        let thumb_percentage = text_units_per_page / total_text_units;
7842        let thumb_size = (track_length * thumb_percentage)
7843            .max(ScrollbarLayout::MIN_THUMB_SIZE)
7844            .min(track_length);
7845        let text_unit_size =
7846            (track_length - thumb_size) / (total_text_units - text_units_per_page).max(0.);
7847
7848        ScrollbarLayout {
7849            hitbox: scrollbar_track_hitbox,
7850            visible_range,
7851            text_unit_size,
7852            content_offset,
7853            thumb_size,
7854            axis,
7855        }
7856    }
7857
7858    fn thumb_bounds(&self) -> Bounds<Pixels> {
7859        let scrollbar_track = &self.hitbox.bounds;
7860        Bounds::new(
7861            scrollbar_track
7862                .origin
7863                .apply_along(self.axis, |origin| self.thumb_origin(origin)),
7864            scrollbar_track
7865                .size
7866                .apply_along(self.axis, |_| self.thumb_size),
7867        )
7868    }
7869
7870    fn thumb_origin(&self, origin: Pixels) -> Pixels {
7871        origin + self.content_offset + self.visible_range.start * self.text_unit_size
7872    }
7873
7874    fn marker_quads_for_ranges(
7875        &self,
7876        row_ranges: impl IntoIterator<Item = ColoredRange<DisplayRow>>,
7877        column: Option<usize>,
7878    ) -> Vec<PaintQuad> {
7879        struct MinMax {
7880            min: Pixels,
7881            max: Pixels,
7882        }
7883        let (x_range, height_limit) = if let Some(column) = column {
7884            let column_width = px(((self.hitbox.size.width - Self::BORDER_WIDTH).0 / 3.0).floor());
7885            let start = Self::BORDER_WIDTH + (column as f32 * column_width);
7886            let end = start + column_width;
7887            (
7888                Range { start, end },
7889                MinMax {
7890                    min: Self::MIN_MARKER_HEIGHT,
7891                    max: px(f32::MAX),
7892                },
7893            )
7894        } else {
7895            (
7896                Range {
7897                    start: Self::BORDER_WIDTH,
7898                    end: self.hitbox.size.width,
7899                },
7900                MinMax {
7901                    min: Self::LINE_MARKER_HEIGHT,
7902                    max: Self::LINE_MARKER_HEIGHT,
7903                },
7904            )
7905        };
7906
7907        let row_to_y = |row: DisplayRow| row.as_f32() * self.text_unit_size;
7908        let mut pixel_ranges = row_ranges
7909            .into_iter()
7910            .map(|range| {
7911                let start_y = row_to_y(range.start);
7912                let end_y = row_to_y(range.end)
7913                    + self
7914                        .text_unit_size
7915                        .max(height_limit.min)
7916                        .min(height_limit.max);
7917                ColoredRange {
7918                    start: start_y,
7919                    end: end_y,
7920                    color: range.color,
7921                }
7922            })
7923            .peekable();
7924
7925        let mut quads = Vec::new();
7926        while let Some(mut pixel_range) = pixel_ranges.next() {
7927            while let Some(next_pixel_range) = pixel_ranges.peek() {
7928                if pixel_range.end >= next_pixel_range.start - px(1.0)
7929                    && pixel_range.color == next_pixel_range.color
7930                {
7931                    pixel_range.end = next_pixel_range.end.max(pixel_range.end);
7932                    pixel_ranges.next();
7933                } else {
7934                    break;
7935                }
7936            }
7937
7938            let bounds = Bounds::from_corners(
7939                point(x_range.start, pixel_range.start),
7940                point(x_range.end, pixel_range.end),
7941            );
7942            quads.push(quad(
7943                bounds,
7944                Corners::default(),
7945                pixel_range.color,
7946                Edges::default(),
7947                Hsla::transparent_black(),
7948                BorderStyle::default(),
7949            ));
7950        }
7951
7952        quads
7953    }
7954}
7955
7956struct CreaseTrailerLayout {
7957    element: AnyElement,
7958    bounds: Bounds<Pixels>,
7959}
7960
7961pub(crate) struct PositionMap {
7962    pub size: Size<Pixels>,
7963    pub line_height: Pixels,
7964    pub scroll_pixel_position: gpui::Point<Pixels>,
7965    pub scroll_max: gpui::Point<f32>,
7966    pub em_width: Pixels,
7967    pub em_advance: Pixels,
7968    pub visible_row_range: Range<DisplayRow>,
7969    pub line_layouts: Vec<LineWithInvisibles>,
7970    pub snapshot: EditorSnapshot,
7971    pub text_hitbox: Hitbox,
7972    pub gutter_hitbox: Hitbox,
7973}
7974
7975#[derive(Debug, Copy, Clone)]
7976pub struct PointForPosition {
7977    pub previous_valid: DisplayPoint,
7978    pub next_valid: DisplayPoint,
7979    pub exact_unclipped: DisplayPoint,
7980    pub column_overshoot_after_line_end: u32,
7981}
7982
7983impl PointForPosition {
7984    pub fn as_valid(&self) -> Option<DisplayPoint> {
7985        if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
7986            Some(self.previous_valid)
7987        } else {
7988            None
7989        }
7990    }
7991}
7992
7993impl PositionMap {
7994    pub(crate) fn point_for_position(&self, position: gpui::Point<Pixels>) -> PointForPosition {
7995        let text_bounds = self.text_hitbox.bounds;
7996        let scroll_position = self.snapshot.scroll_position();
7997        let position = position - text_bounds.origin;
7998        let y = position.y.max(px(0.)).min(self.size.height);
7999        let x = position.x + (scroll_position.x * self.em_width);
8000        let row = ((y / self.line_height) + scroll_position.y) as u32;
8001
8002        let (column, x_overshoot_after_line_end) = if let Some(line) = self
8003            .line_layouts
8004            .get(row as usize - scroll_position.y as usize)
8005        {
8006            if let Some(ix) = line.index_for_x(x) {
8007                (ix as u32, px(0.))
8008            } else {
8009                (line.len as u32, px(0.).max(x - line.width))
8010            }
8011        } else {
8012            (0, x)
8013        };
8014
8015        let mut exact_unclipped = DisplayPoint::new(DisplayRow(row), column);
8016        let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
8017        let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
8018
8019        let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
8020        *exact_unclipped.column_mut() += column_overshoot_after_line_end;
8021        PointForPosition {
8022            previous_valid,
8023            next_valid,
8024            exact_unclipped,
8025            column_overshoot_after_line_end,
8026        }
8027    }
8028}
8029
8030struct BlockLayout {
8031    id: BlockId,
8032    row: Option<DisplayRow>,
8033    element: AnyElement,
8034    available_space: Size<AvailableSpace>,
8035    style: BlockStyle,
8036    is_buffer_header: bool,
8037}
8038
8039pub fn layout_line(
8040    row: DisplayRow,
8041    snapshot: &EditorSnapshot,
8042    style: &EditorStyle,
8043    text_width: Pixels,
8044    is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
8045    window: &mut Window,
8046    cx: &mut App,
8047) -> LineWithInvisibles {
8048    let chunks = snapshot.highlighted_chunks(row..row + DisplayRow(1), true, style);
8049    LineWithInvisibles::from_chunks(
8050        chunks,
8051        &style,
8052        MAX_LINE_LEN,
8053        1,
8054        snapshot.mode,
8055        text_width,
8056        is_row_soft_wrapped,
8057        window,
8058        cx,
8059    )
8060    .pop()
8061    .unwrap()
8062}
8063
8064#[derive(Debug)]
8065pub struct IndentGuideLayout {
8066    origin: gpui::Point<Pixels>,
8067    length: Pixels,
8068    single_indent_width: Pixels,
8069    depth: u32,
8070    active: bool,
8071    settings: IndentGuideSettings,
8072}
8073
8074pub struct CursorLayout {
8075    origin: gpui::Point<Pixels>,
8076    block_width: Pixels,
8077    line_height: Pixels,
8078    color: Hsla,
8079    shape: CursorShape,
8080    block_text: Option<ShapedLine>,
8081    cursor_name: Option<AnyElement>,
8082}
8083
8084#[derive(Debug)]
8085pub struct CursorName {
8086    string: SharedString,
8087    color: Hsla,
8088    is_top_row: bool,
8089}
8090
8091impl CursorLayout {
8092    pub fn new(
8093        origin: gpui::Point<Pixels>,
8094        block_width: Pixels,
8095        line_height: Pixels,
8096        color: Hsla,
8097        shape: CursorShape,
8098        block_text: Option<ShapedLine>,
8099    ) -> CursorLayout {
8100        CursorLayout {
8101            origin,
8102            block_width,
8103            line_height,
8104            color,
8105            shape,
8106            block_text,
8107            cursor_name: None,
8108        }
8109    }
8110
8111    pub fn bounding_rect(&self, origin: gpui::Point<Pixels>) -> Bounds<Pixels> {
8112        Bounds {
8113            origin: self.origin + origin,
8114            size: size(self.block_width, self.line_height),
8115        }
8116    }
8117
8118    fn bounds(&self, origin: gpui::Point<Pixels>) -> Bounds<Pixels> {
8119        match self.shape {
8120            CursorShape::Bar => Bounds {
8121                origin: self.origin + origin,
8122                size: size(px(2.0), self.line_height),
8123            },
8124            CursorShape::Block | CursorShape::Hollow => Bounds {
8125                origin: self.origin + origin,
8126                size: size(self.block_width, self.line_height),
8127            },
8128            CursorShape::Underline => Bounds {
8129                origin: self.origin
8130                    + origin
8131                    + gpui::Point::new(Pixels::ZERO, self.line_height - px(2.0)),
8132                size: size(self.block_width, px(2.0)),
8133            },
8134        }
8135    }
8136
8137    pub fn layout(
8138        &mut self,
8139        origin: gpui::Point<Pixels>,
8140        cursor_name: Option<CursorName>,
8141        window: &mut Window,
8142        cx: &mut App,
8143    ) {
8144        if let Some(cursor_name) = cursor_name {
8145            let bounds = self.bounds(origin);
8146            let text_size = self.line_height / 1.5;
8147
8148            let name_origin = if cursor_name.is_top_row {
8149                point(bounds.right() - px(1.), bounds.top())
8150            } else {
8151                match self.shape {
8152                    CursorShape::Bar => point(
8153                        bounds.right() - px(2.),
8154                        bounds.top() - text_size / 2. - px(1.),
8155                    ),
8156                    _ => point(
8157                        bounds.right() - px(1.),
8158                        bounds.top() - text_size / 2. - px(1.),
8159                    ),
8160                }
8161            };
8162            let mut name_element = div()
8163                .bg(self.color)
8164                .text_size(text_size)
8165                .px_0p5()
8166                .line_height(text_size + px(2.))
8167                .text_color(cursor_name.color)
8168                .child(cursor_name.string.clone())
8169                .into_any_element();
8170
8171            name_element.prepaint_as_root(name_origin, AvailableSpace::min_size(), window, cx);
8172
8173            self.cursor_name = Some(name_element);
8174        }
8175    }
8176
8177    pub fn paint(&mut self, origin: gpui::Point<Pixels>, window: &mut Window, cx: &mut App) {
8178        let bounds = self.bounds(origin);
8179
8180        //Draw background or border quad
8181        let cursor = if matches!(self.shape, CursorShape::Hollow) {
8182            outline(bounds, self.color, BorderStyle::Solid)
8183        } else {
8184            fill(bounds, self.color)
8185        };
8186
8187        if let Some(name) = &mut self.cursor_name {
8188            name.paint(window, cx);
8189        }
8190
8191        window.paint_quad(cursor);
8192
8193        if let Some(block_text) = &self.block_text {
8194            block_text
8195                .paint(self.origin + origin, self.line_height, window, cx)
8196                .log_err();
8197        }
8198    }
8199
8200    pub fn shape(&self) -> CursorShape {
8201        self.shape
8202    }
8203}
8204
8205#[derive(Debug)]
8206pub struct HighlightedRange {
8207    pub start_y: Pixels,
8208    pub line_height: Pixels,
8209    pub lines: Vec<HighlightedRangeLine>,
8210    pub color: Hsla,
8211    pub corner_radius: Pixels,
8212}
8213
8214#[derive(Debug)]
8215pub struct HighlightedRangeLine {
8216    pub start_x: Pixels,
8217    pub end_x: Pixels,
8218}
8219
8220impl HighlightedRange {
8221    pub fn paint(&self, bounds: Bounds<Pixels>, window: &mut Window) {
8222        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
8223            self.paint_lines(self.start_y, &self.lines[0..1], bounds, window);
8224            self.paint_lines(
8225                self.start_y + self.line_height,
8226                &self.lines[1..],
8227                bounds,
8228                window,
8229            );
8230        } else {
8231            self.paint_lines(self.start_y, &self.lines, bounds, window);
8232        }
8233    }
8234
8235    fn paint_lines(
8236        &self,
8237        start_y: Pixels,
8238        lines: &[HighlightedRangeLine],
8239        _bounds: Bounds<Pixels>,
8240        window: &mut Window,
8241    ) {
8242        if lines.is_empty() {
8243            return;
8244        }
8245
8246        let first_line = lines.first().unwrap();
8247        let last_line = lines.last().unwrap();
8248
8249        let first_top_left = point(first_line.start_x, start_y);
8250        let first_top_right = point(first_line.end_x, start_y);
8251
8252        let curve_height = point(Pixels::ZERO, self.corner_radius);
8253        let curve_width = |start_x: Pixels, end_x: Pixels| {
8254            let max = (end_x - start_x) / 2.;
8255            let width = if max < self.corner_radius {
8256                max
8257            } else {
8258                self.corner_radius
8259            };
8260
8261            point(width, Pixels::ZERO)
8262        };
8263
8264        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
8265        let mut builder = gpui::PathBuilder::fill();
8266        builder.move_to(first_top_right - top_curve_width);
8267        builder.curve_to(first_top_right + curve_height, first_top_right);
8268
8269        let mut iter = lines.iter().enumerate().peekable();
8270        while let Some((ix, line)) = iter.next() {
8271            let bottom_right = point(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
8272
8273            if let Some((_, next_line)) = iter.peek() {
8274                let next_top_right = point(next_line.end_x, bottom_right.y);
8275
8276                match next_top_right.x.partial_cmp(&bottom_right.x).unwrap() {
8277                    Ordering::Equal => {
8278                        builder.line_to(bottom_right);
8279                    }
8280                    Ordering::Less => {
8281                        let curve_width = curve_width(next_top_right.x, bottom_right.x);
8282                        builder.line_to(bottom_right - curve_height);
8283                        if self.corner_radius > Pixels::ZERO {
8284                            builder.curve_to(bottom_right - curve_width, bottom_right);
8285                        }
8286                        builder.line_to(next_top_right + curve_width);
8287                        if self.corner_radius > Pixels::ZERO {
8288                            builder.curve_to(next_top_right + curve_height, next_top_right);
8289                        }
8290                    }
8291                    Ordering::Greater => {
8292                        let curve_width = curve_width(bottom_right.x, next_top_right.x);
8293                        builder.line_to(bottom_right - curve_height);
8294                        if self.corner_radius > Pixels::ZERO {
8295                            builder.curve_to(bottom_right + curve_width, bottom_right);
8296                        }
8297                        builder.line_to(next_top_right - curve_width);
8298                        if self.corner_radius > Pixels::ZERO {
8299                            builder.curve_to(next_top_right + curve_height, next_top_right);
8300                        }
8301                    }
8302                }
8303            } else {
8304                let curve_width = curve_width(line.start_x, line.end_x);
8305                builder.line_to(bottom_right - curve_height);
8306                if self.corner_radius > Pixels::ZERO {
8307                    builder.curve_to(bottom_right - curve_width, bottom_right);
8308                }
8309
8310                let bottom_left = point(line.start_x, bottom_right.y);
8311                builder.line_to(bottom_left + curve_width);
8312                if self.corner_radius > Pixels::ZERO {
8313                    builder.curve_to(bottom_left - curve_height, bottom_left);
8314                }
8315            }
8316        }
8317
8318        if first_line.start_x > last_line.start_x {
8319            let curve_width = curve_width(last_line.start_x, first_line.start_x);
8320            let second_top_left = point(last_line.start_x, start_y + self.line_height);
8321            builder.line_to(second_top_left + curve_height);
8322            if self.corner_radius > Pixels::ZERO {
8323                builder.curve_to(second_top_left + curve_width, second_top_left);
8324            }
8325            let first_bottom_left = point(first_line.start_x, second_top_left.y);
8326            builder.line_to(first_bottom_left - curve_width);
8327            if self.corner_radius > Pixels::ZERO {
8328                builder.curve_to(first_bottom_left - curve_height, first_bottom_left);
8329            }
8330        }
8331
8332        builder.line_to(first_top_left + curve_height);
8333        if self.corner_radius > Pixels::ZERO {
8334            builder.curve_to(first_top_left + top_curve_width, first_top_left);
8335        }
8336        builder.line_to(first_top_right - top_curve_width);
8337
8338        if let Ok(path) = builder.build() {
8339            window.paint_path(path, self.color);
8340        }
8341    }
8342}
8343
8344enum CursorPopoverType {
8345    CodeContextMenu,
8346    EditPrediction,
8347}
8348
8349pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 {
8350    (delta.pow(1.5) / 100.0).into()
8351}
8352
8353fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
8354    (delta.pow(1.2) / 300.0).into()
8355}
8356
8357pub fn register_action<T: Action>(
8358    editor: &Entity<Editor>,
8359    window: &mut Window,
8360    listener: impl Fn(&mut Editor, &T, &mut Window, &mut Context<Editor>) + 'static,
8361) {
8362    let editor = editor.clone();
8363    window.on_action(TypeId::of::<T>(), move |action, phase, window, cx| {
8364        let action = action.downcast_ref().unwrap();
8365        if phase == DispatchPhase::Bubble {
8366            editor.update(cx, |editor, cx| {
8367                listener(editor, action, window, cx);
8368            })
8369        }
8370    })
8371}
8372
8373fn compute_auto_height_layout(
8374    editor: &mut Editor,
8375    max_lines: usize,
8376    max_line_number_width: Pixels,
8377    known_dimensions: Size<Option<Pixels>>,
8378    available_width: AvailableSpace,
8379    window: &mut Window,
8380    cx: &mut Context<Editor>,
8381) -> Option<Size<Pixels>> {
8382    let width = known_dimensions.width.or({
8383        if let AvailableSpace::Definite(available_width) = available_width {
8384            Some(available_width)
8385        } else {
8386            None
8387        }
8388    })?;
8389    if let Some(height) = known_dimensions.height {
8390        return Some(size(width, height));
8391    }
8392
8393    let style = editor.style.as_ref().unwrap();
8394    let font_id = window.text_system().resolve_font(&style.text.font());
8395    let font_size = style.text.font_size.to_pixels(window.rem_size());
8396    let line_height = style.text.line_height_in_pixels(window.rem_size());
8397    let em_width = window.text_system().em_width(font_id, font_size).unwrap();
8398
8399    let mut snapshot = editor.snapshot(window, cx);
8400    let gutter_dimensions = snapshot
8401        .gutter_dimensions(font_id, font_size, max_line_number_width, cx)
8402        .unwrap_or_default();
8403
8404    editor.gutter_dimensions = gutter_dimensions;
8405    let text_width = width - gutter_dimensions.width;
8406    let overscroll = size(em_width, px(0.));
8407
8408    let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width;
8409    if editor.set_wrap_width(Some(editor_width), cx) {
8410        snapshot = editor.snapshot(window, cx);
8411    }
8412
8413    let scroll_height = (snapshot.max_point().row().next_row().0 as f32) * line_height;
8414    let height = scroll_height
8415        .max(line_height)
8416        .min(line_height * max_lines as f32);
8417
8418    Some(size(width, height))
8419}
8420
8421#[cfg(test)]
8422mod tests {
8423    use super::*;
8424    use crate::{
8425        Editor, MultiBuffer,
8426        display_map::{BlockPlacement, BlockProperties},
8427        editor_tests::{init_test, update_test_language_settings},
8428    };
8429    use gpui::{TestAppContext, VisualTestContext};
8430    use language::language_settings;
8431    use log::info;
8432    use std::num::NonZeroU32;
8433    use util::test::sample_text;
8434
8435    #[gpui::test]
8436    fn test_shape_line_numbers(cx: &mut TestAppContext) {
8437        init_test(cx, |_| {});
8438        let window = cx.add_window(|window, cx| {
8439            let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
8440            Editor::new(EditorMode::Full, buffer, None, window, cx)
8441        });
8442
8443        let editor = window.root(cx).unwrap();
8444        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
8445        let line_height = window
8446            .update(cx, |_, window, _| {
8447                style.text.line_height_in_pixels(window.rem_size())
8448            })
8449            .unwrap();
8450        let element = EditorElement::new(&editor, style);
8451        let snapshot = window
8452            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
8453            .unwrap();
8454
8455        let layouts = cx
8456            .update_window(*window, |_, window, cx| {
8457                element.layout_line_numbers(
8458                    None,
8459                    GutterDimensions {
8460                        left_padding: Pixels::ZERO,
8461                        right_padding: Pixels::ZERO,
8462                        width: px(30.0),
8463                        margin: Pixels::ZERO,
8464                        git_blame_entries_width: None,
8465                    },
8466                    line_height,
8467                    gpui::Point::default(),
8468                    DisplayRow(0)..DisplayRow(6),
8469                    &(0..6)
8470                        .map(|row| RowInfo {
8471                            buffer_row: Some(row),
8472                            ..Default::default()
8473                        })
8474                        .collect::<Vec<_>>(),
8475                    &BTreeMap::default(),
8476                    Some(DisplayPoint::new(DisplayRow(0), 0)),
8477                    &snapshot,
8478                    window,
8479                    cx,
8480                )
8481            })
8482            .unwrap();
8483        assert_eq!(layouts.len(), 6);
8484
8485        let relative_rows = window
8486            .update(cx, |editor, window, cx| {
8487                let snapshot = editor.snapshot(window, cx);
8488                element.calculate_relative_line_numbers(
8489                    &snapshot,
8490                    &(DisplayRow(0)..DisplayRow(6)),
8491                    Some(DisplayRow(3)),
8492                )
8493            })
8494            .unwrap();
8495        assert_eq!(relative_rows[&DisplayRow(0)], 3);
8496        assert_eq!(relative_rows[&DisplayRow(1)], 2);
8497        assert_eq!(relative_rows[&DisplayRow(2)], 1);
8498        // current line has no relative number
8499        assert_eq!(relative_rows[&DisplayRow(4)], 1);
8500        assert_eq!(relative_rows[&DisplayRow(5)], 2);
8501
8502        // works if cursor is before screen
8503        let relative_rows = window
8504            .update(cx, |editor, window, cx| {
8505                let snapshot = editor.snapshot(window, cx);
8506                element.calculate_relative_line_numbers(
8507                    &snapshot,
8508                    &(DisplayRow(3)..DisplayRow(6)),
8509                    Some(DisplayRow(1)),
8510                )
8511            })
8512            .unwrap();
8513        assert_eq!(relative_rows.len(), 3);
8514        assert_eq!(relative_rows[&DisplayRow(3)], 2);
8515        assert_eq!(relative_rows[&DisplayRow(4)], 3);
8516        assert_eq!(relative_rows[&DisplayRow(5)], 4);
8517
8518        // works if cursor is after screen
8519        let relative_rows = window
8520            .update(cx, |editor, window, cx| {
8521                let snapshot = editor.snapshot(window, cx);
8522                element.calculate_relative_line_numbers(
8523                    &snapshot,
8524                    &(DisplayRow(0)..DisplayRow(3)),
8525                    Some(DisplayRow(6)),
8526                )
8527            })
8528            .unwrap();
8529        assert_eq!(relative_rows.len(), 3);
8530        assert_eq!(relative_rows[&DisplayRow(0)], 5);
8531        assert_eq!(relative_rows[&DisplayRow(1)], 4);
8532        assert_eq!(relative_rows[&DisplayRow(2)], 3);
8533    }
8534
8535    #[gpui::test]
8536    async fn test_vim_visual_selections(cx: &mut TestAppContext) {
8537        init_test(cx, |_| {});
8538
8539        let window = cx.add_window(|window, cx| {
8540            let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
8541            Editor::new(EditorMode::Full, buffer, None, window, cx)
8542        });
8543        let cx = &mut VisualTestContext::from_window(*window, cx);
8544        let editor = window.root(cx).unwrap();
8545        let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
8546
8547        window
8548            .update(cx, |editor, window, cx| {
8549                editor.cursor_shape = CursorShape::Block;
8550                editor.change_selections(None, window, cx, |s| {
8551                    s.select_ranges([
8552                        Point::new(0, 0)..Point::new(1, 0),
8553                        Point::new(3, 2)..Point::new(3, 3),
8554                        Point::new(5, 6)..Point::new(6, 0),
8555                    ]);
8556                });
8557            })
8558            .unwrap();
8559
8560        let (_, state) = cx.draw(
8561            point(px(500.), px(500.)),
8562            size(px(500.), px(500.)),
8563            |_, _| EditorElement::new(&editor, style),
8564        );
8565
8566        assert_eq!(state.selections.len(), 1);
8567        let local_selections = &state.selections[0].1;
8568        assert_eq!(local_selections.len(), 3);
8569        // moves cursor back one line
8570        assert_eq!(
8571            local_selections[0].head,
8572            DisplayPoint::new(DisplayRow(0), 6)
8573        );
8574        assert_eq!(
8575            local_selections[0].range,
8576            DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(1), 0)
8577        );
8578
8579        // moves cursor back one column
8580        assert_eq!(
8581            local_selections[1].range,
8582            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 3)
8583        );
8584        assert_eq!(
8585            local_selections[1].head,
8586            DisplayPoint::new(DisplayRow(3), 2)
8587        );
8588
8589        // leaves cursor on the max point
8590        assert_eq!(
8591            local_selections[2].range,
8592            DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(6), 0)
8593        );
8594        assert_eq!(
8595            local_selections[2].head,
8596            DisplayPoint::new(DisplayRow(6), 0)
8597        );
8598
8599        // active lines does not include 1 (even though the range of the selection does)
8600        assert_eq!(
8601            state.active_rows.keys().cloned().collect::<Vec<_>>(),
8602            vec![DisplayRow(0), DisplayRow(3), DisplayRow(5), DisplayRow(6)]
8603        );
8604    }
8605
8606    #[gpui::test]
8607    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
8608        init_test(cx, |_| {});
8609
8610        let window = cx.add_window(|window, cx| {
8611            let buffer = MultiBuffer::build_simple("", cx);
8612            Editor::new(EditorMode::Full, buffer, None, window, cx)
8613        });
8614        let cx = &mut VisualTestContext::from_window(*window, cx);
8615        let editor = window.root(cx).unwrap();
8616        let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
8617        window
8618            .update(cx, |editor, window, cx| {
8619                editor.set_placeholder_text("hello", cx);
8620                editor.insert_blocks(
8621                    [BlockProperties {
8622                        style: BlockStyle::Fixed,
8623                        placement: BlockPlacement::Above(Anchor::min()),
8624                        height: 3,
8625                        render: Arc::new(|cx| div().h(3. * cx.window.line_height()).into_any()),
8626                        priority: 0,
8627                    }],
8628                    None,
8629                    cx,
8630                );
8631
8632                // Blur the editor so that it displays placeholder text.
8633                window.blur();
8634            })
8635            .unwrap();
8636
8637        let (_, state) = cx.draw(
8638            point(px(500.), px(500.)),
8639            size(px(500.), px(500.)),
8640            |_, _| EditorElement::new(&editor, style),
8641        );
8642        assert_eq!(state.position_map.line_layouts.len(), 4);
8643        assert_eq!(state.line_numbers.len(), 1);
8644        assert_eq!(
8645            state
8646                .line_numbers
8647                .get(&MultiBufferRow(0))
8648                .map(|line_number| line_number.shaped_line.text.as_ref()),
8649            Some("1")
8650        );
8651    }
8652
8653    #[gpui::test]
8654    fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
8655        const TAB_SIZE: u32 = 4;
8656
8657        let input_text = "\t \t|\t| a b";
8658        let expected_invisibles = vec![
8659            Invisible::Tab {
8660                line_start_offset: 0,
8661                line_end_offset: TAB_SIZE as usize,
8662            },
8663            Invisible::Whitespace {
8664                line_offset: TAB_SIZE as usize,
8665            },
8666            Invisible::Tab {
8667                line_start_offset: TAB_SIZE as usize + 1,
8668                line_end_offset: TAB_SIZE as usize * 2,
8669            },
8670            Invisible::Tab {
8671                line_start_offset: TAB_SIZE as usize * 2 + 1,
8672                line_end_offset: TAB_SIZE as usize * 3,
8673            },
8674            Invisible::Whitespace {
8675                line_offset: TAB_SIZE as usize * 3 + 1,
8676            },
8677            Invisible::Whitespace {
8678                line_offset: TAB_SIZE as usize * 3 + 3,
8679            },
8680        ];
8681        assert_eq!(
8682            expected_invisibles.len(),
8683            input_text
8684                .chars()
8685                .filter(|initial_char| initial_char.is_whitespace())
8686                .count(),
8687            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
8688        );
8689
8690        for show_line_numbers in [true, false] {
8691            init_test(cx, |s| {
8692                s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
8693                s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
8694            });
8695
8696            let actual_invisibles = collect_invisibles_from_new_editor(
8697                cx,
8698                EditorMode::Full,
8699                input_text,
8700                px(500.0),
8701                show_line_numbers,
8702            );
8703
8704            assert_eq!(expected_invisibles, actual_invisibles);
8705        }
8706    }
8707
8708    #[gpui::test]
8709    fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
8710        init_test(cx, |s| {
8711            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
8712            s.defaults.tab_size = NonZeroU32::new(4);
8713        });
8714
8715        for editor_mode_without_invisibles in [
8716            EditorMode::SingleLine { auto_width: false },
8717            EditorMode::AutoHeight { max_lines: 100 },
8718        ] {
8719            for show_line_numbers in [true, false] {
8720                let invisibles = collect_invisibles_from_new_editor(
8721                    cx,
8722                    editor_mode_without_invisibles,
8723                    "\t\t\t| | a b",
8724                    px(500.0),
8725                    show_line_numbers,
8726                );
8727                assert!(
8728                    invisibles.is_empty(),
8729                    "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}"
8730                );
8731            }
8732        }
8733    }
8734
8735    #[gpui::test]
8736    fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) {
8737        let tab_size = 4;
8738        let input_text = "a\tbcd     ".repeat(9);
8739        let repeated_invisibles = [
8740            Invisible::Tab {
8741                line_start_offset: 1,
8742                line_end_offset: tab_size as usize,
8743            },
8744            Invisible::Whitespace {
8745                line_offset: tab_size as usize + 3,
8746            },
8747            Invisible::Whitespace {
8748                line_offset: tab_size as usize + 4,
8749            },
8750            Invisible::Whitespace {
8751                line_offset: tab_size as usize + 5,
8752            },
8753            Invisible::Whitespace {
8754                line_offset: tab_size as usize + 6,
8755            },
8756            Invisible::Whitespace {
8757                line_offset: tab_size as usize + 7,
8758            },
8759        ];
8760        let expected_invisibles = std::iter::once(repeated_invisibles)
8761            .cycle()
8762            .take(9)
8763            .flatten()
8764            .collect::<Vec<_>>();
8765        assert_eq!(
8766            expected_invisibles.len(),
8767            input_text
8768                .chars()
8769                .filter(|initial_char| initial_char.is_whitespace())
8770                .count(),
8771            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
8772        );
8773        info!("Expected invisibles: {expected_invisibles:?}");
8774
8775        init_test(cx, |_| {});
8776
8777        // Put the same string with repeating whitespace pattern into editors of various size,
8778        // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
8779        let resize_step = 10.0;
8780        let mut editor_width = 200.0;
8781        while editor_width <= 1000.0 {
8782            for show_line_numbers in [true, false] {
8783                update_test_language_settings(cx, |s| {
8784                    s.defaults.tab_size = NonZeroU32::new(tab_size);
8785                    s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
8786                    s.defaults.preferred_line_length = Some(editor_width as u32);
8787                    s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
8788                });
8789
8790                let actual_invisibles = collect_invisibles_from_new_editor(
8791                    cx,
8792                    EditorMode::Full,
8793                    &input_text,
8794                    px(editor_width),
8795                    show_line_numbers,
8796                );
8797
8798                // Whatever the editor size is, ensure it has the same invisible kinds in the same order
8799                // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets).
8800                let mut i = 0;
8801                for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() {
8802                    i = actual_index;
8803                    match expected_invisibles.get(i) {
8804                        Some(expected_invisible) => match (expected_invisible, actual_invisible) {
8805                            (Invisible::Whitespace { .. }, Invisible::Whitespace { .. })
8806                            | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {}
8807                            _ => {
8808                                panic!(
8809                                    "At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}"
8810                                )
8811                            }
8812                        },
8813                        None => {
8814                            panic!("Unexpected extra invisible {actual_invisible:?} at index {i}")
8815                        }
8816                    }
8817                }
8818                let missing_expected_invisibles = &expected_invisibles[i + 1..];
8819                assert!(
8820                    missing_expected_invisibles.is_empty(),
8821                    "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}"
8822                );
8823
8824                editor_width += resize_step;
8825            }
8826        }
8827    }
8828
8829    fn collect_invisibles_from_new_editor(
8830        cx: &mut TestAppContext,
8831        editor_mode: EditorMode,
8832        input_text: &str,
8833        editor_width: Pixels,
8834        show_line_numbers: bool,
8835    ) -> Vec<Invisible> {
8836        info!(
8837            "Creating editor with mode {editor_mode:?}, width {}px and text '{input_text}'",
8838            editor_width.0
8839        );
8840        let window = cx.add_window(|window, cx| {
8841            let buffer = MultiBuffer::build_simple(input_text, cx);
8842            Editor::new(editor_mode, buffer, None, window, cx)
8843        });
8844        let cx = &mut VisualTestContext::from_window(*window, cx);
8845        let editor = window.root(cx).unwrap();
8846
8847        let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
8848        window
8849            .update(cx, |editor, _, cx| {
8850                editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
8851                editor.set_wrap_width(Some(editor_width), cx);
8852                editor.set_show_line_numbers(show_line_numbers, cx);
8853            })
8854            .unwrap();
8855        let (_, state) = cx.draw(
8856            point(px(500.), px(500.)),
8857            size(px(500.), px(500.)),
8858            |_, _| EditorElement::new(&editor, style),
8859        );
8860        state
8861            .position_map
8862            .line_layouts
8863            .iter()
8864            .flat_map(|line_with_invisibles| &line_with_invisibles.invisibles)
8865            .cloned()
8866            .collect()
8867    }
8868}