element.rs

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