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