element.rs

   1use crate::{
   2    display_map::{
   3        BlockContext, BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint,
   4        TransformBlock,
   5    },
   6    editor_settings::ShowScrollbar,
   7    git::{diff_hunk_to_display, DisplayDiffHunk},
   8    hover_popover::{
   9        self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
  10    },
  11    items::BufferSearchHighlights,
  12    mouse_context_menu,
  13    scroll::scroll_amount::ScrollAmount,
  14    CursorShape, DisplayPoint, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode,
  15    EditorSettings, EditorSnapshot, EditorStyle, GutterDimensions, HalfPageDown, HalfPageUp,
  16    HoveredCursor, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, SelectPhase, Selection,
  17    SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
  18};
  19use anyhow::Result;
  20use collections::{BTreeMap, HashMap};
  21use git::diff::DiffHunkStatus;
  22use gpui::{
  23    div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
  24    AnchorCorner, AnyElement, AvailableSpace, Bounds, ContentMask, Corners, CursorStyle,
  25    DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla, InteractiveBounds,
  26    InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
  27    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
  28    SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
  29    TextStyle, View, ViewContext, WindowContext,
  30};
  31use itertools::Itertools;
  32use language::language_settings::ShowWhitespaceSetting;
  33use lsp::DiagnosticSeverity;
  34use multi_buffer::Anchor;
  35use project::{
  36    project_settings::{GitGutterSetting, ProjectSettings},
  37    ProjectPath,
  38};
  39use settings::Settings;
  40use smallvec::SmallVec;
  41use std::{
  42    any::TypeId,
  43    borrow::Cow,
  44    cmp::{self, Ordering},
  45    fmt::Write,
  46    iter,
  47    ops::Range,
  48    sync::Arc,
  49};
  50use sum_tree::Bias;
  51use theme::{ActiveTheme, PlayerColor};
  52use ui::prelude::*;
  53use ui::{h_flex, ButtonLike, ButtonStyle, IconButton, Tooltip};
  54use util::ResultExt;
  55use workspace::item::Item;
  56
  57struct SelectionLayout {
  58    head: DisplayPoint,
  59    cursor_shape: CursorShape,
  60    is_newest: bool,
  61    is_local: bool,
  62    range: Range<DisplayPoint>,
  63    active_rows: Range<u32>,
  64    user_name: Option<SharedString>,
  65}
  66
  67impl SelectionLayout {
  68    fn new<T: ToPoint + ToDisplayPoint + Clone>(
  69        selection: Selection<T>,
  70        line_mode: bool,
  71        cursor_shape: CursorShape,
  72        map: &DisplaySnapshot,
  73        is_newest: bool,
  74        is_local: bool,
  75        user_name: Option<SharedString>,
  76    ) -> Self {
  77        let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
  78        let display_selection = point_selection.map(|p| p.to_display_point(map));
  79        let mut range = display_selection.range();
  80        let mut head = display_selection.head();
  81        let mut active_rows = map.prev_line_boundary(point_selection.start).1.row()
  82            ..map.next_line_boundary(point_selection.end).1.row();
  83
  84        // vim visual line mode
  85        if line_mode {
  86            let point_range = map.expand_to_line(point_selection.range());
  87            range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map);
  88        }
  89
  90        // any vim visual mode (including line mode)
  91        if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed {
  92            if head.column() > 0 {
  93                head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left)
  94            } else if head.row() > 0 && head != map.max_point() {
  95                head = map.clip_point(
  96                    DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)),
  97                    Bias::Left,
  98                );
  99                // updating range.end is a no-op unless you're cursor is
 100                // on the newline containing a multi-buffer divider
 101                // in which case the clip_point may have moved the head up
 102                // an additional row.
 103                range.end = DisplayPoint::new(head.row() + 1, 0);
 104                active_rows.end = head.row();
 105            }
 106        }
 107
 108        Self {
 109            head,
 110            cursor_shape,
 111            is_newest,
 112            is_local,
 113            range,
 114            active_rows,
 115            user_name,
 116        }
 117    }
 118}
 119
 120pub struct EditorElement {
 121    editor: View<Editor>,
 122    style: EditorStyle,
 123}
 124
 125impl EditorElement {
 126    pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
 127        Self {
 128            editor: editor.clone(),
 129            style,
 130        }
 131    }
 132
 133    fn register_actions(&self, cx: &mut WindowContext) {
 134        let view = &self.editor;
 135        view.update(cx, |editor, cx| {
 136            for action in editor.editor_actions.iter() {
 137                (action)(cx)
 138            }
 139        });
 140
 141        crate::rust_analyzer_ext::apply_related_actions(view, cx);
 142        register_action(view, cx, Editor::move_left);
 143        register_action(view, cx, Editor::move_right);
 144        register_action(view, cx, Editor::move_down);
 145        register_action(view, cx, Editor::move_down_by_lines);
 146        register_action(view, cx, Editor::select_down_by_lines);
 147        register_action(view, cx, Editor::move_up);
 148        register_action(view, cx, Editor::move_up_by_lines);
 149        register_action(view, cx, Editor::select_up_by_lines);
 150        register_action(view, cx, Editor::cancel);
 151        register_action(view, cx, Editor::newline);
 152        register_action(view, cx, Editor::newline_above);
 153        register_action(view, cx, Editor::newline_below);
 154        register_action(view, cx, Editor::backspace);
 155        register_action(view, cx, Editor::delete);
 156        register_action(view, cx, Editor::tab);
 157        register_action(view, cx, Editor::tab_prev);
 158        register_action(view, cx, Editor::indent);
 159        register_action(view, cx, Editor::outdent);
 160        register_action(view, cx, Editor::delete_line);
 161        register_action(view, cx, Editor::join_lines);
 162        register_action(view, cx, Editor::sort_lines_case_sensitive);
 163        register_action(view, cx, Editor::sort_lines_case_insensitive);
 164        register_action(view, cx, Editor::reverse_lines);
 165        register_action(view, cx, Editor::shuffle_lines);
 166        register_action(view, cx, Editor::convert_to_upper_case);
 167        register_action(view, cx, Editor::convert_to_lower_case);
 168        register_action(view, cx, Editor::convert_to_title_case);
 169        register_action(view, cx, Editor::convert_to_snake_case);
 170        register_action(view, cx, Editor::convert_to_kebab_case);
 171        register_action(view, cx, Editor::convert_to_upper_camel_case);
 172        register_action(view, cx, Editor::convert_to_lower_camel_case);
 173        register_action(view, cx, Editor::delete_to_previous_word_start);
 174        register_action(view, cx, Editor::delete_to_previous_subword_start);
 175        register_action(view, cx, Editor::delete_to_next_word_end);
 176        register_action(view, cx, Editor::delete_to_next_subword_end);
 177        register_action(view, cx, Editor::delete_to_beginning_of_line);
 178        register_action(view, cx, Editor::delete_to_end_of_line);
 179        register_action(view, cx, Editor::cut_to_end_of_line);
 180        register_action(view, cx, Editor::duplicate_line);
 181        register_action(view, cx, Editor::move_line_up);
 182        register_action(view, cx, Editor::move_line_down);
 183        register_action(view, cx, Editor::transpose);
 184        register_action(view, cx, Editor::cut);
 185        register_action(view, cx, Editor::copy);
 186        register_action(view, cx, Editor::paste);
 187        register_action(view, cx, Editor::undo);
 188        register_action(view, cx, Editor::redo);
 189        register_action(view, cx, Editor::move_page_up);
 190        register_action(view, cx, Editor::move_page_down);
 191        register_action(view, cx, Editor::next_screen);
 192        register_action(view, cx, Editor::scroll_cursor_top);
 193        register_action(view, cx, Editor::scroll_cursor_center);
 194        register_action(view, cx, Editor::scroll_cursor_bottom);
 195        register_action(view, cx, |editor, _: &LineDown, cx| {
 196            editor.scroll_screen(&ScrollAmount::Line(1.), cx)
 197        });
 198        register_action(view, cx, |editor, _: &LineUp, cx| {
 199            editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
 200        });
 201        register_action(view, cx, |editor, _: &HalfPageDown, cx| {
 202            editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
 203        });
 204        register_action(view, cx, |editor, _: &HalfPageUp, cx| {
 205            editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
 206        });
 207        register_action(view, cx, |editor, _: &PageDown, cx| {
 208            editor.scroll_screen(&ScrollAmount::Page(1.), cx)
 209        });
 210        register_action(view, cx, |editor, _: &PageUp, cx| {
 211            editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
 212        });
 213        register_action(view, cx, Editor::move_to_previous_word_start);
 214        register_action(view, cx, Editor::move_to_previous_subword_start);
 215        register_action(view, cx, Editor::move_to_next_word_end);
 216        register_action(view, cx, Editor::move_to_next_subword_end);
 217        register_action(view, cx, Editor::move_to_beginning_of_line);
 218        register_action(view, cx, Editor::move_to_end_of_line);
 219        register_action(view, cx, Editor::move_to_start_of_paragraph);
 220        register_action(view, cx, Editor::move_to_end_of_paragraph);
 221        register_action(view, cx, Editor::move_to_beginning);
 222        register_action(view, cx, Editor::move_to_end);
 223        register_action(view, cx, Editor::select_up);
 224        register_action(view, cx, Editor::select_down);
 225        register_action(view, cx, Editor::select_left);
 226        register_action(view, cx, Editor::select_right);
 227        register_action(view, cx, Editor::select_to_previous_word_start);
 228        register_action(view, cx, Editor::select_to_previous_subword_start);
 229        register_action(view, cx, Editor::select_to_next_word_end);
 230        register_action(view, cx, Editor::select_to_next_subword_end);
 231        register_action(view, cx, Editor::select_to_beginning_of_line);
 232        register_action(view, cx, Editor::select_to_end_of_line);
 233        register_action(view, cx, Editor::select_to_start_of_paragraph);
 234        register_action(view, cx, Editor::select_to_end_of_paragraph);
 235        register_action(view, cx, Editor::select_to_beginning);
 236        register_action(view, cx, Editor::select_to_end);
 237        register_action(view, cx, Editor::select_all);
 238        register_action(view, cx, |editor, action, cx| {
 239            editor.select_all_matches(action, cx).log_err();
 240        });
 241        register_action(view, cx, Editor::select_line);
 242        register_action(view, cx, Editor::split_selection_into_lines);
 243        register_action(view, cx, Editor::add_selection_above);
 244        register_action(view, cx, Editor::add_selection_below);
 245        register_action(view, cx, |editor, action, cx| {
 246            editor.select_next(action, cx).log_err();
 247        });
 248        register_action(view, cx, |editor, action, cx| {
 249            editor.select_previous(action, cx).log_err();
 250        });
 251        register_action(view, cx, Editor::toggle_comments);
 252        register_action(view, cx, Editor::select_larger_syntax_node);
 253        register_action(view, cx, Editor::select_smaller_syntax_node);
 254        register_action(view, cx, Editor::move_to_enclosing_bracket);
 255        register_action(view, cx, Editor::undo_selection);
 256        register_action(view, cx, Editor::redo_selection);
 257        register_action(view, cx, Editor::go_to_diagnostic);
 258        register_action(view, cx, Editor::go_to_prev_diagnostic);
 259        register_action(view, cx, Editor::go_to_hunk);
 260        register_action(view, cx, Editor::go_to_prev_hunk);
 261        register_action(view, cx, Editor::go_to_definition);
 262        register_action(view, cx, Editor::go_to_definition_split);
 263        register_action(view, cx, Editor::go_to_implementation);
 264        register_action(view, cx, Editor::go_to_implementation_split);
 265        register_action(view, cx, Editor::go_to_type_definition);
 266        register_action(view, cx, Editor::go_to_type_definition_split);
 267        register_action(view, cx, Editor::open_url);
 268        register_action(view, cx, Editor::fold);
 269        register_action(view, cx, Editor::fold_at);
 270        register_action(view, cx, Editor::unfold_lines);
 271        register_action(view, cx, Editor::unfold_at);
 272        register_action(view, cx, Editor::fold_selected_ranges);
 273        register_action(view, cx, Editor::show_completions);
 274        register_action(view, cx, Editor::toggle_code_actions);
 275        register_action(view, cx, Editor::open_excerpts);
 276        register_action(view, cx, Editor::toggle_soft_wrap);
 277        register_action(view, cx, Editor::toggle_inlay_hints);
 278        register_action(view, cx, hover_popover::hover);
 279        register_action(view, cx, Editor::reveal_in_finder);
 280        register_action(view, cx, Editor::copy_path);
 281        register_action(view, cx, Editor::copy_relative_path);
 282        register_action(view, cx, Editor::copy_highlight_json);
 283        register_action(view, cx, Editor::copy_permalink_to_line);
 284        register_action(view, cx, Editor::open_permalink_to_line);
 285        register_action(view, cx, |editor, action, cx| {
 286            if let Some(task) = editor.format(action, cx) {
 287                task.detach_and_log_err(cx);
 288            } else {
 289                cx.propagate();
 290            }
 291        });
 292        register_action(view, cx, Editor::restart_language_server);
 293        register_action(view, cx, Editor::show_character_palette);
 294        register_action(view, cx, |editor, action, cx| {
 295            if let Some(task) = editor.confirm_completion(action, cx) {
 296                task.detach_and_log_err(cx);
 297            } else {
 298                cx.propagate();
 299            }
 300        });
 301        register_action(view, cx, |editor, action, cx| {
 302            if let Some(task) = editor.confirm_code_action(action, cx) {
 303                task.detach_and_log_err(cx);
 304            } else {
 305                cx.propagate();
 306            }
 307        });
 308        register_action(view, cx, |editor, action, cx| {
 309            if let Some(task) = editor.rename(action, cx) {
 310                task.detach_and_log_err(cx);
 311            } else {
 312                cx.propagate();
 313            }
 314        });
 315        register_action(view, cx, |editor, action, cx| {
 316            if let Some(task) = editor.confirm_rename(action, cx) {
 317                task.detach_and_log_err(cx);
 318            } else {
 319                cx.propagate();
 320            }
 321        });
 322        register_action(view, cx, |editor, action, cx| {
 323            if let Some(task) = editor.find_all_references(action, cx) {
 324                task.detach_and_log_err(cx);
 325            } else {
 326                cx.propagate();
 327            }
 328        });
 329        register_action(view, cx, Editor::next_copilot_suggestion);
 330        register_action(view, cx, Editor::previous_copilot_suggestion);
 331        register_action(view, cx, Editor::copilot_suggest);
 332        register_action(view, cx, Editor::context_menu_first);
 333        register_action(view, cx, Editor::context_menu_prev);
 334        register_action(view, cx, Editor::context_menu_next);
 335        register_action(view, cx, Editor::context_menu_last);
 336        register_action(view, cx, Editor::display_cursor_names);
 337        register_action(view, cx, Editor::unique_lines_case_insensitive);
 338        register_action(view, cx, Editor::unique_lines_case_sensitive);
 339    }
 340
 341    fn register_key_listeners(
 342        &self,
 343        cx: &mut ElementContext,
 344        text_bounds: Bounds<Pixels>,
 345        layout: &LayoutState,
 346    ) {
 347        let position_map = layout.position_map.clone();
 348        let stacking_order = cx.stacking_order().clone();
 349        cx.on_key_event({
 350            let editor = self.editor.clone();
 351            move |event: &ModifiersChangedEvent, phase, cx| {
 352                if phase != DispatchPhase::Bubble {
 353                    return;
 354                }
 355
 356                editor.update(cx, |editor, cx| {
 357                    Self::modifiers_changed(
 358                        editor,
 359                        event,
 360                        &position_map,
 361                        text_bounds,
 362                        &stacking_order,
 363                        cx,
 364                    )
 365                })
 366            }
 367        });
 368    }
 369
 370    fn modifiers_changed(
 371        editor: &mut Editor,
 372        event: &ModifiersChangedEvent,
 373        position_map: &PositionMap,
 374        text_bounds: Bounds<Pixels>,
 375        stacking_order: &StackingOrder,
 376        cx: &mut ViewContext<Editor>,
 377    ) {
 378        let mouse_position = cx.mouse_position();
 379        if !text_bounds.contains(&mouse_position)
 380            || !cx.was_top_layer(&mouse_position, stacking_order)
 381        {
 382            return;
 383        }
 384
 385        editor.update_hovered_link(
 386            position_map.point_for_position(text_bounds, mouse_position),
 387            &position_map.snapshot,
 388            event.modifiers,
 389            cx,
 390        )
 391    }
 392
 393    fn mouse_left_down(
 394        editor: &mut Editor,
 395        event: &MouseDownEvent,
 396        position_map: &PositionMap,
 397        text_bounds: Bounds<Pixels>,
 398        gutter_bounds: Bounds<Pixels>,
 399        stacking_order: &StackingOrder,
 400        cx: &mut ViewContext<Editor>,
 401    ) {
 402        let mut click_count = event.click_count;
 403        let modifiers = event.modifiers;
 404
 405        if cx.default_prevented() {
 406            return;
 407        } else if gutter_bounds.contains(&event.position) {
 408            click_count = 3; // Simulate triple-click when clicking the gutter to select lines
 409        } else if !text_bounds.contains(&event.position) {
 410            return;
 411        }
 412        if !cx.was_top_layer(&event.position, stacking_order) {
 413            return;
 414        }
 415
 416        let point_for_position = position_map.point_for_position(text_bounds, event.position);
 417        let position = point_for_position.previous_valid;
 418        if modifiers.shift && modifiers.alt {
 419            editor.select(
 420                SelectPhase::BeginColumnar {
 421                    position,
 422                    goal_column: point_for_position.exact_unclipped.column(),
 423                },
 424                cx,
 425            );
 426        } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.command {
 427            editor.select(
 428                SelectPhase::Extend {
 429                    position,
 430                    click_count,
 431                },
 432                cx,
 433            );
 434        } else {
 435            editor.select(
 436                SelectPhase::Begin {
 437                    position,
 438                    add: modifiers.alt,
 439                    click_count,
 440                },
 441                cx,
 442            );
 443        }
 444
 445        cx.stop_propagation();
 446    }
 447
 448    fn mouse_right_down(
 449        editor: &mut Editor,
 450        event: &MouseDownEvent,
 451        position_map: &PositionMap,
 452        text_bounds: Bounds<Pixels>,
 453        cx: &mut ViewContext<Editor>,
 454    ) {
 455        if !text_bounds.contains(&event.position) {
 456            return;
 457        }
 458        let point_for_position = position_map.point_for_position(text_bounds, event.position);
 459        mouse_context_menu::deploy_context_menu(
 460            editor,
 461            event.position,
 462            point_for_position.previous_valid,
 463            cx,
 464        );
 465        cx.stop_propagation();
 466    }
 467
 468    fn mouse_up(
 469        editor: &mut Editor,
 470        event: &MouseUpEvent,
 471        position_map: &PositionMap,
 472        text_bounds: Bounds<Pixels>,
 473        interactive_bounds: &InteractiveBounds,
 474        stacking_order: &StackingOrder,
 475        cx: &mut ViewContext<Editor>,
 476    ) {
 477        let end_selection = editor.has_pending_selection();
 478        let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 479
 480        if end_selection {
 481            editor.select(SelectPhase::End, cx);
 482        }
 483
 484        if interactive_bounds.visibly_contains(&event.position, cx)
 485            && !pending_nonempty_selections
 486            && event.modifiers.command
 487            && text_bounds.contains(&event.position)
 488            && cx.was_top_layer(&event.position, stacking_order)
 489        {
 490            let point = position_map.point_for_position(text_bounds, event.position);
 491            editor.handle_click_hovered_link(point, event.modifiers, cx);
 492
 493            cx.stop_propagation();
 494        } else if end_selection {
 495            cx.stop_propagation();
 496        }
 497    }
 498
 499    fn mouse_dragged(
 500        editor: &mut Editor,
 501        event: &MouseMoveEvent,
 502        position_map: &PositionMap,
 503        text_bounds: Bounds<Pixels>,
 504        _gutter_bounds: Bounds<Pixels>,
 505        _stacking_order: &StackingOrder,
 506        cx: &mut ViewContext<Editor>,
 507    ) {
 508        if !editor.has_pending_selection() {
 509            return;
 510        }
 511
 512        let point_for_position = position_map.point_for_position(text_bounds, event.position);
 513        let mut scroll_delta = gpui::Point::<f32>::default();
 514        let vertical_margin = position_map.line_height.min(text_bounds.size.height / 3.0);
 515        let top = text_bounds.origin.y + vertical_margin;
 516        let bottom = text_bounds.lower_left().y - vertical_margin;
 517        if event.position.y < top {
 518            scroll_delta.y = -scale_vertical_mouse_autoscroll_delta(top - event.position.y);
 519        }
 520        if event.position.y > bottom {
 521            scroll_delta.y = scale_vertical_mouse_autoscroll_delta(event.position.y - bottom);
 522        }
 523
 524        let horizontal_margin = position_map.line_height.min(text_bounds.size.width / 3.0);
 525        let left = text_bounds.origin.x + horizontal_margin;
 526        let right = text_bounds.upper_right().x - horizontal_margin;
 527        if event.position.x < left {
 528            scroll_delta.x = -scale_horizontal_mouse_autoscroll_delta(left - event.position.x);
 529        }
 530        if event.position.x > right {
 531            scroll_delta.x = scale_horizontal_mouse_autoscroll_delta(event.position.x - right);
 532        }
 533
 534        editor.select(
 535            SelectPhase::Update {
 536                position: point_for_position.previous_valid,
 537                goal_column: point_for_position.exact_unclipped.column(),
 538                scroll_delta,
 539            },
 540            cx,
 541        );
 542    }
 543
 544    fn mouse_moved(
 545        editor: &mut Editor,
 546        event: &MouseMoveEvent,
 547        position_map: &PositionMap,
 548        text_bounds: Bounds<Pixels>,
 549        gutter_bounds: Bounds<Pixels>,
 550        stacking_order: &StackingOrder,
 551        cx: &mut ViewContext<Editor>,
 552    ) {
 553        let modifiers = event.modifiers;
 554        let text_hovered = text_bounds.contains(&event.position);
 555        let gutter_hovered = gutter_bounds.contains(&event.position);
 556        let was_top = cx.was_top_layer(&event.position, stacking_order);
 557
 558        editor.set_gutter_hovered(gutter_hovered, cx);
 559
 560        // Don't trigger hover popover if mouse is hovering over context menu
 561        if text_hovered && was_top {
 562            let point_for_position = position_map.point_for_position(text_bounds, event.position);
 563
 564            editor.update_hovered_link(point_for_position, &position_map.snapshot, modifiers, cx);
 565
 566            if let Some(point) = point_for_position.as_valid() {
 567                hover_at(editor, Some(point), cx);
 568                Self::update_visible_cursor(editor, point, position_map, cx);
 569            }
 570        } else {
 571            editor.hide_hovered_link(cx);
 572            hover_at(editor, None, cx);
 573            if gutter_hovered && was_top {
 574                cx.stop_propagation();
 575            }
 576        }
 577    }
 578
 579    fn update_visible_cursor(
 580        editor: &mut Editor,
 581        point: DisplayPoint,
 582        position_map: &PositionMap,
 583        cx: &mut ViewContext<Editor>,
 584    ) {
 585        let snapshot = &position_map.snapshot;
 586        let Some(hub) = editor.collaboration_hub() else {
 587            return;
 588        };
 589        let range = DisplayPoint::new(point.row(), point.column().saturating_sub(1))
 590            ..DisplayPoint::new(
 591                point.row(),
 592                (point.column() + 1).min(snapshot.line_len(point.row())),
 593            );
 594
 595        let range = snapshot
 596            .buffer_snapshot
 597            .anchor_at(range.start.to_point(&snapshot.display_snapshot), Bias::Left)
 598            ..snapshot
 599                .buffer_snapshot
 600                .anchor_at(range.end.to_point(&snapshot.display_snapshot), Bias::Right);
 601
 602        let Some(selection) = snapshot.remote_selections_in_range(&range, hub, cx).next() else {
 603            return;
 604        };
 605        let key = crate::HoveredCursor {
 606            replica_id: selection.replica_id,
 607            selection_id: selection.selection.id,
 608        };
 609        editor.hovered_cursors.insert(
 610            key.clone(),
 611            cx.spawn(|editor, mut cx| async move {
 612                cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 613                editor
 614                    .update(&mut cx, |editor, cx| {
 615                        editor.hovered_cursors.remove(&key);
 616                        cx.notify();
 617                    })
 618                    .ok();
 619            }),
 620        );
 621        cx.notify()
 622    }
 623
 624    fn paint_background(
 625        &self,
 626        gutter_bounds: Bounds<Pixels>,
 627        text_bounds: Bounds<Pixels>,
 628        layout: &LayoutState,
 629        cx: &mut ElementContext,
 630    ) {
 631        let bounds = gutter_bounds.union(&text_bounds);
 632        let scroll_top =
 633            layout.position_map.snapshot.scroll_position().y * layout.position_map.line_height;
 634        let gutter_bg = cx.theme().colors().editor_gutter_background;
 635        cx.paint_quad(fill(gutter_bounds, gutter_bg));
 636        cx.paint_quad(fill(text_bounds, self.style.background));
 637
 638        if let EditorMode::Full = layout.mode {
 639            let mut active_rows = layout.active_rows.iter().peekable();
 640            while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
 641                let mut end_row = *start_row;
 642                while active_rows.peek().map_or(false, |r| {
 643                    *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
 644                }) {
 645                    active_rows.next().unwrap();
 646                    end_row += 1;
 647                }
 648
 649                if !contains_non_empty_selection {
 650                    let origin = point(
 651                        bounds.origin.x,
 652                        bounds.origin.y + (layout.position_map.line_height * *start_row as f32)
 653                            - scroll_top,
 654                    );
 655                    let size = size(
 656                        bounds.size.width,
 657                        layout.position_map.line_height * (end_row - start_row + 1) as f32,
 658                    );
 659                    let active_line_bg = cx.theme().colors().editor_active_line_background;
 660                    cx.paint_quad(fill(Bounds { origin, size }, active_line_bg));
 661                }
 662            }
 663
 664            if let Some(highlighted_rows) = &layout.highlighted_rows {
 665                let origin = point(
 666                    bounds.origin.x,
 667                    bounds.origin.y
 668                        + (layout.position_map.line_height * highlighted_rows.start as f32)
 669                        - scroll_top,
 670                );
 671                let size = size(
 672                    bounds.size.width,
 673                    layout.position_map.line_height * highlighted_rows.len() as f32,
 674                );
 675                let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background;
 676                cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg));
 677            }
 678
 679            let scroll_left =
 680                layout.position_map.snapshot.scroll_position().x * layout.position_map.em_width;
 681
 682            for (wrap_position, active) in layout.wrap_guides.iter() {
 683                let x = (text_bounds.origin.x + *wrap_position + layout.position_map.em_width / 2.)
 684                    - scroll_left;
 685
 686                if x < text_bounds.origin.x
 687                    || (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
 688                {
 689                    continue;
 690                }
 691
 692                let color = if *active {
 693                    cx.theme().colors().editor_active_wrap_guide
 694                } else {
 695                    cx.theme().colors().editor_wrap_guide
 696                };
 697                cx.paint_quad(fill(
 698                    Bounds {
 699                        origin: point(x, text_bounds.origin.y),
 700                        size: size(px(1.), text_bounds.size.height),
 701                    },
 702                    color,
 703                ));
 704            }
 705        }
 706    }
 707
 708    fn paint_gutter(
 709        &mut self,
 710        bounds: Bounds<Pixels>,
 711        layout: &mut LayoutState,
 712        cx: &mut ElementContext,
 713    ) {
 714        let line_height = layout.position_map.line_height;
 715
 716        let scroll_position = layout.position_map.snapshot.scroll_position();
 717        let scroll_top = scroll_position.y * line_height;
 718
 719        let show_git_gutter = matches!(
 720            ProjectSettings::get_global(cx).git.git_gutter,
 721            Some(GitGutterSetting::TrackedFiles)
 722        );
 723
 724        if show_git_gutter {
 725            Self::paint_diff_hunks(bounds, layout, cx);
 726        }
 727
 728        let gutter_settings = EditorSettings::get_global(cx).gutter;
 729
 730        for (ix, line) in layout.line_numbers.iter().enumerate() {
 731            if let Some(line) = line {
 732                let line_origin = bounds.origin
 733                    + point(
 734                        bounds.size.width - line.width - layout.gutter_dimensions.right_padding,
 735                        ix as f32 * line_height - (scroll_top % line_height),
 736                    );
 737
 738                line.paint(line_origin, line_height, cx).log_err();
 739            }
 740        }
 741
 742        cx.with_z_index(1, |cx| {
 743            for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
 744                if let Some(fold_indicator) = fold_indicator {
 745                    debug_assert!(gutter_settings.folds);
 746                    let mut fold_indicator = fold_indicator.into_any_element();
 747                    let available_space = size(
 748                        AvailableSpace::MinContent,
 749                        AvailableSpace::Definite(line_height * 0.55),
 750                    );
 751                    let fold_indicator_size = fold_indicator.measure(available_space, cx);
 752
 753                    let position = point(
 754                        bounds.size.width - layout.gutter_dimensions.right_padding,
 755                        ix as f32 * line_height - (scroll_top % line_height),
 756                    );
 757                    let centering_offset = point(
 758                        (layout.gutter_dimensions.right_padding + layout.gutter_dimensions.margin
 759                            - fold_indicator_size.width)
 760                            / 2.,
 761                        (line_height - fold_indicator_size.height) / 2.,
 762                    );
 763                    let origin = bounds.origin + position + centering_offset;
 764                    fold_indicator.draw(origin, available_space, cx);
 765                }
 766            }
 767
 768            if let Some(indicator) = layout.code_actions_indicator.take() {
 769                debug_assert!(gutter_settings.code_actions);
 770                let mut button = indicator.button.into_any_element();
 771                let available_space = size(
 772                    AvailableSpace::MinContent,
 773                    AvailableSpace::Definite(line_height),
 774                );
 775                let indicator_size = button.measure(available_space, cx);
 776
 777                let mut x = Pixels::ZERO;
 778                let mut y = indicator.row as f32 * line_height - scroll_top;
 779                // Center indicator.
 780                x += (layout.gutter_dimensions.margin + layout.gutter_dimensions.left_padding
 781                    - indicator_size.width)
 782                    / 2.;
 783                y += (line_height - indicator_size.height) / 2.;
 784
 785                button.draw(bounds.origin + point(x, y), available_space, cx);
 786            }
 787        });
 788    }
 789
 790    fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
 791        let line_height = layout.position_map.line_height;
 792
 793        let scroll_position = layout.position_map.snapshot.scroll_position();
 794        let scroll_top = scroll_position.y * line_height;
 795
 796        for hunk in &layout.display_hunks {
 797            let (display_row_range, status) = match hunk {
 798                //TODO: This rendering is entirely a horrible hack
 799                &DisplayDiffHunk::Folded { display_row: row } => {
 800                    let start_y = row as f32 * line_height - scroll_top;
 801                    let end_y = start_y + line_height;
 802
 803                    let width = 0.275 * line_height;
 804                    let highlight_origin = bounds.origin + point(-width, start_y);
 805                    let highlight_size = size(width * 2., end_y - start_y);
 806                    let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
 807                    cx.paint_quad(quad(
 808                        highlight_bounds,
 809                        Corners::all(1. * line_height),
 810                        cx.theme().status().modified,
 811                        Edges::default(),
 812                        transparent_black(),
 813                    ));
 814
 815                    continue;
 816                }
 817
 818                DisplayDiffHunk::Unfolded {
 819                    display_row_range,
 820                    status,
 821                } => (display_row_range, status),
 822            };
 823
 824            let color = match status {
 825                DiffHunkStatus::Added => cx.theme().status().created,
 826                DiffHunkStatus::Modified => cx.theme().status().modified,
 827
 828                //TODO: This rendering is entirely a horrible hack
 829                DiffHunkStatus::Removed => {
 830                    let row = display_row_range.start;
 831
 832                    let offset = line_height / 2.;
 833                    let start_y = row as f32 * line_height - offset - scroll_top;
 834                    let end_y = start_y + line_height;
 835
 836                    let width = 0.275 * line_height;
 837                    let highlight_origin = bounds.origin + point(-width, start_y);
 838                    let highlight_size = size(width * 2., end_y - start_y);
 839                    let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
 840                    cx.paint_quad(quad(
 841                        highlight_bounds,
 842                        Corners::all(1. * line_height),
 843                        cx.theme().status().deleted,
 844                        Edges::default(),
 845                        transparent_black(),
 846                    ));
 847
 848                    continue;
 849                }
 850            };
 851
 852            let start_row = display_row_range.start;
 853            let end_row = display_row_range.end;
 854            // If we're in a multibuffer, row range span might include an
 855            // excerpt header, so if we were to draw the marker straight away,
 856            // the hunk might include the rows of that header.
 857            // Making the range inclusive doesn't quite cut it, as we rely on the exclusivity for the soft wrap.
 858            // Instead, we simply check whether the range we're dealing with includes
 859            // any excerpt headers and if so, we stop painting the diff hunk on the first row of that header.
 860            let end_row_in_current_excerpt = layout
 861                .position_map
 862                .snapshot
 863                .blocks_in_range(start_row..end_row)
 864                .find_map(|(start_row, block)| {
 865                    if matches!(block, TransformBlock::ExcerptHeader { .. }) {
 866                        Some(start_row)
 867                    } else {
 868                        None
 869                    }
 870                })
 871                .unwrap_or(end_row);
 872
 873            let start_y = start_row as f32 * line_height - scroll_top;
 874            let end_y = end_row_in_current_excerpt as f32 * line_height - scroll_top;
 875
 876            let width = 0.275 * line_height;
 877            let highlight_origin = bounds.origin + point(-width, start_y);
 878            let highlight_size = size(width * 2., end_y - start_y);
 879            let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
 880            cx.paint_quad(quad(
 881                highlight_bounds,
 882                Corners::all(0.05 * line_height),
 883                color,
 884                Edges::default(),
 885                transparent_black(),
 886            ));
 887        }
 888    }
 889
 890    fn paint_text(
 891        &mut self,
 892        text_bounds: Bounds<Pixels>,
 893        layout: &mut LayoutState,
 894        cx: &mut ElementContext,
 895    ) {
 896        let start_row = layout.visible_display_row_range.start;
 897        // Offset the content_bounds from the text_bounds by the gutter margin (which is roughly half a character wide) to make hit testing work more like how we want.
 898        let content_origin =
 899            text_bounds.origin + point(layout.gutter_dimensions.margin, Pixels::ZERO);
 900        let line_end_overshoot = 0.15 * layout.position_map.line_height;
 901        let whitespace_setting = self
 902            .editor
 903            .read(cx)
 904            .buffer
 905            .read(cx)
 906            .settings_at(0, cx)
 907            .show_whitespaces;
 908
 909        cx.with_content_mask(
 910            Some(ContentMask {
 911                bounds: text_bounds,
 912            }),
 913            |cx| {
 914                let interactive_text_bounds = InteractiveBounds {
 915                    bounds: text_bounds,
 916                    stacking_order: cx.stacking_order().clone(),
 917                };
 918                if interactive_text_bounds.visibly_contains(&cx.mouse_position(), cx) {
 919                    if self
 920                        .editor
 921                        .read(cx)
 922                        .hovered_link_state
 923                        .as_ref()
 924                        .is_some_and(|hovered_link_state| !hovered_link_state.links.is_empty())
 925                    {
 926                        cx.set_cursor_style(
 927                            CursorStyle::PointingHand,
 928                            interactive_text_bounds.stacking_order.clone(),
 929                        );
 930                    } else {
 931                        cx.set_cursor_style(
 932                            CursorStyle::IBeam,
 933                            interactive_text_bounds.stacking_order.clone(),
 934                        );
 935                    }
 936                }
 937
 938                let fold_corner_radius = 0.15 * layout.position_map.line_height;
 939                cx.with_element_id(Some("folds"), |cx| {
 940                    let snapshot = &layout.position_map.snapshot;
 941
 942                    for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
 943                        let fold_range = fold.range.clone();
 944                        let display_range = fold.range.start.to_display_point(&snapshot)
 945                            ..fold.range.end.to_display_point(&snapshot);
 946                        debug_assert_eq!(display_range.start.row(), display_range.end.row());
 947                        let row = display_range.start.row();
 948                        debug_assert!(row < layout.visible_display_row_range.end);
 949                        let Some(line_layout) = &layout
 950                            .position_map
 951                            .line_layouts
 952                            .get((row - layout.visible_display_row_range.start) as usize)
 953                            .map(|l| &l.line)
 954                        else {
 955                            continue;
 956                        };
 957
 958                        let start_x = content_origin.x
 959                            + line_layout.x_for_index(display_range.start.column() as usize)
 960                            - layout.position_map.scroll_position.x;
 961                        let start_y = content_origin.y
 962                            + row as f32 * layout.position_map.line_height
 963                            - layout.position_map.scroll_position.y;
 964                        let end_x = content_origin.x
 965                            + line_layout.x_for_index(display_range.end.column() as usize)
 966                            - layout.position_map.scroll_position.x;
 967
 968                        let fold_bounds = Bounds {
 969                            origin: point(start_x, start_y),
 970                            size: size(end_x - start_x, layout.position_map.line_height),
 971                        };
 972
 973                        let fold_background = cx.with_z_index(1, |cx| {
 974                            div()
 975                                .id(fold.id)
 976                                .size_full()
 977                                .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
 978                                .on_click(cx.listener_for(
 979                                    &self.editor,
 980                                    move |editor: &mut Editor, _, cx| {
 981                                        editor.unfold_ranges(
 982                                            [fold_range.start..fold_range.end],
 983                                            true,
 984                                            false,
 985                                            cx,
 986                                        );
 987                                        cx.stop_propagation();
 988                                    },
 989                                ))
 990                                .draw_and_update_state(
 991                                    fold_bounds.origin,
 992                                    fold_bounds.size,
 993                                    cx,
 994                                    |fold_element_state, cx| {
 995                                        if fold_element_state.is_active() {
 996                                            cx.theme().colors().ghost_element_active
 997                                        } else if fold_bounds.contains(&cx.mouse_position()) {
 998                                            cx.theme().colors().ghost_element_hover
 999                                        } else {
1000                                            cx.theme().colors().ghost_element_background
1001                                        }
1002                                    },
1003                                )
1004                        });
1005
1006                        self.paint_highlighted_range(
1007                            display_range.clone(),
1008                            fold_background,
1009                            fold_corner_radius,
1010                            fold_corner_radius * 2.,
1011                            layout,
1012                            content_origin,
1013                            text_bounds,
1014                            cx,
1015                        );
1016                    }
1017                });
1018
1019                for (range, color) in &layout.highlighted_ranges {
1020                    self.paint_highlighted_range(
1021                        range.clone(),
1022                        *color,
1023                        Pixels::ZERO,
1024                        line_end_overshoot,
1025                        layout,
1026                        content_origin,
1027                        text_bounds,
1028                        cx,
1029                    );
1030                }
1031
1032                let mut cursors = SmallVec::<[Cursor; 32]>::new();
1033                let corner_radius = 0.15 * layout.position_map.line_height;
1034                let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
1035
1036                for (participant_ix, (player_color, selections)) in
1037                    layout.selections.iter().enumerate()
1038                {
1039                    for selection in selections.into_iter() {
1040                        self.paint_highlighted_range(
1041                            selection.range.clone(),
1042                            player_color.selection,
1043                            corner_radius,
1044                            corner_radius * 2.,
1045                            layout,
1046                            content_origin,
1047                            text_bounds,
1048                            cx,
1049                        );
1050
1051                        if selection.is_local && !selection.range.is_empty() {
1052                            invisible_display_ranges.push(selection.range.clone());
1053                        }
1054
1055                        if !selection.is_local || self.editor.read(cx).show_local_cursors(cx) {
1056                            let cursor_position = selection.head;
1057                            if layout
1058                                .visible_display_row_range
1059                                .contains(&cursor_position.row())
1060                            {
1061                                let cursor_row_layout = &layout.position_map.line_layouts
1062                                    [(cursor_position.row() - start_row) as usize]
1063                                    .line;
1064                                let cursor_column = cursor_position.column() as usize;
1065
1066                                let cursor_character_x =
1067                                    cursor_row_layout.x_for_index(cursor_column);
1068                                let mut block_width = cursor_row_layout
1069                                    .x_for_index(cursor_column + 1)
1070                                    - cursor_character_x;
1071                                if block_width == Pixels::ZERO {
1072                                    block_width = layout.position_map.em_width;
1073                                }
1074                                let block_text = if let CursorShape::Block = selection.cursor_shape
1075                                {
1076                                    layout
1077                                        .position_map
1078                                        .snapshot
1079                                        .chars_at(cursor_position)
1080                                        .next()
1081                                        .and_then(|(character, _)| {
1082                                            let text = if character == '\n' {
1083                                                SharedString::from(" ")
1084                                            } else {
1085                                                SharedString::from(character.to_string())
1086                                            };
1087                                            let len = text.len();
1088                                            cx.text_system()
1089                                                .shape_line(
1090                                                    text,
1091                                                    cursor_row_layout.font_size,
1092                                                    &[TextRun {
1093                                                        len,
1094                                                        font: self.style.text.font(),
1095                                                        color: self.style.background,
1096                                                        background_color: None,
1097                                                        strikethrough: None,
1098                                                        underline: None,
1099                                                    }],
1100                                                )
1101                                                .log_err()
1102                                        })
1103                                } else {
1104                                    None
1105                                };
1106
1107                                let x = cursor_character_x - layout.position_map.scroll_position.x;
1108                                let y = cursor_position.row() as f32
1109                                    * layout.position_map.line_height
1110                                    - layout.position_map.scroll_position.y;
1111                                if selection.is_newest {
1112                                    self.editor.update(cx, |editor, _| {
1113                                        editor.pixel_position_of_newest_cursor = Some(point(
1114                                            text_bounds.origin.x + x + block_width / 2.,
1115                                            text_bounds.origin.y
1116                                                + y
1117                                                + layout.position_map.line_height / 2.,
1118                                        ))
1119                                    });
1120                                }
1121
1122                                cursors.push(Cursor {
1123                                    color: player_color.cursor,
1124                                    block_width,
1125                                    origin: point(x, y),
1126                                    line_height: layout.position_map.line_height,
1127                                    shape: selection.cursor_shape,
1128                                    block_text,
1129                                    cursor_name: selection.user_name.clone().map(|name| {
1130                                        CursorName {
1131                                            string: name,
1132                                            color: self.style.background,
1133                                            is_top_row: cursor_position.row() == 0,
1134                                            z_index: (participant_ix % 256).try_into().unwrap(),
1135                                        }
1136                                    }),
1137                                });
1138                            }
1139                        }
1140                    }
1141                }
1142
1143                for (ix, line_with_invisibles) in
1144                    layout.position_map.line_layouts.iter().enumerate()
1145                {
1146                    let row = start_row + ix as u32;
1147                    line_with_invisibles.draw(
1148                        layout,
1149                        row,
1150                        content_origin,
1151                        whitespace_setting,
1152                        &invisible_display_ranges,
1153                        cx,
1154                    )
1155                }
1156
1157                cx.with_z_index(0, |cx| self.paint_redactions(text_bounds, &layout, cx));
1158
1159                cx.with_z_index(1, |cx| {
1160                    for cursor in cursors {
1161                        cursor.paint(content_origin, cx);
1162                    }
1163                });
1164            },
1165        )
1166    }
1167
1168    fn paint_redactions(
1169        &mut self,
1170        text_bounds: Bounds<Pixels>,
1171        layout: &LayoutState,
1172        cx: &mut ElementContext,
1173    ) {
1174        let content_origin =
1175            text_bounds.origin + point(layout.gutter_dimensions.margin, Pixels::ZERO);
1176        let line_end_overshoot = layout.line_end_overshoot();
1177
1178        // A softer than perfect black
1179        let redaction_color = gpui::rgb(0x0e1111);
1180
1181        for range in layout.redacted_ranges.iter() {
1182            self.paint_highlighted_range(
1183                range.clone(),
1184                redaction_color.into(),
1185                Pixels::ZERO,
1186                line_end_overshoot,
1187                layout,
1188                content_origin,
1189                text_bounds,
1190                cx,
1191            );
1192        }
1193    }
1194
1195    fn paint_overlays(
1196        &mut self,
1197        text_bounds: Bounds<Pixels>,
1198        layout: &mut LayoutState,
1199        cx: &mut ElementContext,
1200    ) {
1201        let content_origin =
1202            text_bounds.origin + point(layout.gutter_dimensions.margin, Pixels::ZERO);
1203        let start_row = layout.visible_display_row_range.start;
1204        if let Some((position, mut context_menu)) = layout.context_menu.take() {
1205            let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
1206            let context_menu_size = context_menu.measure(available_space, cx);
1207
1208            let cursor_row_layout =
1209                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
1210            let x = cursor_row_layout.x_for_index(position.column() as usize)
1211                - layout.position_map.scroll_position.x;
1212            let y = (position.row() + 1) as f32 * layout.position_map.line_height
1213                - layout.position_map.scroll_position.y;
1214            let mut list_origin = content_origin + point(x, y);
1215            let list_width = context_menu_size.width;
1216            let list_height = context_menu_size.height;
1217
1218            // Snap the right edge of the list to the right edge of the window if
1219            // its horizontal bounds overflow.
1220            if list_origin.x + list_width > cx.viewport_size().width {
1221                list_origin.x = (cx.viewport_size().width - list_width).max(Pixels::ZERO);
1222            }
1223
1224            if list_origin.y + list_height > text_bounds.lower_right().y {
1225                list_origin.y -= layout.position_map.line_height + list_height;
1226            }
1227
1228            cx.break_content_mask(|cx| context_menu.draw(list_origin, available_space, cx));
1229        }
1230
1231        if let Some((position, mut hover_popovers)) = layout.hover_popovers.take() {
1232            let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
1233
1234            // This is safe because we check on layout whether the required row is available
1235            let hovered_row_layout =
1236                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
1237
1238            // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
1239            // height. This is the size we will use to decide whether to render popovers above or below
1240            // the hovered line.
1241            let first_size = hover_popovers[0].measure(available_space, cx);
1242            let height_to_reserve =
1243                first_size.height + 1.5 * MIN_POPOVER_LINE_HEIGHT * layout.position_map.line_height;
1244
1245            // Compute Hovered Point
1246            let x = hovered_row_layout.x_for_index(position.column() as usize)
1247                - layout.position_map.scroll_position.x;
1248            let y = position.row() as f32 * layout.position_map.line_height
1249                - layout.position_map.scroll_position.y;
1250            let hovered_point = content_origin + point(x, y);
1251
1252            if hovered_point.y - height_to_reserve > Pixels::ZERO {
1253                // There is enough space above. Render popovers above the hovered point
1254                let mut current_y = hovered_point.y;
1255                for mut hover_popover in hover_popovers {
1256                    let size = hover_popover.measure(available_space, cx);
1257                    let mut popover_origin = point(hovered_point.x, current_y - size.height);
1258
1259                    let x_out_of_bounds =
1260                        text_bounds.upper_right().x - (popover_origin.x + size.width);
1261                    if x_out_of_bounds < Pixels::ZERO {
1262                        popover_origin.x = popover_origin.x + x_out_of_bounds;
1263                    }
1264
1265                    if cx.was_top_layer(&popover_origin, cx.stacking_order()) {
1266                        cx.break_content_mask(|cx| {
1267                            hover_popover.draw(popover_origin, available_space, cx)
1268                        });
1269                    }
1270
1271                    current_y = popover_origin.y - HOVER_POPOVER_GAP;
1272                }
1273            } else {
1274                // There is not enough space above. Render popovers below the hovered point
1275                let mut current_y = hovered_point.y + layout.position_map.line_height;
1276                for mut hover_popover in hover_popovers {
1277                    let size = hover_popover.measure(available_space, cx);
1278                    let mut popover_origin = point(hovered_point.x, current_y);
1279
1280                    let x_out_of_bounds =
1281                        text_bounds.upper_right().x - (popover_origin.x + size.width);
1282                    if x_out_of_bounds < Pixels::ZERO {
1283                        popover_origin.x = popover_origin.x + x_out_of_bounds;
1284                    }
1285
1286                    hover_popover.draw(popover_origin, available_space, cx);
1287
1288                    current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP;
1289                }
1290            }
1291        }
1292
1293        if let Some(mouse_context_menu) = self.editor.read(cx).mouse_context_menu.as_ref() {
1294            let element = overlay()
1295                .position(mouse_context_menu.position)
1296                .child(mouse_context_menu.context_menu.clone())
1297                .anchor(AnchorCorner::TopLeft)
1298                .snap_to_window();
1299            element.into_any().draw(
1300                gpui::Point::default(),
1301                size(AvailableSpace::MinContent, AvailableSpace::MinContent),
1302                cx,
1303            );
1304        }
1305    }
1306
1307    fn scrollbar_left(&self, bounds: &Bounds<Pixels>) -> Pixels {
1308        bounds.upper_right().x - self.style.scrollbar_width
1309    }
1310
1311    fn paint_scrollbar(
1312        &mut self,
1313        bounds: Bounds<Pixels>,
1314        layout: &mut LayoutState,
1315        cx: &mut ElementContext,
1316    ) {
1317        if layout.mode != EditorMode::Full {
1318            return;
1319        }
1320
1321        // If a drag took place after we started dragging the scrollbar,
1322        // cancel the scrollbar drag.
1323        if cx.has_active_drag() {
1324            self.editor.update(cx, |editor, cx| {
1325                editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
1326            });
1327        }
1328
1329        let top = bounds.origin.y;
1330        let bottom = bounds.lower_left().y;
1331        let right = bounds.lower_right().x;
1332        let left = self.scrollbar_left(&bounds);
1333        let row_range = layout.scrollbar_row_range.clone();
1334        let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
1335
1336        let mut height = bounds.size.height;
1337        let mut first_row_y_offset = px(0.0);
1338
1339        // Impose a minimum height on the scrollbar thumb
1340        let row_height = height / max_row;
1341        let min_thumb_height = layout.position_map.line_height;
1342        let thumb_height = (row_range.end - row_range.start) * row_height;
1343        if thumb_height < min_thumb_height {
1344            first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
1345            height -= min_thumb_height - thumb_height;
1346        }
1347
1348        let y_for_row = |row: f32| -> Pixels { top + first_row_y_offset + row * row_height };
1349
1350        let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
1351        let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
1352        let track_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
1353        let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
1354
1355        if layout.show_scrollbars {
1356            cx.paint_quad(quad(
1357                track_bounds,
1358                Corners::default(),
1359                cx.theme().colors().scrollbar_track_background,
1360                Edges {
1361                    top: Pixels::ZERO,
1362                    right: Pixels::ZERO,
1363                    bottom: Pixels::ZERO,
1364                    left: px(1.),
1365                },
1366                cx.theme().colors().scrollbar_track_border,
1367            ));
1368            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
1369            if layout.is_singleton && scrollbar_settings.selections {
1370                let start_anchor = Anchor::min();
1371                let end_anchor = Anchor::max();
1372                let background_ranges = self
1373                    .editor
1374                    .read(cx)
1375                    .background_highlight_row_ranges::<BufferSearchHighlights>(
1376                        start_anchor..end_anchor,
1377                        &layout.position_map.snapshot,
1378                        50000,
1379                    );
1380                for range in background_ranges {
1381                    let start_y = y_for_row(range.start().row() as f32);
1382                    let mut end_y = y_for_row(range.end().row() as f32);
1383                    if end_y - start_y < px(1.) {
1384                        end_y = start_y + px(1.);
1385                    }
1386                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
1387                    cx.paint_quad(quad(
1388                        bounds,
1389                        Corners::default(),
1390                        cx.theme().status().info,
1391                        Edges {
1392                            top: Pixels::ZERO,
1393                            right: px(1.),
1394                            bottom: Pixels::ZERO,
1395                            left: px(1.),
1396                        },
1397                        cx.theme().colors().scrollbar_thumb_border,
1398                    ));
1399                }
1400            }
1401
1402            if layout.is_singleton && scrollbar_settings.symbols_selections {
1403                let selection_ranges = self.editor.read(cx).background_highlights_in_range(
1404                    Anchor::min()..Anchor::max(),
1405                    &layout.position_map.snapshot,
1406                    cx.theme().colors(),
1407                );
1408                for hunk in selection_ranges {
1409                    let start_display = Point::new(hunk.0.start.row(), 0)
1410                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1411                    let end_display = Point::new(hunk.0.end.row(), 0)
1412                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1413                    let start_y = y_for_row(start_display.row() as f32);
1414                    let mut end_y = if hunk.0.start == hunk.0.end {
1415                        y_for_row((end_display.row() + 1) as f32)
1416                    } else {
1417                        y_for_row((end_display.row()) as f32)
1418                    };
1419
1420                    if end_y - start_y < px(1.) {
1421                        end_y = start_y + px(1.);
1422                    }
1423                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
1424
1425                    cx.paint_quad(quad(
1426                        bounds,
1427                        Corners::default(),
1428                        cx.theme().status().info,
1429                        Edges {
1430                            top: Pixels::ZERO,
1431                            right: px(1.),
1432                            bottom: Pixels::ZERO,
1433                            left: px(1.),
1434                        },
1435                        cx.theme().colors().scrollbar_thumb_border,
1436                    ));
1437                }
1438            }
1439
1440            if layout.is_singleton && scrollbar_settings.git_diff {
1441                for hunk in layout
1442                    .position_map
1443                    .snapshot
1444                    .buffer_snapshot
1445                    .git_diff_hunks_in_range(0..(max_row.floor() as u32))
1446                {
1447                    let start_display = Point::new(hunk.buffer_range.start, 0)
1448                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1449                    let end_display = Point::new(hunk.buffer_range.end, 0)
1450                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1451                    let start_y = y_for_row(start_display.row() as f32);
1452                    let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
1453                        y_for_row((end_display.row() + 1) as f32)
1454                    } else {
1455                        y_for_row((end_display.row()) as f32)
1456                    };
1457
1458                    if end_y - start_y < px(1.) {
1459                        end_y = start_y + px(1.);
1460                    }
1461                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
1462
1463                    let color = match hunk.status() {
1464                        DiffHunkStatus::Added => cx.theme().status().created,
1465                        DiffHunkStatus::Modified => cx.theme().status().modified,
1466                        DiffHunkStatus::Removed => cx.theme().status().deleted,
1467                    };
1468                    cx.paint_quad(quad(
1469                        bounds,
1470                        Corners::default(),
1471                        color,
1472                        Edges {
1473                            top: Pixels::ZERO,
1474                            right: px(1.),
1475                            bottom: Pixels::ZERO,
1476                            left: px(1.),
1477                        },
1478                        cx.theme().colors().scrollbar_thumb_border,
1479                    ));
1480                }
1481            }
1482
1483            if layout.is_singleton && scrollbar_settings.diagnostics {
1484                let max_point = layout
1485                    .position_map
1486                    .snapshot
1487                    .display_snapshot
1488                    .buffer_snapshot
1489                    .max_point();
1490
1491                let diagnostics = layout
1492                    .position_map
1493                    .snapshot
1494                    .buffer_snapshot
1495                    .diagnostics_in_range::<_, Point>(Point::zero()..max_point, false)
1496                    // We want to sort by severity, in order to paint the most severe diagnostics last.
1497                    .sorted_by_key(|diagnostic| std::cmp::Reverse(diagnostic.diagnostic.severity));
1498
1499                for diagnostic in diagnostics {
1500                    let start_display = diagnostic
1501                        .range
1502                        .start
1503                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1504                    let end_display = diagnostic
1505                        .range
1506                        .end
1507                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1508                    let start_y = y_for_row(start_display.row() as f32);
1509                    let mut end_y = if diagnostic.range.start == diagnostic.range.end {
1510                        y_for_row((end_display.row() + 1) as f32)
1511                    } else {
1512                        y_for_row((end_display.row()) as f32)
1513                    };
1514
1515                    if end_y - start_y < px(1.) {
1516                        end_y = start_y + px(1.);
1517                    }
1518                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
1519
1520                    let color = match diagnostic.diagnostic.severity {
1521                        DiagnosticSeverity::ERROR => cx.theme().status().error,
1522                        DiagnosticSeverity::WARNING => cx.theme().status().warning,
1523                        DiagnosticSeverity::INFORMATION => cx.theme().status().info,
1524                        _ => cx.theme().status().hint,
1525                    };
1526                    cx.paint_quad(quad(
1527                        bounds,
1528                        Corners::default(),
1529                        color,
1530                        Edges {
1531                            top: Pixels::ZERO,
1532                            right: px(1.),
1533                            bottom: Pixels::ZERO,
1534                            left: px(1.),
1535                        },
1536                        cx.theme().colors().scrollbar_thumb_border,
1537                    ));
1538                }
1539            }
1540
1541            cx.paint_quad(quad(
1542                thumb_bounds,
1543                Corners::default(),
1544                cx.theme().colors().scrollbar_thumb_background,
1545                Edges {
1546                    top: Pixels::ZERO,
1547                    right: px(1.),
1548                    bottom: Pixels::ZERO,
1549                    left: px(1.),
1550                },
1551                cx.theme().colors().scrollbar_thumb_border,
1552            ));
1553        }
1554
1555        let interactive_track_bounds = InteractiveBounds {
1556            bounds: track_bounds,
1557            stacking_order: cx.stacking_order().clone(),
1558        };
1559        let mut mouse_position = cx.mouse_position();
1560        if interactive_track_bounds.visibly_contains(&mouse_position, cx) {
1561            cx.set_cursor_style(
1562                CursorStyle::Arrow,
1563                interactive_track_bounds.stacking_order.clone(),
1564            );
1565        }
1566
1567        cx.on_mouse_event({
1568            let editor = self.editor.clone();
1569            move |event: &MouseMoveEvent, phase, cx| {
1570                if phase == DispatchPhase::Capture {
1571                    return;
1572                }
1573
1574                editor.update(cx, |editor, cx| {
1575                    if event.pressed_button == Some(MouseButton::Left)
1576                        && editor.scroll_manager.is_dragging_scrollbar()
1577                    {
1578                        let y = mouse_position.y;
1579                        let new_y = event.position.y;
1580                        if (track_bounds.top()..track_bounds.bottom()).contains(&y) {
1581                            let mut position = editor.scroll_position(cx);
1582                            position.y += (new_y - y) * (max_row as f32) / height;
1583                            if position.y < 0.0 {
1584                                position.y = 0.0;
1585                            }
1586                            editor.set_scroll_position(position, cx);
1587                        }
1588
1589                        mouse_position = event.position;
1590                        cx.stop_propagation();
1591                    } else {
1592                        editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
1593                        if interactive_track_bounds.visibly_contains(&event.position, cx) {
1594                            editor.scroll_manager.show_scrollbar(cx);
1595                        }
1596                    }
1597                })
1598            }
1599        });
1600
1601        if self.editor.read(cx).scroll_manager.is_dragging_scrollbar() {
1602            cx.on_mouse_event({
1603                let editor = self.editor.clone();
1604                move |_: &MouseUpEvent, phase, cx| {
1605                    if phase == DispatchPhase::Capture {
1606                        return;
1607                    }
1608
1609                    editor.update(cx, |editor, cx| {
1610                        editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
1611                        cx.stop_propagation();
1612                    });
1613                }
1614            });
1615        } else {
1616            cx.on_mouse_event({
1617                let editor = self.editor.clone();
1618                move |event: &MouseDownEvent, phase, cx| {
1619                    if phase == DispatchPhase::Capture {
1620                        return;
1621                    }
1622
1623                    editor.update(cx, |editor, cx| {
1624                        if track_bounds.contains(&event.position) {
1625                            editor.scroll_manager.set_is_dragging_scrollbar(true, cx);
1626
1627                            let y = event.position.y;
1628                            if y < thumb_top || thumb_bottom < y {
1629                                let center_row =
1630                                    ((y - top) * max_row as f32 / height).round() as u32;
1631                                let top_row = center_row
1632                                    .saturating_sub((row_range.end - row_range.start) as u32 / 2);
1633                                let mut position = editor.scroll_position(cx);
1634                                position.y = top_row as f32;
1635                                editor.set_scroll_position(position, cx);
1636                            } else {
1637                                editor.scroll_manager.show_scrollbar(cx);
1638                            }
1639
1640                            cx.stop_propagation();
1641                        }
1642                    });
1643                }
1644            });
1645        }
1646    }
1647
1648    #[allow(clippy::too_many_arguments)]
1649    fn paint_highlighted_range(
1650        &self,
1651        range: Range<DisplayPoint>,
1652        color: Hsla,
1653        corner_radius: Pixels,
1654        line_end_overshoot: Pixels,
1655        layout: &LayoutState,
1656        content_origin: gpui::Point<Pixels>,
1657        bounds: Bounds<Pixels>,
1658        cx: &mut ElementContext,
1659    ) {
1660        let start_row = layout.visible_display_row_range.start;
1661        let end_row = layout.visible_display_row_range.end;
1662        if range.start != range.end {
1663            let row_range = if range.end.column() == 0 {
1664                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
1665            } else {
1666                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
1667            };
1668
1669            let highlighted_range = HighlightedRange {
1670                color,
1671                line_height: layout.position_map.line_height,
1672                corner_radius,
1673                start_y: content_origin.y
1674                    + row_range.start as f32 * layout.position_map.line_height
1675                    - layout.position_map.scroll_position.y,
1676                lines: row_range
1677                    .into_iter()
1678                    .map(|row| {
1679                        let line_layout =
1680                            &layout.position_map.line_layouts[(row - start_row) as usize].line;
1681                        HighlightedRangeLine {
1682                            start_x: if row == range.start.row() {
1683                                content_origin.x
1684                                    + line_layout.x_for_index(range.start.column() as usize)
1685                                    - layout.position_map.scroll_position.x
1686                            } else {
1687                                content_origin.x - layout.position_map.scroll_position.x
1688                            },
1689                            end_x: if row == range.end.row() {
1690                                content_origin.x
1691                                    + line_layout.x_for_index(range.end.column() as usize)
1692                                    - layout.position_map.scroll_position.x
1693                            } else {
1694                                content_origin.x + line_layout.width + line_end_overshoot
1695                                    - layout.position_map.scroll_position.x
1696                            },
1697                        }
1698                    })
1699                    .collect(),
1700            };
1701
1702            highlighted_range.paint(bounds, cx);
1703        }
1704    }
1705
1706    fn paint_blocks(
1707        &mut self,
1708        bounds: Bounds<Pixels>,
1709        layout: &mut LayoutState,
1710        cx: &mut ElementContext,
1711    ) {
1712        let scroll_position = layout.position_map.snapshot.scroll_position();
1713        let scroll_left = scroll_position.x * layout.position_map.em_width;
1714        let scroll_top = scroll_position.y * layout.position_map.line_height;
1715
1716        for mut block in layout.blocks.drain(..) {
1717            let mut origin = bounds.origin
1718                + point(
1719                    Pixels::ZERO,
1720                    block.row as f32 * layout.position_map.line_height - scroll_top,
1721                );
1722            if !matches!(block.style, BlockStyle::Sticky) {
1723                origin += point(-scroll_left, Pixels::ZERO);
1724            }
1725            block.element.draw(origin, block.available_space, cx);
1726        }
1727    }
1728
1729    fn column_pixels(&self, column: usize, cx: &WindowContext) -> Pixels {
1730        let style = &self.style;
1731        let font_size = style.text.font_size.to_pixels(cx.rem_size());
1732        let layout = cx
1733            .text_system()
1734            .shape_line(
1735                SharedString::from(" ".repeat(column)),
1736                font_size,
1737                &[TextRun {
1738                    len: column,
1739                    font: style.text.font(),
1740                    color: Hsla::default(),
1741                    background_color: None,
1742                    underline: None,
1743                    strikethrough: None,
1744                }],
1745            )
1746            .unwrap();
1747
1748        layout.width
1749    }
1750
1751    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &WindowContext) -> Pixels {
1752        let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
1753        self.column_pixels(digit_count, cx)
1754    }
1755
1756    //Folds contained in a hunk are ignored apart from shrinking visual size
1757    //If a fold contains any hunks then that fold line is marked as modified
1758    fn layout_git_gutters(
1759        &self,
1760        display_rows: Range<u32>,
1761        snapshot: &EditorSnapshot,
1762    ) -> Vec<DisplayDiffHunk> {
1763        let buffer_snapshot = &snapshot.buffer_snapshot;
1764
1765        let buffer_start_row = DisplayPoint::new(display_rows.start, 0)
1766            .to_point(snapshot)
1767            .row;
1768        let buffer_end_row = DisplayPoint::new(display_rows.end, 0)
1769            .to_point(snapshot)
1770            .row;
1771
1772        buffer_snapshot
1773            .git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
1774            .map(|hunk| diff_hunk_to_display(hunk, snapshot))
1775            .dedup()
1776            .collect()
1777    }
1778
1779    fn calculate_relative_line_numbers(
1780        &self,
1781        snapshot: &EditorSnapshot,
1782        rows: &Range<u32>,
1783        relative_to: Option<u32>,
1784    ) -> HashMap<u32, u32> {
1785        let mut relative_rows: HashMap<u32, u32> = Default::default();
1786        let Some(relative_to) = relative_to else {
1787            return relative_rows;
1788        };
1789
1790        let start = rows.start.min(relative_to);
1791        let end = rows.end.max(relative_to);
1792
1793        let buffer_rows = snapshot
1794            .buffer_rows(start)
1795            .take(1 + (end - start) as usize)
1796            .collect::<Vec<_>>();
1797
1798        let head_idx = relative_to - start;
1799        let mut delta = 1;
1800        let mut i = head_idx + 1;
1801        while i < buffer_rows.len() as u32 {
1802            if buffer_rows[i as usize].is_some() {
1803                if rows.contains(&(i + start)) {
1804                    relative_rows.insert(i + start, delta);
1805                }
1806                delta += 1;
1807            }
1808            i += 1;
1809        }
1810        delta = 1;
1811        i = head_idx.min(buffer_rows.len() as u32 - 1);
1812        while i > 0 && buffer_rows[i as usize].is_none() {
1813            i -= 1;
1814        }
1815
1816        while i > 0 {
1817            i -= 1;
1818            if buffer_rows[i as usize].is_some() {
1819                if rows.contains(&(i + start)) {
1820                    relative_rows.insert(i + start, delta);
1821                }
1822                delta += 1;
1823            }
1824        }
1825
1826        relative_rows
1827    }
1828
1829    fn shape_line_numbers(
1830        &self,
1831        rows: Range<u32>,
1832        active_rows: &BTreeMap<u32, bool>,
1833        newest_selection_head: DisplayPoint,
1834        is_singleton: bool,
1835        snapshot: &EditorSnapshot,
1836        cx: &ViewContext<Editor>,
1837    ) -> (
1838        Vec<Option<ShapedLine>>,
1839        Vec<Option<(FoldStatus, BufferRow, bool)>>,
1840    ) {
1841        let font_size = self.style.text.font_size.to_pixels(cx.rem_size());
1842        let include_line_numbers =
1843            EditorSettings::get_global(cx).gutter.line_numbers && snapshot.mode == EditorMode::Full;
1844        let include_fold_statuses =
1845            EditorSettings::get_global(cx).gutter.folds && snapshot.mode == EditorMode::Full;
1846        let mut shaped_line_numbers = Vec::with_capacity(rows.len());
1847        let mut fold_statuses = Vec::with_capacity(rows.len());
1848        let mut line_number = String::new();
1849        let is_relative = EditorSettings::get_global(cx).relative_line_numbers;
1850        let relative_to = if is_relative {
1851            Some(newest_selection_head.row())
1852        } else {
1853            None
1854        };
1855
1856        let relative_rows = self.calculate_relative_line_numbers(&snapshot, &rows, relative_to);
1857
1858        for (ix, row) in snapshot
1859            .buffer_rows(rows.start)
1860            .take((rows.end - rows.start) as usize)
1861            .enumerate()
1862        {
1863            let display_row = rows.start + ix as u32;
1864            let (active, color) = if active_rows.contains_key(&display_row) {
1865                (true, cx.theme().colors().editor_active_line_number)
1866            } else {
1867                (false, cx.theme().colors().editor_line_number)
1868            };
1869            if let Some(buffer_row) = row {
1870                if include_line_numbers {
1871                    line_number.clear();
1872                    let default_number = buffer_row + 1;
1873                    let number = relative_rows
1874                        .get(&(ix as u32 + rows.start))
1875                        .unwrap_or(&default_number);
1876                    write!(&mut line_number, "{}", number).unwrap();
1877                    let run = TextRun {
1878                        len: line_number.len(),
1879                        font: self.style.text.font(),
1880                        color,
1881                        background_color: None,
1882                        underline: None,
1883                        strikethrough: None,
1884                    };
1885                    let shaped_line = cx
1886                        .text_system()
1887                        .shape_line(line_number.clone().into(), font_size, &[run])
1888                        .unwrap();
1889                    shaped_line_numbers.push(Some(shaped_line));
1890                }
1891                if include_fold_statuses {
1892                    fold_statuses.push(
1893                        is_singleton
1894                            .then(|| {
1895                                snapshot
1896                                    .fold_for_line(buffer_row)
1897                                    .map(|fold_status| (fold_status, buffer_row, active))
1898                            })
1899                            .flatten(),
1900                    )
1901                }
1902            } else {
1903                fold_statuses.push(None);
1904                shaped_line_numbers.push(None);
1905            }
1906        }
1907
1908        (shaped_line_numbers, fold_statuses)
1909    }
1910
1911    fn layout_lines(
1912        &self,
1913        rows: Range<u32>,
1914        line_number_layouts: &[Option<ShapedLine>],
1915        snapshot: &EditorSnapshot,
1916        cx: &mut ViewContext<Editor>,
1917    ) -> Vec<LineWithInvisibles> {
1918        if rows.start >= rows.end {
1919            return Vec::new();
1920        }
1921
1922        // Show the placeholder when the editor is empty
1923        if snapshot.is_empty() {
1924            let font_size = self.style.text.font_size.to_pixels(cx.rem_size());
1925            let placeholder_color = cx.theme().colors().text_placeholder;
1926            let placeholder_text = snapshot.placeholder_text(cx);
1927
1928            let placeholder_lines = placeholder_text
1929                .as_ref()
1930                .map_or("", AsRef::as_ref)
1931                .split('\n')
1932                .skip(rows.start as usize)
1933                .chain(iter::repeat(""))
1934                .take(rows.len());
1935            placeholder_lines
1936                .filter_map(move |line| {
1937                    let run = TextRun {
1938                        len: line.len(),
1939                        font: self.style.text.font(),
1940                        color: placeholder_color,
1941                        background_color: None,
1942                        underline: Default::default(),
1943                        strikethrough: None,
1944                    };
1945                    cx.text_system()
1946                        .shape_line(line.to_string().into(), font_size, &[run])
1947                        .log_err()
1948                })
1949                .map(|line| LineWithInvisibles {
1950                    line,
1951                    invisibles: Vec::new(),
1952                })
1953                .collect()
1954        } else {
1955            let chunks = snapshot.highlighted_chunks(rows.clone(), true, &self.style);
1956            LineWithInvisibles::from_chunks(
1957                chunks,
1958                &self.style.text,
1959                MAX_LINE_LEN,
1960                rows.len() as usize,
1961                line_number_layouts,
1962                snapshot.mode,
1963                cx,
1964            )
1965        }
1966    }
1967
1968    fn compute_layout(&mut self, bounds: Bounds<Pixels>, cx: &mut ElementContext) -> LayoutState {
1969        self.editor.update(cx, |editor, cx| {
1970            let snapshot = editor.snapshot(cx);
1971            let style = self.style.clone();
1972
1973            let font_id = cx.text_system().resolve_font(&style.text.font());
1974            let font_size = style.text.font_size.to_pixels(cx.rem_size());
1975            let line_height = style.text.line_height_in_pixels(cx.rem_size());
1976            let em_width = cx
1977                .text_system()
1978                .typographic_bounds(font_id, font_size, 'm')
1979                .unwrap()
1980                .size
1981                .width;
1982            let em_advance = cx
1983                .text_system()
1984                .advance(font_id, font_size, 'm')
1985                .unwrap()
1986                .width;
1987
1988            let gutter_dimensions = snapshot.gutter_dimensions(
1989                font_id,
1990                font_size,
1991                em_width,
1992                self.max_line_number_width(&snapshot, cx),
1993                cx,
1994            );
1995
1996            editor.gutter_width = gutter_dimensions.width;
1997
1998            let text_width = bounds.size.width - gutter_dimensions.width;
1999            let overscroll = size(em_width, px(0.));
2000            let _snapshot = {
2001                editor.set_visible_line_count((bounds.size.height / line_height).into(), cx);
2002
2003                let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width;
2004                let wrap_width = match editor.soft_wrap_mode(cx) {
2005                    SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
2006                    SoftWrap::EditorWidth => editor_width,
2007                    SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
2008                };
2009
2010                if editor.set_wrap_width(Some(wrap_width), cx) {
2011                    editor.snapshot(cx)
2012                } else {
2013                    snapshot
2014                }
2015            };
2016
2017            let wrap_guides = editor
2018                .wrap_guides(cx)
2019                .iter()
2020                .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
2021                .collect::<SmallVec<[_; 2]>>();
2022
2023            let gutter_size = size(gutter_dimensions.width, bounds.size.height);
2024            let text_size = size(text_width, bounds.size.height);
2025
2026            let autoscroll_horizontally =
2027                editor.autoscroll_vertically(bounds.size.height, line_height, cx);
2028            let mut snapshot = editor.snapshot(cx);
2029
2030            let scroll_position = snapshot.scroll_position();
2031            // The scroll position is a fractional point, the whole number of which represents
2032            // the top of the window in terms of display rows.
2033            let start_row = scroll_position.y as u32;
2034            let height_in_lines = f32::from(bounds.size.height / line_height);
2035            let max_row = snapshot.max_point().row();
2036
2037            // Add 1 to ensure selections bleed off screen
2038            let end_row = 1 + cmp::min((scroll_position.y + height_in_lines).ceil() as u32, max_row);
2039
2040            let start_anchor = if start_row == 0 {
2041                Anchor::min()
2042            } else {
2043                snapshot
2044                    .buffer_snapshot
2045                    .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
2046            };
2047            let end_anchor = if end_row > max_row {
2048                Anchor::max()
2049            } else {
2050                snapshot
2051                    .buffer_snapshot
2052                    .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
2053            };
2054
2055            let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
2056            let mut active_rows = BTreeMap::new();
2057            let is_singleton = editor.is_singleton(cx);
2058
2059            let highlighted_rows = editor.highlighted_rows();
2060            let highlighted_ranges = editor.background_highlights_in_range(
2061                start_anchor..end_anchor,
2062                &snapshot.display_snapshot,
2063                cx.theme().colors(),
2064            );
2065
2066            let redacted_ranges = editor.redacted_ranges(start_anchor..end_anchor, &snapshot.display_snapshot, cx);
2067
2068            let mut newest_selection_head = None;
2069
2070            if editor.show_local_selections {
2071                let mut local_selections: Vec<Selection<Point>> = editor
2072                    .selections
2073                    .disjoint_in_range(start_anchor..end_anchor, cx);
2074                local_selections.extend(editor.selections.pending(cx));
2075                let mut layouts = Vec::new();
2076                let newest = editor.selections.newest(cx);
2077                for selection in local_selections.drain(..) {
2078                    let is_empty = selection.start == selection.end;
2079                    let is_newest = selection == newest;
2080
2081                    let layout = SelectionLayout::new(
2082                        selection,
2083                        editor.selections.line_mode,
2084                        editor.cursor_shape,
2085                        &snapshot.display_snapshot,
2086                        is_newest,
2087                        true,
2088                        None,
2089                    );
2090                    if is_newest {
2091                        newest_selection_head = Some(layout.head);
2092                    }
2093
2094                    for row in cmp::max(layout.active_rows.start, start_row)
2095                        ..=cmp::min(layout.active_rows.end, end_row)
2096                    {
2097                        let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
2098                        *contains_non_empty_selection |= !is_empty;
2099                    }
2100                    layouts.push(layout);
2101                }
2102
2103                let player = if editor.read_only(cx) {
2104                    cx.theme().players().read_only()
2105                } else {
2106                    style.local_player
2107                };
2108
2109                selections.push((player, layouts));
2110            }
2111
2112            if let Some(collaboration_hub) = &editor.collaboration_hub {
2113                // When following someone, render the local selections in their color.
2114                if let Some(leader_id) = editor.leader_peer_id {
2115                    if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) {
2116                        if let Some(participant_index) = collaboration_hub
2117                            .user_participant_indices(cx)
2118                            .get(&collaborator.user_id)
2119                        {
2120                            if let Some((local_selection_style, _)) = selections.first_mut() {
2121                                *local_selection_style = cx
2122                                    .theme()
2123                                    .players()
2124                                    .color_for_participant(participant_index.0);
2125                            }
2126                        }
2127                    }
2128                }
2129
2130                let mut remote_selections = HashMap::default();
2131                for selection in snapshot.remote_selections_in_range(
2132                    &(start_anchor..end_anchor),
2133                    collaboration_hub.as_ref(),
2134                    cx,
2135                ) {
2136                    let selection_style = if let Some(participant_index) = selection.participant_index {
2137                        cx.theme()
2138                            .players()
2139                            .color_for_participant(participant_index.0)
2140                    } else {
2141                        cx.theme().players().absent()
2142                    };
2143
2144                    // Don't re-render the leader's selections, since the local selections
2145                    // match theirs.
2146                    if Some(selection.peer_id) == editor.leader_peer_id {
2147                        continue;
2148                    }
2149                    let key = HoveredCursor{replica_id: selection.replica_id, selection_id: selection.selection.id};
2150
2151                    let is_shown = editor.show_cursor_names || editor.hovered_cursors.contains_key(&key);
2152
2153                    remote_selections
2154                        .entry(selection.replica_id)
2155                        .or_insert((selection_style, Vec::new()))
2156                        .1
2157                        .push(SelectionLayout::new(
2158                            selection.selection,
2159                            selection.line_mode,
2160                            selection.cursor_shape,
2161                            &snapshot.display_snapshot,
2162                            false,
2163                            false,
2164                            if is_shown {
2165                                selection.user_name
2166                            } else {
2167                                None
2168                            },
2169                        ));
2170                }
2171
2172                selections.extend(remote_selections.into_values());
2173            }
2174
2175            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
2176            let show_scrollbars = match scrollbar_settings.show {
2177                ShowScrollbar::Auto => {
2178                    // Git
2179                    (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
2180                    ||
2181                    // Selections
2182                    (is_singleton && scrollbar_settings.selections && editor.has_background_highlights::<BufferSearchHighlights>())
2183                    ||
2184                    // Symbols Selections
2185                    (is_singleton && scrollbar_settings.symbols_selections && (editor.has_background_highlights::<DocumentHighlightRead>() || editor.has_background_highlights::<DocumentHighlightWrite>()))
2186                    ||
2187                    // Diagnostics
2188                    (is_singleton && scrollbar_settings.diagnostics && snapshot.buffer_snapshot.has_diagnostics())
2189                    ||
2190                    // Scrollmanager
2191                    editor.scroll_manager.scrollbars_visible()
2192                }
2193                ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
2194                ShowScrollbar::Always => true,
2195                ShowScrollbar::Never => false,
2196            };
2197
2198            let head_for_relative = newest_selection_head.unwrap_or_else(|| {
2199                let newest = editor.selections.newest::<Point>(cx);
2200                SelectionLayout::new(
2201                    newest,
2202                    editor.selections.line_mode,
2203                    editor.cursor_shape,
2204                    &snapshot.display_snapshot,
2205                    true,
2206                    true,
2207                    None,
2208                )
2209                .head
2210            });
2211
2212            let (line_numbers, fold_statuses) = self.shape_line_numbers(
2213                start_row..end_row,
2214                &active_rows,
2215                head_for_relative,
2216                is_singleton,
2217                &snapshot,
2218                cx,
2219            );
2220
2221            let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
2222
2223            let scrollbar_row_range = scroll_position.y..(scroll_position.y + height_in_lines);
2224
2225            let mut max_visible_line_width = Pixels::ZERO;
2226            let line_layouts = self.layout_lines(start_row..end_row, &line_numbers, &snapshot, cx);
2227            for line_with_invisibles in &line_layouts {
2228                if line_with_invisibles.line.width > max_visible_line_width {
2229                    max_visible_line_width = line_with_invisibles.line.width;
2230                }
2231            }
2232
2233            let longest_line_width = layout_line(snapshot.longest_row(), &snapshot, &style, cx)
2234                .unwrap()
2235                .width;
2236            let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
2237
2238            let (scroll_width, blocks) = cx.with_element_context(|cx| {
2239             cx.with_element_id(Some("editor_blocks"), |cx| {
2240                self.layout_blocks(
2241                    start_row..end_row,
2242                    &snapshot,
2243                    bounds.size.width,
2244                    scroll_width,
2245                    text_width,
2246                    &gutter_dimensions,
2247                    em_width,
2248                    gutter_dimensions.width + gutter_dimensions.margin,
2249                    line_height,
2250                    &style,
2251                    &line_layouts,
2252                    editor,
2253                    cx,
2254                )
2255            })
2256            });
2257
2258            let scroll_max = point(
2259                f32::from((scroll_width - text_size.width) / em_width).max(0.0),
2260                max_row as f32,
2261            );
2262
2263            let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x);
2264
2265            let autoscrolled = if autoscroll_horizontally {
2266                editor.autoscroll_horizontally(
2267                    start_row,
2268                    text_size.width,
2269                    scroll_width,
2270                    em_width,
2271                    &line_layouts,
2272                    cx,
2273                )
2274            } else {
2275                false
2276            };
2277
2278            if clamped || autoscrolled {
2279                snapshot = editor.snapshot(cx);
2280            }
2281
2282            let gutter_settings = EditorSettings::get_global(cx).gutter;
2283
2284            let mut context_menu = None;
2285            let mut code_actions_indicator = None;
2286            if let Some(newest_selection_head) = newest_selection_head {
2287                if (start_row..end_row).contains(&newest_selection_head.row()) {
2288                    if editor.context_menu_visible() {
2289                        let max_height = cmp::min(
2290                            12. * line_height,
2291                            cmp::max(
2292                                3. * line_height,
2293                                (bounds.size.height - line_height) / 2.,
2294                            )
2295                        );
2296                        context_menu =
2297                            editor.render_context_menu(newest_selection_head, &self.style, max_height, cx);
2298                    }
2299
2300                    let active = matches!(
2301                        editor.context_menu.read().as_ref(),
2302                        Some(crate::ContextMenu::CodeActions(_))
2303                    );
2304
2305                    if gutter_settings.code_actions {
2306                        code_actions_indicator = editor
2307                            .render_code_actions_indicator(&style, active, cx)
2308                            .map(|element| CodeActionsIndicator {
2309                                row: newest_selection_head.row(),
2310                                button: element,
2311                            });
2312                    }
2313                }
2314            }
2315
2316            let visible_rows = start_row..start_row + line_layouts.len() as u32;
2317            let max_size = size(
2318                (120. * em_width) // Default size
2319                    .min(bounds.size.width / 2.) // Shrink to half of the editor width
2320                    .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
2321                (16. * line_height) // Default size
2322                    .min(bounds.size.height / 2.) // Shrink to half of the editor height
2323                    .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
2324            );
2325
2326            let hover = if context_menu.is_some() {
2327                None
2328            } else {
2329                editor.hover_state.render(
2330                    &snapshot,
2331                    &style,
2332                    visible_rows,
2333                    max_size,
2334                    editor.workspace.as_ref().map(|(w, _)| w.clone()),
2335                    cx,
2336                )
2337            };
2338
2339            let editor_view = cx.view().clone();
2340            let fold_indicators = if gutter_settings.folds {
2341                cx.with_element_context(|cx| {
2342                    cx.with_element_id(Some("gutter_fold_indicators"), |_cx| {
2343                        editor.render_fold_indicators(
2344                            fold_statuses,
2345                            &style,
2346                            editor.gutter_hovered,
2347                            line_height,
2348                            gutter_dimensions.margin,
2349                            editor_view,
2350                        )
2351                    })
2352                })
2353            } else {
2354                Vec::new()
2355            };
2356
2357            let invisible_symbol_font_size = font_size / 2.;
2358            let tab_invisible = cx
2359                .text_system()
2360                .shape_line(
2361                    "".into(),
2362                    invisible_symbol_font_size,
2363                    &[TextRun {
2364                        len: "".len(),
2365                        font: self.style.text.font(),
2366                        color: cx.theme().colors().editor_invisible,
2367                        background_color: None,
2368                        underline: None,
2369                        strikethrough: None,
2370                    }],
2371                )
2372                .unwrap();
2373            let space_invisible = cx
2374                .text_system()
2375                .shape_line(
2376                    "".into(),
2377                    invisible_symbol_font_size,
2378                    &[TextRun {
2379                        len: "".len(),
2380                        font: self.style.text.font(),
2381                        color: cx.theme().colors().editor_invisible,
2382                        background_color: None,
2383                        underline: None,
2384                        strikethrough: None,
2385                    }],
2386                )
2387                .unwrap();
2388
2389            LayoutState {
2390                mode: snapshot.mode,
2391                position_map: Arc::new(PositionMap {
2392                    size: bounds.size,
2393                    scroll_position: point(
2394                        scroll_position.x * em_width,
2395                        scroll_position.y * line_height,
2396                    ),
2397                    scroll_max,
2398                    line_layouts,
2399                    line_height,
2400                    em_width,
2401                    em_advance,
2402                    snapshot,
2403                }),
2404                visible_anchor_range: start_anchor..end_anchor,
2405                visible_display_row_range: start_row..end_row,
2406                wrap_guides,
2407                gutter_size,
2408                gutter_dimensions,
2409                text_size,
2410                scrollbar_row_range,
2411                show_scrollbars,
2412                is_singleton,
2413                max_row,
2414                active_rows,
2415                highlighted_rows,
2416                highlighted_ranges,
2417                redacted_ranges,
2418                line_numbers,
2419                display_hunks,
2420                blocks,
2421                selections,
2422                context_menu,
2423                code_actions_indicator,
2424                fold_indicators,
2425                tab_invisible,
2426                space_invisible,
2427                hover_popovers: hover,
2428            }
2429        })
2430    }
2431
2432    #[allow(clippy::too_many_arguments)]
2433    fn layout_blocks(
2434        &self,
2435        rows: Range<u32>,
2436        snapshot: &EditorSnapshot,
2437        editor_width: Pixels,
2438        scroll_width: Pixels,
2439        text_width: Pixels,
2440        gutter_dimensions: &GutterDimensions,
2441        em_width: Pixels,
2442        text_x: Pixels,
2443        line_height: Pixels,
2444        style: &EditorStyle,
2445        line_layouts: &[LineWithInvisibles],
2446        editor: &mut Editor,
2447        cx: &mut ElementContext,
2448    ) -> (Pixels, Vec<BlockLayout>) {
2449        let mut block_id = 0;
2450        let (fixed_blocks, non_fixed_blocks) = snapshot
2451            .blocks_in_range(rows.clone())
2452            .partition::<Vec<_>, _>(|(_, block)| match block {
2453                TransformBlock::ExcerptHeader { .. } => false,
2454                TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
2455            });
2456
2457        let render_block = |block: &TransformBlock,
2458                            available_space: Size<AvailableSpace>,
2459                            block_id: usize,
2460                            editor: &mut Editor,
2461                            cx: &mut ElementContext| {
2462            let mut element = match block {
2463                TransformBlock::Custom(block) => {
2464                    let align_to = block
2465                        .position()
2466                        .to_point(&snapshot.buffer_snapshot)
2467                        .to_display_point(snapshot);
2468                    let anchor_x = text_x
2469                        + if rows.contains(&align_to.row()) {
2470                            line_layouts[(align_to.row() - rows.start) as usize]
2471                                .line
2472                                .x_for_index(align_to.column() as usize)
2473                        } else {
2474                            layout_line(align_to.row(), snapshot, style, cx)
2475                                .unwrap()
2476                                .x_for_index(align_to.column() as usize)
2477                        };
2478
2479                    block.render(&mut BlockContext {
2480                        context: cx,
2481                        anchor_x,
2482                        gutter_dimensions,
2483                        line_height,
2484                        em_width,
2485                        block_id,
2486                        max_width: scroll_width.max(text_width),
2487                        editor_style: &self.style,
2488                    })
2489                }
2490
2491                TransformBlock::ExcerptHeader {
2492                    buffer,
2493                    range,
2494                    starts_new_buffer,
2495                    ..
2496                } => {
2497                    let include_root = editor
2498                        .project
2499                        .as_ref()
2500                        .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
2501                        .unwrap_or_default();
2502
2503                    let jump_handler = project::File::from_dyn(buffer.file()).map(|file| {
2504                        let jump_path = ProjectPath {
2505                            worktree_id: file.worktree_id(cx),
2506                            path: file.path.clone(),
2507                        };
2508                        let jump_anchor = range
2509                            .primary
2510                            .as_ref()
2511                            .map_or(range.context.start, |primary| primary.start);
2512                        let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
2513
2514                        cx.listener_for(&self.editor, move |editor, _, cx| {
2515                            editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
2516                        })
2517                    });
2518
2519                    let element = if *starts_new_buffer {
2520                        let path = buffer.resolve_file_path(cx, include_root);
2521                        let mut filename = None;
2522                        let mut parent_path = None;
2523                        // Can't use .and_then() because `.file_name()` and `.parent()` return references :(
2524                        if let Some(path) = path {
2525                            filename = path.file_name().map(|f| f.to_string_lossy().to_string());
2526                            parent_path = path
2527                                .parent()
2528                                .map(|p| SharedString::from(p.to_string_lossy().to_string() + "/"));
2529                        }
2530
2531                        v_flex()
2532                            .id(("path header container", block_id))
2533                            .size_full()
2534                            .justify_center()
2535                            .p(gpui::px(6.))
2536                            .child(
2537                                h_flex()
2538                                    .id("path header block")
2539                                    .size_full()
2540                                    .pl(gpui::px(12.))
2541                                    .pr(gpui::px(8.))
2542                                    .rounded_md()
2543                                    .shadow_md()
2544                                    .border()
2545                                    .border_color(cx.theme().colors().border)
2546                                    .bg(cx.theme().colors().editor_subheader_background)
2547                                    .justify_between()
2548                                    .hover(|style| style.bg(cx.theme().colors().element_hover))
2549                                    .child(
2550                                        h_flex().gap_3().child(
2551                                            h_flex()
2552                                                .gap_2()
2553                                                .child(
2554                                                    filename
2555                                                        .map(SharedString::from)
2556                                                        .unwrap_or_else(|| "untitled".into()),
2557                                                )
2558                                                .when_some(parent_path, |then, path| {
2559                                                    then.child(
2560                                                        div().child(path).text_color(
2561                                                            cx.theme().colors().text_muted,
2562                                                        ),
2563                                                    )
2564                                                }),
2565                                        ),
2566                                    )
2567                                    .when_some(jump_handler, |this, jump_handler| {
2568                                        this.cursor_pointer()
2569                                            .tooltip(|cx| {
2570                                                Tooltip::for_action(
2571                                                    "Jump to Buffer",
2572                                                    &OpenExcerpts,
2573                                                    cx,
2574                                                )
2575                                            })
2576                                            .on_mouse_down(MouseButton::Left, |_, cx| {
2577                                                cx.stop_propagation()
2578                                            })
2579                                            .on_click(jump_handler)
2580                                    }),
2581                            )
2582                    } else {
2583                        h_flex()
2584                            .id(("collapsed context", block_id))
2585                            .size_full()
2586                            .gap(gutter_dimensions.left_padding + gutter_dimensions.right_padding)
2587                            .child(
2588                                h_flex()
2589                                    .justify_end()
2590                                    .flex_none()
2591                                    .w(gutter_dimensions.width
2592                                        - (gutter_dimensions.left_padding
2593                                            + gutter_dimensions.right_padding))
2594                                    .h_full()
2595                                    .text_buffer(cx)
2596                                    .text_color(cx.theme().colors().editor_line_number)
2597                                    .child("..."),
2598                            )
2599                            .child(
2600                                ButtonLike::new("jump to collapsed context")
2601                                    .style(ButtonStyle::Transparent)
2602                                    .full_width()
2603                                    .child(
2604                                        div()
2605                                            .h_px()
2606                                            .w_full()
2607                                            .bg(cx.theme().colors().border_variant)
2608                                            .group_hover("", |style| {
2609                                                style.bg(cx.theme().colors().border)
2610                                            }),
2611                                    )
2612                                    .when_some(jump_handler, |this, jump_handler| {
2613                                        this.on_click(jump_handler).tooltip(|cx| {
2614                                            Tooltip::for_action("Jump to Buffer", &OpenExcerpts, cx)
2615                                        })
2616                                    }),
2617                            )
2618                    };
2619                    element.into_any()
2620                }
2621            };
2622
2623            let size = element.measure(available_space, cx);
2624            (element, size)
2625        };
2626
2627        let mut fixed_block_max_width = Pixels::ZERO;
2628        let mut blocks = Vec::new();
2629        for (row, block) in fixed_blocks {
2630            let available_space = size(
2631                AvailableSpace::MinContent,
2632                AvailableSpace::Definite(block.height() as f32 * line_height),
2633            );
2634            let (element, element_size) =
2635                render_block(block, available_space, block_id, editor, cx);
2636            block_id += 1;
2637            fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
2638            blocks.push(BlockLayout {
2639                row,
2640                element,
2641                available_space,
2642                style: BlockStyle::Fixed,
2643            });
2644        }
2645        for (row, block) in non_fixed_blocks {
2646            let style = match block {
2647                TransformBlock::Custom(block) => block.style(),
2648                TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
2649            };
2650            let width = match style {
2651                BlockStyle::Sticky => editor_width,
2652                BlockStyle::Flex => editor_width
2653                    .max(fixed_block_max_width)
2654                    .max(gutter_dimensions.width + scroll_width),
2655                BlockStyle::Fixed => unreachable!(),
2656            };
2657            let available_space = size(
2658                AvailableSpace::Definite(width),
2659                AvailableSpace::Definite(block.height() as f32 * line_height),
2660            );
2661            let (element, _) = render_block(block, available_space, block_id, editor, cx);
2662            block_id += 1;
2663            blocks.push(BlockLayout {
2664                row,
2665                element,
2666                available_space,
2667                style,
2668            });
2669        }
2670        (
2671            scroll_width.max(fixed_block_max_width - gutter_dimensions.width),
2672            blocks,
2673        )
2674    }
2675
2676    fn paint_scroll_wheel_listener(
2677        &mut self,
2678        interactive_bounds: &InteractiveBounds,
2679        layout: &LayoutState,
2680        cx: &mut ElementContext,
2681    ) {
2682        cx.on_mouse_event({
2683            let position_map = layout.position_map.clone();
2684            let editor = self.editor.clone();
2685            let interactive_bounds = interactive_bounds.clone();
2686            let mut delta = ScrollDelta::default();
2687
2688            move |event: &ScrollWheelEvent, phase, cx| {
2689                if phase == DispatchPhase::Bubble
2690                    && interactive_bounds.visibly_contains(&event.position, cx)
2691                {
2692                    delta = delta.coalesce(event.delta);
2693                    editor.update(cx, |editor, cx| {
2694                        let position = event.position;
2695                        let position_map: &PositionMap = &position_map;
2696                        let bounds = &interactive_bounds;
2697                        if !bounds.visibly_contains(&position, cx) {
2698                            return;
2699                        }
2700
2701                        let line_height = position_map.line_height;
2702                        let max_glyph_width = position_map.em_width;
2703                        let (delta, axis) = match delta {
2704                            gpui::ScrollDelta::Pixels(mut pixels) => {
2705                                //Trackpad
2706                                let axis = position_map.snapshot.ongoing_scroll.filter(&mut pixels);
2707                                (pixels, axis)
2708                            }
2709
2710                            gpui::ScrollDelta::Lines(lines) => {
2711                                //Not trackpad
2712                                let pixels =
2713                                    point(lines.x * max_glyph_width, lines.y * line_height);
2714                                (pixels, None)
2715                            }
2716                        };
2717
2718                        let scroll_position = position_map.snapshot.scroll_position();
2719                        let x = f32::from(
2720                            (scroll_position.x * max_glyph_width - delta.x) / max_glyph_width,
2721                        );
2722                        let y =
2723                            f32::from((scroll_position.y * line_height - delta.y) / line_height);
2724                        let scroll_position =
2725                            point(x, y).clamp(&point(0., 0.), &position_map.scroll_max);
2726                        editor.scroll(scroll_position, axis, cx);
2727                        cx.stop_propagation();
2728                    });
2729                }
2730            }
2731        });
2732    }
2733
2734    fn paint_mouse_listeners(
2735        &mut self,
2736        bounds: Bounds<Pixels>,
2737        gutter_bounds: Bounds<Pixels>,
2738        text_bounds: Bounds<Pixels>,
2739        layout: &LayoutState,
2740        cx: &mut ElementContext,
2741    ) {
2742        let interactive_bounds = InteractiveBounds {
2743            bounds: bounds.intersect(&cx.content_mask().bounds),
2744            stacking_order: cx.stacking_order().clone(),
2745        };
2746
2747        self.paint_scroll_wheel_listener(&interactive_bounds, layout, cx);
2748
2749        cx.on_mouse_event({
2750            let position_map = layout.position_map.clone();
2751            let editor = self.editor.clone();
2752            let stacking_order = cx.stacking_order().clone();
2753            let interactive_bounds = interactive_bounds.clone();
2754
2755            move |event: &MouseDownEvent, phase, cx| {
2756                if phase == DispatchPhase::Bubble
2757                    && interactive_bounds.visibly_contains(&event.position, cx)
2758                {
2759                    match event.button {
2760                        MouseButton::Left => editor.update(cx, |editor, cx| {
2761                            Self::mouse_left_down(
2762                                editor,
2763                                event,
2764                                &position_map,
2765                                text_bounds,
2766                                gutter_bounds,
2767                                &stacking_order,
2768                                cx,
2769                            );
2770                        }),
2771                        MouseButton::Right => editor.update(cx, |editor, cx| {
2772                            Self::mouse_right_down(editor, event, &position_map, text_bounds, cx);
2773                        }),
2774                        _ => {}
2775                    };
2776                }
2777            }
2778        });
2779
2780        cx.on_mouse_event({
2781            let position_map = layout.position_map.clone();
2782            let editor = self.editor.clone();
2783            let stacking_order = cx.stacking_order().clone();
2784            let interactive_bounds = interactive_bounds.clone();
2785
2786            move |event: &MouseUpEvent, phase, cx| {
2787                if phase == DispatchPhase::Bubble {
2788                    editor.update(cx, |editor, cx| {
2789                        Self::mouse_up(
2790                            editor,
2791                            event,
2792                            &position_map,
2793                            text_bounds,
2794                            &interactive_bounds,
2795                            &stacking_order,
2796                            cx,
2797                        )
2798                    });
2799                }
2800            }
2801        });
2802        cx.on_mouse_event({
2803            let position_map = layout.position_map.clone();
2804            let editor = self.editor.clone();
2805            let stacking_order = cx.stacking_order().clone();
2806
2807            move |event: &MouseMoveEvent, phase, cx| {
2808                // if editor.has_pending_selection() && event.pressed_button == Some(MouseButton::Left) {
2809
2810                if phase == DispatchPhase::Bubble {
2811                    editor.update(cx, |editor, cx| {
2812                        if event.pressed_button == Some(MouseButton::Left) {
2813                            Self::mouse_dragged(
2814                                editor,
2815                                event,
2816                                &position_map,
2817                                text_bounds,
2818                                gutter_bounds,
2819                                &stacking_order,
2820                                cx,
2821                            )
2822                        }
2823
2824                        if interactive_bounds.visibly_contains(&event.position, cx) {
2825                            Self::mouse_moved(
2826                                editor,
2827                                event,
2828                                &position_map,
2829                                text_bounds,
2830                                gutter_bounds,
2831                                &stacking_order,
2832                                cx,
2833                            )
2834                        }
2835                    });
2836                }
2837            }
2838        });
2839    }
2840}
2841
2842#[derive(Debug)]
2843pub(crate) struct LineWithInvisibles {
2844    pub line: ShapedLine,
2845    invisibles: Vec<Invisible>,
2846}
2847
2848impl LineWithInvisibles {
2849    fn from_chunks<'a>(
2850        chunks: impl Iterator<Item = HighlightedChunk<'a>>,
2851        text_style: &TextStyle,
2852        max_line_len: usize,
2853        max_line_count: usize,
2854        line_number_layouts: &[Option<ShapedLine>],
2855        editor_mode: EditorMode,
2856        cx: &WindowContext,
2857    ) -> Vec<Self> {
2858        let mut layouts = Vec::with_capacity(max_line_count);
2859        let mut line = String::new();
2860        let mut invisibles = Vec::new();
2861        let mut styles = Vec::new();
2862        let mut non_whitespace_added = false;
2863        let mut row = 0;
2864        let mut line_exceeded_max_len = false;
2865        let font_size = text_style.font_size.to_pixels(cx.rem_size());
2866
2867        for highlighted_chunk in chunks.chain([HighlightedChunk {
2868            chunk: "\n",
2869            style: None,
2870            is_tab: false,
2871        }]) {
2872            for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
2873                if ix > 0 {
2874                    let shaped_line = cx
2875                        .text_system()
2876                        .shape_line(line.clone().into(), font_size, &styles)
2877                        .unwrap();
2878                    layouts.push(Self {
2879                        line: shaped_line,
2880                        invisibles: invisibles.drain(..).collect(),
2881                    });
2882
2883                    line.clear();
2884                    styles.clear();
2885                    row += 1;
2886                    line_exceeded_max_len = false;
2887                    non_whitespace_added = false;
2888                    if row == max_line_count {
2889                        return layouts;
2890                    }
2891                }
2892
2893                if !line_chunk.is_empty() && !line_exceeded_max_len {
2894                    let text_style = if let Some(style) = highlighted_chunk.style {
2895                        Cow::Owned(text_style.clone().highlight(style))
2896                    } else {
2897                        Cow::Borrowed(text_style)
2898                    };
2899
2900                    if line.len() + line_chunk.len() > max_line_len {
2901                        let mut chunk_len = max_line_len - line.len();
2902                        while !line_chunk.is_char_boundary(chunk_len) {
2903                            chunk_len -= 1;
2904                        }
2905                        line_chunk = &line_chunk[..chunk_len];
2906                        line_exceeded_max_len = true;
2907                    }
2908
2909                    styles.push(TextRun {
2910                        len: line_chunk.len(),
2911                        font: text_style.font(),
2912                        color: text_style.color,
2913                        background_color: text_style.background_color,
2914                        underline: text_style.underline,
2915                        strikethrough: text_style.strikethrough,
2916                    });
2917
2918                    if editor_mode == EditorMode::Full {
2919                        // Line wrap pads its contents with fake whitespaces,
2920                        // avoid printing them
2921                        let inside_wrapped_string = line_number_layouts
2922                            .get(row)
2923                            .and_then(|layout| layout.as_ref())
2924                            .is_none();
2925                        if highlighted_chunk.is_tab {
2926                            if non_whitespace_added || !inside_wrapped_string {
2927                                invisibles.push(Invisible::Tab {
2928                                    line_start_offset: line.len(),
2929                                });
2930                            }
2931                        } else {
2932                            invisibles.extend(
2933                                line_chunk
2934                                    .chars()
2935                                    .enumerate()
2936                                    .filter(|(_, line_char)| {
2937                                        let is_whitespace = line_char.is_whitespace();
2938                                        non_whitespace_added |= !is_whitespace;
2939                                        is_whitespace
2940                                            && (non_whitespace_added || !inside_wrapped_string)
2941                                    })
2942                                    .map(|(whitespace_index, _)| Invisible::Whitespace {
2943                                        line_offset: line.len() + whitespace_index,
2944                                    }),
2945                            )
2946                        }
2947                    }
2948
2949                    line.push_str(line_chunk);
2950                }
2951            }
2952        }
2953
2954        layouts
2955    }
2956
2957    fn draw(
2958        &self,
2959        layout: &LayoutState,
2960        row: u32,
2961        content_origin: gpui::Point<Pixels>,
2962        whitespace_setting: ShowWhitespaceSetting,
2963        selection_ranges: &[Range<DisplayPoint>],
2964        cx: &mut ElementContext,
2965    ) {
2966        let line_height = layout.position_map.line_height;
2967        let line_y = line_height * row as f32 - layout.position_map.scroll_position.y;
2968
2969        self.line
2970            .paint(
2971                content_origin + gpui::point(-layout.position_map.scroll_position.x, line_y),
2972                line_height,
2973                cx,
2974            )
2975            .log_err();
2976
2977        self.draw_invisibles(
2978            &selection_ranges,
2979            layout,
2980            content_origin,
2981            line_y,
2982            row,
2983            line_height,
2984            whitespace_setting,
2985            cx,
2986        );
2987    }
2988
2989    fn draw_invisibles(
2990        &self,
2991        selection_ranges: &[Range<DisplayPoint>],
2992        layout: &LayoutState,
2993        content_origin: gpui::Point<Pixels>,
2994        line_y: Pixels,
2995        row: u32,
2996        line_height: Pixels,
2997        whitespace_setting: ShowWhitespaceSetting,
2998        cx: &mut ElementContext,
2999    ) {
3000        let allowed_invisibles_regions = match whitespace_setting {
3001            ShowWhitespaceSetting::None => return,
3002            ShowWhitespaceSetting::Selection => Some(selection_ranges),
3003            ShowWhitespaceSetting::All => None,
3004        };
3005
3006        for invisible in &self.invisibles {
3007            let (&token_offset, invisible_symbol) = match invisible {
3008                Invisible::Tab { line_start_offset } => (line_start_offset, &layout.tab_invisible),
3009                Invisible::Whitespace { line_offset } => (line_offset, &layout.space_invisible),
3010            };
3011
3012            let x_offset = self.line.x_for_index(token_offset);
3013            let invisible_offset =
3014                (layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0;
3015            let origin = content_origin
3016                + gpui::point(
3017                    x_offset + invisible_offset - layout.position_map.scroll_position.x,
3018                    line_y,
3019                );
3020
3021            if let Some(allowed_regions) = allowed_invisibles_regions {
3022                let invisible_point = DisplayPoint::new(row, token_offset as u32);
3023                if !allowed_regions
3024                    .iter()
3025                    .any(|region| region.start <= invisible_point && invisible_point < region.end)
3026                {
3027                    continue;
3028                }
3029            }
3030            invisible_symbol.paint(origin, line_height, cx).log_err();
3031        }
3032    }
3033}
3034
3035#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3036enum Invisible {
3037    Tab { line_start_offset: usize },
3038    Whitespace { line_offset: usize },
3039}
3040
3041impl Element for EditorElement {
3042    type State = ();
3043
3044    fn request_layout(
3045        &mut self,
3046        _element_state: Option<Self::State>,
3047        cx: &mut gpui::ElementContext,
3048    ) -> (gpui::LayoutId, Self::State) {
3049        cx.with_view_id(self.editor.entity_id(), |cx| {
3050            self.editor.update(cx, |editor, cx| {
3051                editor.set_style(self.style.clone(), cx);
3052
3053                let layout_id = match editor.mode {
3054                    EditorMode::SingleLine => {
3055                        let rem_size = cx.rem_size();
3056                        let mut style = Style::default();
3057                        style.size.width = relative(1.).into();
3058                        style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
3059                        cx.with_element_context(|cx| cx.request_layout(&style, None))
3060                    }
3061                    EditorMode::AutoHeight { max_lines } => {
3062                        let editor_handle = cx.view().clone();
3063                        let max_line_number_width =
3064                            self.max_line_number_width(&editor.snapshot(cx), cx);
3065                        cx.with_element_context(|cx| {
3066                            cx.request_measured_layout(
3067                                Style::default(),
3068                                move |known_dimensions, _, cx| {
3069                                    editor_handle
3070                                        .update(cx, |editor, cx| {
3071                                            compute_auto_height_layout(
3072                                                editor,
3073                                                max_lines,
3074                                                max_line_number_width,
3075                                                known_dimensions,
3076                                                cx,
3077                                            )
3078                                        })
3079                                        .unwrap_or_default()
3080                                },
3081                            )
3082                        })
3083                    }
3084                    EditorMode::Full => {
3085                        let mut style = Style::default();
3086                        style.size.width = relative(1.).into();
3087                        style.size.height = relative(1.).into();
3088                        cx.with_element_context(|cx| cx.request_layout(&style, None))
3089                    }
3090                };
3091
3092                (layout_id, ())
3093            })
3094        })
3095    }
3096
3097    fn paint(
3098        &mut self,
3099        bounds: Bounds<gpui::Pixels>,
3100        _element_state: &mut Self::State,
3101        cx: &mut gpui::ElementContext,
3102    ) {
3103        let editor = self.editor.clone();
3104
3105        cx.paint_view(self.editor.entity_id(), |cx| {
3106            cx.with_text_style(
3107                Some(gpui::TextStyleRefinement {
3108                    font_size: Some(self.style.text.font_size),
3109                    line_height: Some(self.style.text.line_height),
3110                    ..Default::default()
3111                }),
3112                |cx| {
3113                    let mut layout = self.compute_layout(bounds, cx);
3114                    let gutter_bounds = Bounds {
3115                        origin: bounds.origin,
3116                        size: layout.gutter_size,
3117                    };
3118                    let text_bounds = Bounds {
3119                        origin: gutter_bounds.upper_right(),
3120                        size: layout.text_size,
3121                    };
3122
3123                    let focus_handle = editor.focus_handle(cx);
3124                    let key_context = self.editor.read(cx).key_context(cx);
3125                    cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
3126                        self.register_actions(cx);
3127
3128                        cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
3129                            self.register_key_listeners(cx, text_bounds, &layout);
3130                            cx.handle_input(
3131                                &focus_handle,
3132                                ElementInputHandler::new(bounds, self.editor.clone()),
3133                            );
3134
3135                            self.paint_background(gutter_bounds, text_bounds, &layout, cx);
3136                            if layout.gutter_size.width > Pixels::ZERO {
3137                                self.paint_gutter(gutter_bounds, &mut layout, cx);
3138                            }
3139                            self.paint_text(text_bounds, &mut layout, cx);
3140
3141                            cx.with_z_index(0, |cx| {
3142                                self.paint_mouse_listeners(
3143                                    bounds,
3144                                    gutter_bounds,
3145                                    text_bounds,
3146                                    &layout,
3147                                    cx,
3148                                );
3149                            });
3150                            if !layout.blocks.is_empty() {
3151                                cx.with_z_index(0, |cx| {
3152                                    cx.with_element_id(Some("editor_blocks"), |cx| {
3153                                        self.paint_blocks(bounds, &mut layout, cx);
3154                                    });
3155                                })
3156                            }
3157
3158                            cx.with_z_index(1, |cx| {
3159                                self.paint_overlays(text_bounds, &mut layout, cx);
3160                            });
3161
3162                            cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
3163                        });
3164                    })
3165                },
3166            )
3167        })
3168    }
3169}
3170
3171impl IntoElement for EditorElement {
3172    type Element = Self;
3173
3174    fn element_id(&self) -> Option<gpui::ElementId> {
3175        self.editor.element_id()
3176    }
3177
3178    fn into_element(self) -> Self::Element {
3179        self
3180    }
3181}
3182
3183type BufferRow = u32;
3184
3185pub struct LayoutState {
3186    position_map: Arc<PositionMap>,
3187    gutter_size: Size<Pixels>,
3188    gutter_dimensions: GutterDimensions,
3189    text_size: gpui::Size<Pixels>,
3190    mode: EditorMode,
3191    wrap_guides: SmallVec<[(Pixels, bool); 2]>,
3192    visible_anchor_range: Range<Anchor>,
3193    visible_display_row_range: Range<u32>,
3194    active_rows: BTreeMap<u32, bool>,
3195    highlighted_rows: Option<Range<u32>>,
3196    line_numbers: Vec<Option<ShapedLine>>,
3197    display_hunks: Vec<DisplayDiffHunk>,
3198    blocks: Vec<BlockLayout>,
3199    highlighted_ranges: Vec<(Range<DisplayPoint>, Hsla)>,
3200    redacted_ranges: Vec<Range<DisplayPoint>>,
3201    selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
3202    scrollbar_row_range: Range<f32>,
3203    show_scrollbars: bool,
3204    is_singleton: bool,
3205    max_row: u32,
3206    context_menu: Option<(DisplayPoint, AnyElement)>,
3207    code_actions_indicator: Option<CodeActionsIndicator>,
3208    hover_popovers: Option<(DisplayPoint, Vec<AnyElement>)>,
3209    fold_indicators: Vec<Option<IconButton>>,
3210    tab_invisible: ShapedLine,
3211    space_invisible: ShapedLine,
3212}
3213
3214impl LayoutState {
3215    fn line_end_overshoot(&self) -> Pixels {
3216        0.15 * self.position_map.line_height
3217    }
3218}
3219
3220struct CodeActionsIndicator {
3221    row: u32,
3222    button: IconButton,
3223}
3224
3225struct PositionMap {
3226    size: Size<Pixels>,
3227    line_height: Pixels,
3228    scroll_position: gpui::Point<Pixels>,
3229    scroll_max: gpui::Point<f32>,
3230    em_width: Pixels,
3231    em_advance: Pixels,
3232    line_layouts: Vec<LineWithInvisibles>,
3233    snapshot: EditorSnapshot,
3234}
3235
3236#[derive(Debug, Copy, Clone)]
3237pub struct PointForPosition {
3238    pub previous_valid: DisplayPoint,
3239    pub next_valid: DisplayPoint,
3240    pub exact_unclipped: DisplayPoint,
3241    pub column_overshoot_after_line_end: u32,
3242}
3243
3244impl PointForPosition {
3245    pub fn as_valid(&self) -> Option<DisplayPoint> {
3246        if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
3247            Some(self.previous_valid)
3248        } else {
3249            None
3250        }
3251    }
3252}
3253
3254impl PositionMap {
3255    fn point_for_position(
3256        &self,
3257        text_bounds: Bounds<Pixels>,
3258        position: gpui::Point<Pixels>,
3259    ) -> PointForPosition {
3260        let scroll_position = self.snapshot.scroll_position();
3261        let position = position - text_bounds.origin;
3262        let y = position.y.max(px(0.)).min(self.size.height);
3263        let x = position.x + (scroll_position.x * self.em_width);
3264        let row = (f32::from(y / self.line_height) + scroll_position.y) as u32;
3265
3266        let (column, x_overshoot_after_line_end) = if let Some(line) = self
3267            .line_layouts
3268            .get(row as usize - scroll_position.y as usize)
3269            .map(|&LineWithInvisibles { ref line, .. }| line)
3270        {
3271            if let Some(ix) = line.index_for_x(x) {
3272                (ix as u32, px(0.))
3273            } else {
3274                (line.len as u32, px(0.).max(x - line.width))
3275            }
3276        } else {
3277            (0, x)
3278        };
3279
3280        let mut exact_unclipped = DisplayPoint::new(row, column);
3281        let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
3282        let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
3283
3284        let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
3285        *exact_unclipped.column_mut() += column_overshoot_after_line_end;
3286        PointForPosition {
3287            previous_valid,
3288            next_valid,
3289            exact_unclipped,
3290            column_overshoot_after_line_end,
3291        }
3292    }
3293}
3294
3295struct BlockLayout {
3296    row: u32,
3297    element: AnyElement,
3298    available_space: Size<AvailableSpace>,
3299    style: BlockStyle,
3300}
3301
3302fn layout_line(
3303    row: u32,
3304    snapshot: &EditorSnapshot,
3305    style: &EditorStyle,
3306    cx: &WindowContext,
3307) -> Result<ShapedLine> {
3308    let mut line = snapshot.line(row);
3309
3310    if line.len() > MAX_LINE_LEN {
3311        let mut len = MAX_LINE_LEN;
3312        while !line.is_char_boundary(len) {
3313            len -= 1;
3314        }
3315
3316        line.truncate(len);
3317    }
3318
3319    cx.text_system().shape_line(
3320        line.into(),
3321        style.text.font_size.to_pixels(cx.rem_size()),
3322        &[TextRun {
3323            len: snapshot.line_len(row) as usize,
3324            font: style.text.font(),
3325            color: Hsla::default(),
3326            background_color: None,
3327            underline: None,
3328            strikethrough: None,
3329        }],
3330    )
3331}
3332
3333#[derive(Debug)]
3334pub struct Cursor {
3335    origin: gpui::Point<Pixels>,
3336    block_width: Pixels,
3337    line_height: Pixels,
3338    color: Hsla,
3339    shape: CursorShape,
3340    block_text: Option<ShapedLine>,
3341    cursor_name: Option<CursorName>,
3342}
3343
3344#[derive(Debug)]
3345pub struct CursorName {
3346    string: SharedString,
3347    color: Hsla,
3348    is_top_row: bool,
3349    z_index: u16,
3350}
3351
3352impl Cursor {
3353    pub fn new(
3354        origin: gpui::Point<Pixels>,
3355        block_width: Pixels,
3356        line_height: Pixels,
3357        color: Hsla,
3358        shape: CursorShape,
3359        block_text: Option<ShapedLine>,
3360        cursor_name: Option<CursorName>,
3361    ) -> Cursor {
3362        Cursor {
3363            origin,
3364            block_width,
3365            line_height,
3366            color,
3367            shape,
3368            block_text,
3369            cursor_name,
3370        }
3371    }
3372
3373    pub fn bounding_rect(&self, origin: gpui::Point<Pixels>) -> Bounds<Pixels> {
3374        Bounds {
3375            origin: self.origin + origin,
3376            size: size(self.block_width, self.line_height),
3377        }
3378    }
3379
3380    pub fn paint(&self, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
3381        let bounds = match self.shape {
3382            CursorShape::Bar => Bounds {
3383                origin: self.origin + origin,
3384                size: size(px(2.0), self.line_height),
3385            },
3386            CursorShape::Block | CursorShape::Hollow => Bounds {
3387                origin: self.origin + origin,
3388                size: size(self.block_width, self.line_height),
3389            },
3390            CursorShape::Underscore => Bounds {
3391                origin: self.origin
3392                    + origin
3393                    + gpui::Point::new(Pixels::ZERO, self.line_height - px(2.0)),
3394                size: size(self.block_width, px(2.0)),
3395            },
3396        };
3397
3398        //Draw background or border quad
3399        let cursor = if matches!(self.shape, CursorShape::Hollow) {
3400            outline(bounds, self.color)
3401        } else {
3402            fill(bounds, self.color)
3403        };
3404
3405        if let Some(name) = &self.cursor_name {
3406            let text_size = self.line_height / 1.5;
3407
3408            let name_origin = if name.is_top_row {
3409                point(bounds.right() - px(1.), bounds.top())
3410            } else {
3411                point(bounds.left(), bounds.top() - text_size / 2. - px(1.))
3412            };
3413            cx.with_z_index(name.z_index, |cx| {
3414                div()
3415                    .bg(self.color)
3416                    .text_size(text_size)
3417                    .px_0p5()
3418                    .line_height(text_size + px(2.))
3419                    .text_color(name.color)
3420                    .child(name.string.clone())
3421                    .into_any_element()
3422                    .draw(
3423                        name_origin,
3424                        size(AvailableSpace::MinContent, AvailableSpace::MinContent),
3425                        cx,
3426                    )
3427            })
3428        }
3429
3430        cx.paint_quad(cursor);
3431
3432        if let Some(block_text) = &self.block_text {
3433            block_text
3434                .paint(self.origin + origin, self.line_height, cx)
3435                .log_err();
3436        }
3437    }
3438
3439    pub fn shape(&self) -> CursorShape {
3440        self.shape
3441    }
3442}
3443
3444#[derive(Debug)]
3445pub struct HighlightedRange {
3446    pub start_y: Pixels,
3447    pub line_height: Pixels,
3448    pub lines: Vec<HighlightedRangeLine>,
3449    pub color: Hsla,
3450    pub corner_radius: Pixels,
3451}
3452
3453#[derive(Debug)]
3454pub struct HighlightedRangeLine {
3455    pub start_x: Pixels,
3456    pub end_x: Pixels,
3457}
3458
3459impl HighlightedRange {
3460    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ElementContext) {
3461        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
3462            self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx);
3463            self.paint_lines(
3464                self.start_y + self.line_height,
3465                &self.lines[1..],
3466                bounds,
3467                cx,
3468            );
3469        } else {
3470            self.paint_lines(self.start_y, &self.lines, bounds, cx);
3471        }
3472    }
3473
3474    fn paint_lines(
3475        &self,
3476        start_y: Pixels,
3477        lines: &[HighlightedRangeLine],
3478        _bounds: Bounds<Pixels>,
3479        cx: &mut ElementContext,
3480    ) {
3481        if lines.is_empty() {
3482            return;
3483        }
3484
3485        let first_line = lines.first().unwrap();
3486        let last_line = lines.last().unwrap();
3487
3488        let first_top_left = point(first_line.start_x, start_y);
3489        let first_top_right = point(first_line.end_x, start_y);
3490
3491        let curve_height = point(Pixels::ZERO, self.corner_radius);
3492        let curve_width = |start_x: Pixels, end_x: Pixels| {
3493            let max = (end_x - start_x) / 2.;
3494            let width = if max < self.corner_radius {
3495                max
3496            } else {
3497                self.corner_radius
3498            };
3499
3500            point(width, Pixels::ZERO)
3501        };
3502
3503        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
3504        let mut path = gpui::Path::new(first_top_right - top_curve_width);
3505        path.curve_to(first_top_right + curve_height, first_top_right);
3506
3507        let mut iter = lines.iter().enumerate().peekable();
3508        while let Some((ix, line)) = iter.next() {
3509            let bottom_right = point(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
3510
3511            if let Some((_, next_line)) = iter.peek() {
3512                let next_top_right = point(next_line.end_x, bottom_right.y);
3513
3514                match next_top_right.x.partial_cmp(&bottom_right.x).unwrap() {
3515                    Ordering::Equal => {
3516                        path.line_to(bottom_right);
3517                    }
3518                    Ordering::Less => {
3519                        let curve_width = curve_width(next_top_right.x, bottom_right.x);
3520                        path.line_to(bottom_right - curve_height);
3521                        if self.corner_radius > Pixels::ZERO {
3522                            path.curve_to(bottom_right - curve_width, bottom_right);
3523                        }
3524                        path.line_to(next_top_right + curve_width);
3525                        if self.corner_radius > Pixels::ZERO {
3526                            path.curve_to(next_top_right + curve_height, next_top_right);
3527                        }
3528                    }
3529                    Ordering::Greater => {
3530                        let curve_width = curve_width(bottom_right.x, next_top_right.x);
3531                        path.line_to(bottom_right - curve_height);
3532                        if self.corner_radius > Pixels::ZERO {
3533                            path.curve_to(bottom_right + curve_width, bottom_right);
3534                        }
3535                        path.line_to(next_top_right - curve_width);
3536                        if self.corner_radius > Pixels::ZERO {
3537                            path.curve_to(next_top_right + curve_height, next_top_right);
3538                        }
3539                    }
3540                }
3541            } else {
3542                let curve_width = curve_width(line.start_x, line.end_x);
3543                path.line_to(bottom_right - curve_height);
3544                if self.corner_radius > Pixels::ZERO {
3545                    path.curve_to(bottom_right - curve_width, bottom_right);
3546                }
3547
3548                let bottom_left = point(line.start_x, bottom_right.y);
3549                path.line_to(bottom_left + curve_width);
3550                if self.corner_radius > Pixels::ZERO {
3551                    path.curve_to(bottom_left - curve_height, bottom_left);
3552                }
3553            }
3554        }
3555
3556        if first_line.start_x > last_line.start_x {
3557            let curve_width = curve_width(last_line.start_x, first_line.start_x);
3558            let second_top_left = point(last_line.start_x, start_y + self.line_height);
3559            path.line_to(second_top_left + curve_height);
3560            if self.corner_radius > Pixels::ZERO {
3561                path.curve_to(second_top_left + curve_width, second_top_left);
3562            }
3563            let first_bottom_left = point(first_line.start_x, second_top_left.y);
3564            path.line_to(first_bottom_left - curve_width);
3565            if self.corner_radius > Pixels::ZERO {
3566                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
3567            }
3568        }
3569
3570        path.line_to(first_top_left + curve_height);
3571        if self.corner_radius > Pixels::ZERO {
3572            path.curve_to(first_top_left + top_curve_width, first_top_left);
3573        }
3574        path.line_to(first_top_right - top_curve_width);
3575
3576        cx.paint_path(path, self.color);
3577    }
3578}
3579
3580pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 {
3581    (delta.pow(1.5) / 100.0).into()
3582}
3583
3584fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
3585    (delta.pow(1.2) / 300.0).into()
3586}
3587
3588#[cfg(test)]
3589mod tests {
3590    use super::*;
3591    use crate::{
3592        display_map::{BlockDisposition, BlockProperties},
3593        editor_tests::{init_test, update_test_language_settings},
3594        Editor, MultiBuffer,
3595    };
3596    use gpui::TestAppContext;
3597    use language::language_settings;
3598    use log::info;
3599    use std::{num::NonZeroU32, sync::Arc};
3600    use util::test::sample_text;
3601
3602    #[gpui::test]
3603    fn test_shape_line_numbers(cx: &mut TestAppContext) {
3604        init_test(cx, |_| {});
3605        let window = cx.add_window(|cx| {
3606            let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
3607            Editor::new(EditorMode::Full, buffer, None, cx)
3608        });
3609
3610        let editor = window.root(cx).unwrap();
3611        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3612        let element = EditorElement::new(&editor, style);
3613
3614        let layouts = window
3615            .update(cx, |editor, cx| {
3616                let snapshot = editor.snapshot(cx);
3617                element
3618                    .shape_line_numbers(
3619                        0..6,
3620                        &Default::default(),
3621                        DisplayPoint::new(0, 0),
3622                        false,
3623                        &snapshot,
3624                        cx,
3625                    )
3626                    .0
3627            })
3628            .unwrap();
3629        assert_eq!(layouts.len(), 6);
3630
3631        let relative_rows = window
3632            .update(cx, |editor, cx| {
3633                let snapshot = editor.snapshot(cx);
3634                element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3))
3635            })
3636            .unwrap();
3637        assert_eq!(relative_rows[&0], 3);
3638        assert_eq!(relative_rows[&1], 2);
3639        assert_eq!(relative_rows[&2], 1);
3640        // current line has no relative number
3641        assert_eq!(relative_rows[&4], 1);
3642        assert_eq!(relative_rows[&5], 2);
3643
3644        // works if cursor is before screen
3645        let relative_rows = window
3646            .update(cx, |editor, cx| {
3647                let snapshot = editor.snapshot(cx);
3648
3649                element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1))
3650            })
3651            .unwrap();
3652        assert_eq!(relative_rows.len(), 3);
3653        assert_eq!(relative_rows[&3], 2);
3654        assert_eq!(relative_rows[&4], 3);
3655        assert_eq!(relative_rows[&5], 4);
3656
3657        // works if cursor is after screen
3658        let relative_rows = window
3659            .update(cx, |editor, cx| {
3660                let snapshot = editor.snapshot(cx);
3661
3662                element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6))
3663            })
3664            .unwrap();
3665        assert_eq!(relative_rows.len(), 3);
3666        assert_eq!(relative_rows[&0], 5);
3667        assert_eq!(relative_rows[&1], 4);
3668        assert_eq!(relative_rows[&2], 3);
3669    }
3670
3671    #[gpui::test]
3672    async fn test_vim_visual_selections(cx: &mut TestAppContext) {
3673        init_test(cx, |_| {});
3674
3675        let window = cx.add_window(|cx| {
3676            let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
3677            Editor::new(EditorMode::Full, buffer, None, cx)
3678        });
3679        let editor = window.root(cx).unwrap();
3680        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3681        let mut element = EditorElement::new(&editor, style);
3682
3683        window
3684            .update(cx, |editor, cx| {
3685                editor.cursor_shape = CursorShape::Block;
3686                editor.change_selections(None, cx, |s| {
3687                    s.select_ranges([
3688                        Point::new(0, 0)..Point::new(1, 0),
3689                        Point::new(3, 2)..Point::new(3, 3),
3690                        Point::new(5, 6)..Point::new(6, 0),
3691                    ]);
3692                });
3693            })
3694            .unwrap();
3695        let state = cx
3696            .update_window(window.into(), |view, cx| {
3697                cx.with_element_context(|cx| {
3698                    cx.with_view_id(view.entity_id(), |cx| {
3699                        element.compute_layout(
3700                            Bounds {
3701                                origin: point(px(500.), px(500.)),
3702                                size: size(px(500.), px(500.)),
3703                            },
3704                            cx,
3705                        )
3706                    })
3707                })
3708            })
3709            .unwrap();
3710
3711        assert_eq!(state.selections.len(), 1);
3712        let local_selections = &state.selections[0].1;
3713        assert_eq!(local_selections.len(), 3);
3714        // moves cursor back one line
3715        assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6));
3716        assert_eq!(
3717            local_selections[0].range,
3718            DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0)
3719        );
3720
3721        // moves cursor back one column
3722        assert_eq!(
3723            local_selections[1].range,
3724            DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3)
3725        );
3726        assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2));
3727
3728        // leaves cursor on the max point
3729        assert_eq!(
3730            local_selections[2].range,
3731            DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0)
3732        );
3733        assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0));
3734
3735        // active lines does not include 1 (even though the range of the selection does)
3736        assert_eq!(
3737            state.active_rows.keys().cloned().collect::<Vec<u32>>(),
3738            vec![0, 3, 5, 6]
3739        );
3740
3741        // multi-buffer support
3742        // in DisplayPoint coordinates, this is what we're dealing with:
3743        //  0: [[file
3744        //  1:   header]]
3745        //  2: aaaaaa
3746        //  3: bbbbbb
3747        //  4: cccccc
3748        //  5:
3749        //  6: ...
3750        //  7: ffffff
3751        //  8: gggggg
3752        //  9: hhhhhh
3753        // 10:
3754        // 11: [[file
3755        // 12:   header]]
3756        // 13: bbbbbb
3757        // 14: cccccc
3758        // 15: dddddd
3759        let window = cx.add_window(|cx| {
3760            let buffer = MultiBuffer::build_multi(
3761                [
3762                    (
3763                        &(sample_text(8, 6, 'a') + "\n"),
3764                        vec![
3765                            Point::new(0, 0)..Point::new(3, 0),
3766                            Point::new(4, 0)..Point::new(7, 0),
3767                        ],
3768                    ),
3769                    (
3770                        &(sample_text(8, 6, 'a') + "\n"),
3771                        vec![Point::new(1, 0)..Point::new(3, 0)],
3772                    ),
3773                ],
3774                cx,
3775            );
3776            Editor::new(EditorMode::Full, buffer, None, cx)
3777        });
3778        let editor = window.root(cx).unwrap();
3779        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3780        let mut element = EditorElement::new(&editor, style);
3781        let _state = window.update(cx, |editor, cx| {
3782            editor.cursor_shape = CursorShape::Block;
3783            editor.change_selections(None, cx, |s| {
3784                s.select_display_ranges([
3785                    DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0),
3786                    DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
3787                ]);
3788            });
3789        });
3790
3791        let state = cx
3792            .update_window(window.into(), |view, cx| {
3793                cx.with_element_context(|cx| {
3794                    cx.with_view_id(view.entity_id(), |cx| {
3795                        element.compute_layout(
3796                            Bounds {
3797                                origin: point(px(500.), px(500.)),
3798                                size: size(px(500.), px(500.)),
3799                            },
3800                            cx,
3801                        )
3802                    })
3803                })
3804            })
3805            .unwrap();
3806        assert_eq!(state.selections.len(), 1);
3807        let local_selections = &state.selections[0].1;
3808        assert_eq!(local_selections.len(), 2);
3809
3810        // moves cursor on excerpt boundary back a line
3811        // and doesn't allow selection to bleed through
3812        assert_eq!(
3813            local_selections[0].range,
3814            DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0)
3815        );
3816        assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0));
3817        // moves cursor on buffer boundary back two lines
3818        // and doesn't allow selection to bleed through
3819        assert_eq!(
3820            local_selections[1].range,
3821            DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0)
3822        );
3823        assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0));
3824    }
3825
3826    #[gpui::test]
3827    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
3828        init_test(cx, |_| {});
3829
3830        let window = cx.add_window(|cx| {
3831            let buffer = MultiBuffer::build_simple("", cx);
3832            Editor::new(EditorMode::Full, buffer, None, cx)
3833        });
3834        let editor = window.root(cx).unwrap();
3835        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
3836        window
3837            .update(cx, |editor, cx| {
3838                editor.set_placeholder_text("hello", cx);
3839                editor.insert_blocks(
3840                    [BlockProperties {
3841                        style: BlockStyle::Fixed,
3842                        disposition: BlockDisposition::Above,
3843                        height: 3,
3844                        position: Anchor::min(),
3845                        render: Arc::new(|_| div().into_any()),
3846                    }],
3847                    None,
3848                    cx,
3849                );
3850
3851                // Blur the editor so that it displays placeholder text.
3852                cx.blur();
3853            })
3854            .unwrap();
3855
3856        let mut element = EditorElement::new(&editor, style);
3857        let state = cx
3858            .update_window(window.into(), |view, cx| {
3859                cx.with_element_context(|cx| {
3860                    cx.with_view_id(view.entity_id(), |cx| {
3861                        element.compute_layout(
3862                            Bounds {
3863                                origin: point(px(500.), px(500.)),
3864                                size: size(px(500.), px(500.)),
3865                            },
3866                            cx,
3867                        )
3868                    })
3869                })
3870            })
3871            .unwrap();
3872        let size = state.position_map.size;
3873
3874        assert_eq!(state.position_map.line_layouts.len(), 4);
3875        assert_eq!(
3876            state
3877                .line_numbers
3878                .iter()
3879                .map(Option::is_some)
3880                .collect::<Vec<_>>(),
3881            &[false, false, false, true]
3882        );
3883
3884        // Don't panic.
3885        let bounds = Bounds::<Pixels>::new(Default::default(), size);
3886        cx.update_window(window.into(), |_, cx| {
3887            cx.with_element_context(|cx| element.paint(bounds, &mut (), cx))
3888        })
3889        .unwrap()
3890    }
3891
3892    #[gpui::test]
3893    fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
3894        const TAB_SIZE: u32 = 4;
3895
3896        let input_text = "\t \t|\t| a b";
3897        let expected_invisibles = vec![
3898            Invisible::Tab {
3899                line_start_offset: 0,
3900            },
3901            Invisible::Whitespace {
3902                line_offset: TAB_SIZE as usize,
3903            },
3904            Invisible::Tab {
3905                line_start_offset: TAB_SIZE as usize + 1,
3906            },
3907            Invisible::Tab {
3908                line_start_offset: TAB_SIZE as usize * 2 + 1,
3909            },
3910            Invisible::Whitespace {
3911                line_offset: TAB_SIZE as usize * 3 + 1,
3912            },
3913            Invisible::Whitespace {
3914                line_offset: TAB_SIZE as usize * 3 + 3,
3915            },
3916        ];
3917        assert_eq!(
3918            expected_invisibles.len(),
3919            input_text
3920                .chars()
3921                .filter(|initial_char| initial_char.is_whitespace())
3922                .count(),
3923            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3924        );
3925
3926        init_test(cx, |s| {
3927            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3928            s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
3929        });
3930
3931        let actual_invisibles =
3932            collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, px(500.0));
3933
3934        assert_eq!(expected_invisibles, actual_invisibles);
3935    }
3936
3937    #[gpui::test]
3938    fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
3939        init_test(cx, |s| {
3940            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3941            s.defaults.tab_size = NonZeroU32::new(4);
3942        });
3943
3944        for editor_mode_without_invisibles in [
3945            EditorMode::SingleLine,
3946            EditorMode::AutoHeight { max_lines: 100 },
3947        ] {
3948            let invisibles = collect_invisibles_from_new_editor(
3949                cx,
3950                editor_mode_without_invisibles,
3951                "\t\t\t| | a b",
3952                px(500.0),
3953            );
3954            assert!(invisibles.is_empty(),
3955                    "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}");
3956        }
3957    }
3958
3959    #[gpui::test]
3960    fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) {
3961        let tab_size = 4;
3962        let input_text = "a\tbcd   ".repeat(9);
3963        let repeated_invisibles = [
3964            Invisible::Tab {
3965                line_start_offset: 1,
3966            },
3967            Invisible::Whitespace {
3968                line_offset: tab_size as usize + 3,
3969            },
3970            Invisible::Whitespace {
3971                line_offset: tab_size as usize + 4,
3972            },
3973            Invisible::Whitespace {
3974                line_offset: tab_size as usize + 5,
3975            },
3976        ];
3977        let expected_invisibles = std::iter::once(repeated_invisibles)
3978            .cycle()
3979            .take(9)
3980            .flatten()
3981            .collect::<Vec<_>>();
3982        assert_eq!(
3983            expected_invisibles.len(),
3984            input_text
3985                .chars()
3986                .filter(|initial_char| initial_char.is_whitespace())
3987                .count(),
3988            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3989        );
3990        info!("Expected invisibles: {expected_invisibles:?}");
3991
3992        init_test(cx, |_| {});
3993
3994        // Put the same string with repeating whitespace pattern into editors of various size,
3995        // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
3996        let resize_step = 10.0;
3997        let mut editor_width = 200.0;
3998        while editor_width <= 1000.0 {
3999            update_test_language_settings(cx, |s| {
4000                s.defaults.tab_size = NonZeroU32::new(tab_size);
4001                s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
4002                s.defaults.preferred_line_length = Some(editor_width as u32);
4003                s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
4004            });
4005
4006            let actual_invisibles = collect_invisibles_from_new_editor(
4007                cx,
4008                EditorMode::Full,
4009                &input_text,
4010                px(editor_width),
4011            );
4012
4013            // Whatever the editor size is, ensure it has the same invisible kinds in the same order
4014            // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets).
4015            let mut i = 0;
4016            for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() {
4017                i = actual_index;
4018                match expected_invisibles.get(i) {
4019                    Some(expected_invisible) => match (expected_invisible, actual_invisible) {
4020                        (Invisible::Whitespace { .. }, Invisible::Whitespace { .. })
4021                        | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {}
4022                        _ => {
4023                            panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}")
4024                        }
4025                    },
4026                    None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"),
4027                }
4028            }
4029            let missing_expected_invisibles = &expected_invisibles[i + 1..];
4030            assert!(
4031                missing_expected_invisibles.is_empty(),
4032                "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}"
4033            );
4034
4035            editor_width += resize_step;
4036        }
4037    }
4038
4039    fn collect_invisibles_from_new_editor(
4040        cx: &mut TestAppContext,
4041        editor_mode: EditorMode,
4042        input_text: &str,
4043        editor_width: Pixels,
4044    ) -> Vec<Invisible> {
4045        info!(
4046            "Creating editor with mode {editor_mode:?}, width {}px and text '{input_text}'",
4047            editor_width.0
4048        );
4049        let window = cx.add_window(|cx| {
4050            let buffer = MultiBuffer::build_simple(&input_text, cx);
4051            Editor::new(editor_mode, buffer, None, cx)
4052        });
4053        let editor = window.root(cx).unwrap();
4054        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
4055        let mut element = EditorElement::new(&editor, style);
4056        window
4057            .update(cx, |editor, cx| {
4058                editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
4059                editor.set_wrap_width(Some(editor_width), cx);
4060            })
4061            .unwrap();
4062        let layout_state = cx
4063            .update_window(window.into(), |_, cx| {
4064                cx.with_element_context(|cx| {
4065                    element.compute_layout(
4066                        Bounds {
4067                            origin: point(px(500.), px(500.)),
4068                            size: size(px(500.), px(500.)),
4069                        },
4070                        cx,
4071                    )
4072                })
4073            })
4074            .unwrap();
4075
4076        layout_state
4077            .position_map
4078            .line_layouts
4079            .iter()
4080            .map(|line_with_invisibles| &line_with_invisibles.invisibles)
4081            .flatten()
4082            .cloned()
4083            .collect()
4084    }
4085}
4086
4087pub fn register_action<T: Action>(
4088    view: &View<Editor>,
4089    cx: &mut WindowContext,
4090    listener: impl Fn(&mut Editor, &T, &mut ViewContext<Editor>) + 'static,
4091) {
4092    let view = view.clone();
4093    cx.on_action(TypeId::of::<T>(), move |action, phase, cx| {
4094        let action = action.downcast_ref().unwrap();
4095        if phase == DispatchPhase::Bubble {
4096            view.update(cx, |editor, cx| {
4097                listener(editor, action, cx);
4098            })
4099        }
4100    })
4101}
4102
4103fn compute_auto_height_layout(
4104    editor: &mut Editor,
4105    max_lines: usize,
4106    max_line_number_width: Pixels,
4107    known_dimensions: Size<Option<Pixels>>,
4108    cx: &mut ViewContext<Editor>,
4109) -> Option<Size<Pixels>> {
4110    let width = known_dimensions.width?;
4111    if let Some(height) = known_dimensions.height {
4112        return Some(size(width, height));
4113    }
4114
4115    let style = editor.style.as_ref().unwrap();
4116    let font_id = cx.text_system().resolve_font(&style.text.font());
4117    let font_size = style.text.font_size.to_pixels(cx.rem_size());
4118    let line_height = style.text.line_height_in_pixels(cx.rem_size());
4119    let em_width = cx
4120        .text_system()
4121        .typographic_bounds(font_id, font_size, 'm')
4122        .unwrap()
4123        .size
4124        .width;
4125
4126    let mut snapshot = editor.snapshot(cx);
4127    let gutter_dimensions =
4128        snapshot.gutter_dimensions(font_id, font_size, em_width, max_line_number_width, cx);
4129
4130    editor.gutter_width = gutter_dimensions.width;
4131    let text_width = width - gutter_dimensions.width;
4132    let overscroll = size(em_width, px(0.));
4133
4134    let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width;
4135    if editor.set_wrap_width(Some(editor_width), cx) {
4136        snapshot = editor.snapshot(cx);
4137    }
4138
4139    let scroll_height = Pixels::from(snapshot.max_point().row() + 1) * line_height;
4140    let height = scroll_height
4141        .max(line_height)
4142        .min(line_height * max_lines as f32);
4143
4144    Some(size(width, height))
4145}