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