element.rs

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