element.rs

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