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