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