element.rs

   1use super::{
   2    display_map::{BlockContext, ToDisplayPoint},
   3    Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, SelectPhase, SoftWrap, ToPoint,
   4    MAX_LINE_LEN,
   5};
   6use crate::{
   7    display_map::{BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, TransformBlock},
   8    editor_settings::ShowScrollbar,
   9    git::{diff_hunk_to_display, DisplayDiffHunk},
  10    hover_popover::{
  11        hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
  12        MIN_POPOVER_LINE_HEIGHT,
  13    },
  14    link_go_to_definition::{
  15        go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
  16        update_inlay_link_and_hover_points, GoToDefinitionTrigger,
  17    },
  18    mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,
  19};
  20use collections::{BTreeMap, HashMap};
  21use git::diff::DiffHunkStatus;
  22use gpui::{
  23    color::Color,
  24    elements::*,
  25    fonts::TextStyle,
  26    geometry::{
  27        rect::RectF,
  28        vector::{vec2f, Vector2F},
  29        PathBuilder,
  30    },
  31    json::{self, ToJson},
  32    platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
  33    text_layout::{self, Line, RunStyle, TextLayoutCache},
  34    AnyElement, Axis, CursorRegion, Element, EventContext, FontCache, MouseRegion, Quad,
  35    SizeConstraint, ViewContext, WindowContext,
  36};
  37use itertools::Itertools;
  38use json::json;
  39use language::{
  40    language_settings::ShowWhitespaceSetting, Bias, CursorShape, OffsetUtf16, Selection,
  41};
  42use project::{
  43    project_settings::{GitGutterSetting, ProjectSettings},
  44    ProjectPath,
  45};
  46use smallvec::SmallVec;
  47use std::{
  48    borrow::Cow,
  49    cmp::{self, Ordering},
  50    fmt::Write,
  51    iter,
  52    ops::Range,
  53    sync::Arc,
  54};
  55use text::Point;
  56use theme::SelectionStyle;
  57use workspace::item::Item;
  58
  59enum FoldMarkers {}
  60
  61struct SelectionLayout {
  62    head: DisplayPoint,
  63    cursor_shape: CursorShape,
  64    is_newest: bool,
  65    is_local: bool,
  66    range: Range<DisplayPoint>,
  67    active_rows: Range<u32>,
  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    ) -> Self {
  79        let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
  80        let display_selection = point_selection.map(|p| p.to_display_point(map));
  81        let mut range = display_selection.range();
  82        let mut head = display_selection.head();
  83        let mut active_rows = map.prev_line_boundary(point_selection.start).1.row()
  84            ..map.next_line_boundary(point_selection.end).1.row();
  85
  86        // vim visual line mode
  87        if line_mode {
  88            let point_range = map.expand_to_line(point_selection.range());
  89            range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map);
  90        }
  91
  92        // any vim visual mode (including line mode)
  93        if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed {
  94            if head.column() > 0 {
  95                head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left)
  96            } else if head.row() > 0 && head != map.max_point() {
  97                head = map.clip_point(
  98                    DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)),
  99                    Bias::Left,
 100                );
 101                // updating range.end is a no-op unless you're cursor is
 102                // on the newline containing a multi-buffer divider
 103                // in which case the clip_point may have moved the head up
 104                // an additional row.
 105                range.end = DisplayPoint::new(head.row() + 1, 0);
 106                active_rows.end = head.row();
 107            }
 108        }
 109
 110        Self {
 111            head,
 112            cursor_shape,
 113            is_newest,
 114            is_local,
 115            range,
 116            active_rows,
 117        }
 118    }
 119}
 120
 121pub struct EditorElement {
 122    style: Arc<EditorStyle>,
 123}
 124
 125impl EditorElement {
 126    pub fn new(style: EditorStyle) -> Self {
 127        Self {
 128            style: Arc::new(style),
 129        }
 130    }
 131
 132    fn attach_mouse_handlers(
 133        position_map: &Arc<PositionMap>,
 134        has_popovers: bool,
 135        visible_bounds: RectF,
 136        text_bounds: RectF,
 137        gutter_bounds: RectF,
 138        bounds: RectF,
 139        cx: &mut ViewContext<Editor>,
 140    ) {
 141        enum EditorElementMouseHandlers {}
 142        let view_id = cx.view_id();
 143        cx.scene().push_mouse_region(
 144            MouseRegion::new::<EditorElementMouseHandlers>(view_id, view_id, visible_bounds)
 145                .on_down(MouseButton::Left, {
 146                    let position_map = position_map.clone();
 147                    move |event, editor, cx| {
 148                        if !Self::mouse_down(
 149                            editor,
 150                            event.platform_event,
 151                            position_map.as_ref(),
 152                            text_bounds,
 153                            gutter_bounds,
 154                            cx,
 155                        ) {
 156                            cx.propagate_event();
 157                        }
 158                    }
 159                })
 160                .on_down(MouseButton::Right, {
 161                    let position_map = position_map.clone();
 162                    move |event, editor, cx| {
 163                        if !Self::mouse_right_down(
 164                            editor,
 165                            event.position,
 166                            position_map.as_ref(),
 167                            text_bounds,
 168                            cx,
 169                        ) {
 170                            cx.propagate_event();
 171                        }
 172                    }
 173                })
 174                .on_up(MouseButton::Left, {
 175                    let position_map = position_map.clone();
 176                    move |event, editor, cx| {
 177                        if !Self::mouse_up(
 178                            editor,
 179                            event.position,
 180                            event.cmd,
 181                            event.shift,
 182                            event.alt,
 183                            position_map.as_ref(),
 184                            text_bounds,
 185                            cx,
 186                        ) {
 187                            cx.propagate_event()
 188                        }
 189                    }
 190                })
 191                .on_drag(MouseButton::Left, {
 192                    let position_map = position_map.clone();
 193                    move |event, editor, cx| {
 194                        if event.end {
 195                            return;
 196                        }
 197
 198                        if !Self::mouse_dragged(
 199                            editor,
 200                            event.platform_event,
 201                            position_map.as_ref(),
 202                            text_bounds,
 203                            cx,
 204                        ) {
 205                            cx.propagate_event()
 206                        }
 207                    }
 208                })
 209                .on_move({
 210                    let position_map = position_map.clone();
 211                    move |event, editor, cx| {
 212                        if !Self::mouse_moved(
 213                            editor,
 214                            event.platform_event,
 215                            &position_map,
 216                            text_bounds,
 217                            cx,
 218                        ) {
 219                            cx.propagate_event()
 220                        }
 221                    }
 222                })
 223                .on_move_out(move |_, editor: &mut Editor, cx| {
 224                    if has_popovers {
 225                        hide_hover(editor, cx);
 226                    }
 227                })
 228                .on_scroll({
 229                    let position_map = position_map.clone();
 230                    move |event, editor, cx| {
 231                        if !Self::scroll(
 232                            editor,
 233                            event.position,
 234                            *event.delta.raw(),
 235                            event.delta.precise(),
 236                            &position_map,
 237                            bounds,
 238                            cx,
 239                        ) {
 240                            cx.propagate_event()
 241                        }
 242                    }
 243                }),
 244        );
 245
 246        enum GutterHandlers {}
 247        let view_id = cx.view_id();
 248        let region_id = cx.view_id() + 1;
 249        cx.scene().push_mouse_region(
 250            MouseRegion::new::<GutterHandlers>(view_id, region_id, gutter_bounds).on_hover(
 251                |hover, editor: &mut Editor, cx| {
 252                    editor.gutter_hover(
 253                        &GutterHover {
 254                            hovered: hover.started,
 255                        },
 256                        cx,
 257                    );
 258                },
 259            ),
 260        )
 261    }
 262
 263    fn mouse_down(
 264        editor: &mut Editor,
 265        MouseButtonEvent {
 266            position,
 267            modifiers:
 268                Modifiers {
 269                    shift,
 270                    ctrl,
 271                    alt,
 272                    cmd,
 273                    ..
 274                },
 275            mut click_count,
 276            ..
 277        }: MouseButtonEvent,
 278        position_map: &PositionMap,
 279        text_bounds: RectF,
 280        gutter_bounds: RectF,
 281        cx: &mut EventContext<Editor>,
 282    ) -> bool {
 283        if gutter_bounds.contains_point(position) {
 284            click_count = 3; // Simulate triple-click when clicking the gutter to select lines
 285        } else if !text_bounds.contains_point(position) {
 286            return false;
 287        }
 288
 289        let point_for_position = position_map.point_for_position(text_bounds, position);
 290        let position = point_for_position.previous_valid;
 291        if shift && alt {
 292            editor.select(
 293                SelectPhase::BeginColumnar {
 294                    position,
 295                    goal_column: point_for_position.exact_unclipped.column(),
 296                },
 297                cx,
 298            );
 299        } else if shift && !ctrl && !alt && !cmd {
 300            editor.select(
 301                SelectPhase::Extend {
 302                    position,
 303                    click_count,
 304                },
 305                cx,
 306            );
 307        } else {
 308            editor.select(
 309                SelectPhase::Begin {
 310                    position,
 311                    add: alt,
 312                    click_count,
 313                },
 314                cx,
 315            );
 316        }
 317
 318        true
 319    }
 320
 321    fn mouse_right_down(
 322        editor: &mut Editor,
 323        position: Vector2F,
 324        position_map: &PositionMap,
 325        text_bounds: RectF,
 326        cx: &mut EventContext<Editor>,
 327    ) -> bool {
 328        if !text_bounds.contains_point(position) {
 329            return false;
 330        }
 331        let point_for_position = position_map.point_for_position(text_bounds, position);
 332        mouse_context_menu::deploy_context_menu(
 333            editor,
 334            position,
 335            point_for_position.previous_valid,
 336            cx,
 337        );
 338        true
 339    }
 340
 341    fn mouse_up(
 342        editor: &mut Editor,
 343        position: Vector2F,
 344        cmd: bool,
 345        shift: bool,
 346        alt: bool,
 347        position_map: &PositionMap,
 348        text_bounds: RectF,
 349        cx: &mut EventContext<Editor>,
 350    ) -> bool {
 351        let end_selection = editor.has_pending_selection();
 352        let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 353
 354        if end_selection {
 355            editor.select(SelectPhase::End, cx);
 356        }
 357
 358        if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) {
 359            let point = position_map.point_for_position(text_bounds, position);
 360            let could_be_inlay = point.as_valid().is_none();
 361            if shift || could_be_inlay {
 362                go_to_fetched_type_definition(editor, point, alt, cx);
 363            } else {
 364                go_to_fetched_definition(editor, point, alt, cx);
 365            }
 366
 367            return true;
 368        }
 369
 370        end_selection
 371    }
 372
 373    fn mouse_dragged(
 374        editor: &mut Editor,
 375        MouseMovedEvent {
 376            modifiers: Modifiers { cmd, shift, .. },
 377            position,
 378            ..
 379        }: MouseMovedEvent,
 380        position_map: &PositionMap,
 381        text_bounds: RectF,
 382        cx: &mut EventContext<Editor>,
 383    ) -> bool {
 384        // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
 385        // Don't trigger hover popover if mouse is hovering over context menu
 386        let point = if text_bounds.contains_point(position) {
 387            position_map
 388                .point_for_position(text_bounds, position)
 389                .as_valid()
 390        } else {
 391            None
 392        };
 393
 394        update_go_to_definition_link(
 395            editor,
 396            point.map(GoToDefinitionTrigger::Text),
 397            cmd,
 398            shift,
 399            cx,
 400        );
 401
 402        if editor.has_pending_selection() {
 403            let mut scroll_delta = Vector2F::zero();
 404
 405            let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0);
 406            let top = text_bounds.origin_y() + vertical_margin;
 407            let bottom = text_bounds.lower_left().y() - vertical_margin;
 408            if position.y() < top {
 409                scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y()))
 410            }
 411            if position.y() > bottom {
 412                scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom))
 413            }
 414
 415            let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0);
 416            let left = text_bounds.origin_x() + horizontal_margin;
 417            let right = text_bounds.upper_right().x() - horizontal_margin;
 418            if position.x() < left {
 419                scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta(
 420                    left - position.x(),
 421                ))
 422            }
 423            if position.x() > right {
 424                scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta(
 425                    position.x() - right,
 426                ))
 427            }
 428
 429            let point_for_position = position_map.point_for_position(text_bounds, position);
 430
 431            editor.select(
 432                SelectPhase::Update {
 433                    position: point_for_position.previous_valid,
 434                    goal_column: point_for_position.exact_unclipped.column(),
 435                    scroll_position: (position_map.snapshot.scroll_position() + scroll_delta)
 436                        .clamp(Vector2F::zero(), position_map.scroll_max),
 437                },
 438                cx,
 439            );
 440            hover_at(editor, point, cx);
 441            true
 442        } else {
 443            hover_at(editor, point, cx);
 444            false
 445        }
 446    }
 447
 448    fn mouse_moved(
 449        editor: &mut Editor,
 450        MouseMovedEvent {
 451            modifiers: Modifiers { shift, cmd, .. },
 452            position,
 453            ..
 454        }: MouseMovedEvent,
 455        position_map: &PositionMap,
 456        text_bounds: RectF,
 457        cx: &mut ViewContext<Editor>,
 458    ) -> bool {
 459        // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
 460        // Don't trigger hover popover if mouse is hovering over context menu
 461        if text_bounds.contains_point(position) {
 462            let point_for_position = position_map.point_for_position(text_bounds, position);
 463            match point_for_position.as_valid() {
 464                Some(point) => {
 465                    update_go_to_definition_link(
 466                        editor,
 467                        Some(GoToDefinitionTrigger::Text(point)),
 468                        cmd,
 469                        shift,
 470                        cx,
 471                    );
 472                    hover_at(editor, Some(point), cx);
 473                }
 474                None => {
 475                    update_inlay_link_and_hover_points(
 476                        &position_map.snapshot,
 477                        point_for_position,
 478                        editor,
 479                        cmd,
 480                        shift,
 481                        cx,
 482                    );
 483                }
 484            }
 485        } else {
 486            update_go_to_definition_link(editor, None, cmd, shift, cx);
 487            hover_at(editor, None, cx);
 488        }
 489
 490        true
 491    }
 492
 493    fn scroll(
 494        editor: &mut Editor,
 495        position: Vector2F,
 496        mut delta: Vector2F,
 497        precise: bool,
 498        position_map: &PositionMap,
 499        bounds: RectF,
 500        cx: &mut ViewContext<Editor>,
 501    ) -> bool {
 502        if !bounds.contains_point(position) {
 503            return false;
 504        }
 505
 506        let line_height = position_map.line_height;
 507        let max_glyph_width = position_map.em_width;
 508
 509        let axis = if precise {
 510            //Trackpad
 511            position_map.snapshot.ongoing_scroll.filter(&mut delta)
 512        } else {
 513            //Not trackpad
 514            delta *= vec2f(max_glyph_width, line_height);
 515            None //Resets ongoing scroll
 516        };
 517
 518        let scroll_position = position_map.snapshot.scroll_position();
 519        let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width;
 520        let y = (scroll_position.y() * line_height - delta.y()) / line_height;
 521        let scroll_position = vec2f(x, y).clamp(Vector2F::zero(), position_map.scroll_max);
 522        editor.scroll(scroll_position, axis, cx);
 523
 524        true
 525    }
 526
 527    fn paint_background(
 528        &self,
 529        gutter_bounds: RectF,
 530        text_bounds: RectF,
 531        layout: &LayoutState,
 532        cx: &mut ViewContext<Editor>,
 533    ) {
 534        let bounds = gutter_bounds.union_rect(text_bounds);
 535        let scroll_top =
 536            layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height;
 537        cx.scene().push_quad(Quad {
 538            bounds: gutter_bounds,
 539            background: Some(self.style.gutter_background),
 540            border: Border::new(0., Color::transparent_black()).into(),
 541            corner_radii: Default::default(),
 542        });
 543        cx.scene().push_quad(Quad {
 544            bounds: text_bounds,
 545            background: Some(self.style.background),
 546            border: Border::new(0., Color::transparent_black()).into(),
 547            corner_radii: Default::default(),
 548        });
 549
 550        if let EditorMode::Full = layout.mode {
 551            let mut active_rows = layout.active_rows.iter().peekable();
 552            while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
 553                let mut end_row = *start_row;
 554                while active_rows.peek().map_or(false, |r| {
 555                    *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
 556                }) {
 557                    active_rows.next().unwrap();
 558                    end_row += 1;
 559                }
 560
 561                if !contains_non_empty_selection {
 562                    let origin = vec2f(
 563                        bounds.origin_x(),
 564                        bounds.origin_y() + (layout.position_map.line_height * *start_row as f32)
 565                            - scroll_top,
 566                    );
 567                    let size = vec2f(
 568                        bounds.width(),
 569                        layout.position_map.line_height * (end_row - start_row + 1) as f32,
 570                    );
 571                    cx.scene().push_quad(Quad {
 572                        bounds: RectF::new(origin, size),
 573                        background: Some(self.style.active_line_background),
 574                        border: Border::default().into(),
 575                        corner_radii: Default::default(),
 576                    });
 577                }
 578            }
 579
 580            if let Some(highlighted_rows) = &layout.highlighted_rows {
 581                let origin = vec2f(
 582                    bounds.origin_x(),
 583                    bounds.origin_y()
 584                        + (layout.position_map.line_height * highlighted_rows.start as f32)
 585                        - scroll_top,
 586                );
 587                let size = vec2f(
 588                    bounds.width(),
 589                    layout.position_map.line_height * highlighted_rows.len() as f32,
 590                );
 591                cx.scene().push_quad(Quad {
 592                    bounds: RectF::new(origin, size),
 593                    background: Some(self.style.highlighted_line_background),
 594                    border: Border::default().into(),
 595                    corner_radii: Default::default(),
 596                });
 597            }
 598
 599            let scroll_left =
 600                layout.position_map.snapshot.scroll_position().x() * layout.position_map.em_width;
 601
 602            for (wrap_position, active) in layout.wrap_guides.iter() {
 603                let x =
 604                    (text_bounds.origin_x() + wrap_position + layout.position_map.em_width / 2.)
 605                        - scroll_left;
 606
 607                if x < text_bounds.origin_x()
 608                    || (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
 609                {
 610                    continue;
 611                }
 612
 613                let color = if *active {
 614                    self.style.active_wrap_guide
 615                } else {
 616                    self.style.wrap_guide
 617                };
 618                cx.scene().push_quad(Quad {
 619                    bounds: RectF::new(
 620                        vec2f(x, text_bounds.origin_y()),
 621                        vec2f(1., text_bounds.height()),
 622                    ),
 623                    background: Some(color),
 624                    border: Border::new(0., Color::transparent_black()).into(),
 625                    corner_radii: Default::default(),
 626                });
 627            }
 628        }
 629    }
 630
 631    fn paint_gutter(
 632        &mut self,
 633        bounds: RectF,
 634        visible_bounds: RectF,
 635        layout: &mut LayoutState,
 636        editor: &mut Editor,
 637        cx: &mut ViewContext<Editor>,
 638    ) {
 639        let line_height = layout.position_map.line_height;
 640
 641        let scroll_position = layout.position_map.snapshot.scroll_position();
 642        let scroll_top = scroll_position.y() * line_height;
 643
 644        let show_gutter = matches!(
 645            settings::get::<ProjectSettings>(cx).git.git_gutter,
 646            Some(GitGutterSetting::TrackedFiles)
 647        );
 648
 649        if show_gutter {
 650            Self::paint_diff_hunks(bounds, layout, cx);
 651        }
 652
 653        for (ix, line) in layout.line_number_layouts.iter().enumerate() {
 654            if let Some(line) = line {
 655                let line_origin = bounds.origin()
 656                    + vec2f(
 657                        bounds.width() - line.width() - layout.gutter_padding,
 658                        ix as f32 * line_height - (scroll_top % line_height),
 659                    );
 660
 661                line.paint(line_origin, visible_bounds, line_height, cx);
 662            }
 663        }
 664
 665        for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() {
 666            if let Some(indicator) = fold_indicator.as_mut() {
 667                let position = vec2f(
 668                    bounds.width() - layout.gutter_padding,
 669                    ix as f32 * line_height - (scroll_top % line_height),
 670                );
 671                let centering_offset = vec2f(
 672                    (layout.gutter_padding + layout.gutter_margin - indicator.size().x()) / 2.,
 673                    (line_height - indicator.size().y()) / 2.,
 674                );
 675
 676                let indicator_origin = bounds.origin() + position + centering_offset;
 677
 678                indicator.paint(indicator_origin, visible_bounds, editor, cx);
 679            }
 680        }
 681
 682        if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() {
 683            let mut x = 0.;
 684            let mut y = *row as f32 * line_height - scroll_top;
 685            x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.;
 686            y += (line_height - indicator.size().y()) / 2.;
 687            indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, editor, cx);
 688        }
 689    }
 690
 691    fn paint_diff_hunks(bounds: RectF, layout: &mut LayoutState, cx: &mut ViewContext<Editor>) {
 692        let diff_style = &theme::current(cx).editor.diff.clone();
 693        let line_height = layout.position_map.line_height;
 694
 695        let scroll_position = layout.position_map.snapshot.scroll_position();
 696        let scroll_top = scroll_position.y() * line_height;
 697
 698        for hunk in &layout.display_hunks {
 699            let (display_row_range, status) = match hunk {
 700                //TODO: This rendering is entirely a horrible hack
 701                &DisplayDiffHunk::Folded { display_row: row } => {
 702                    let start_y = row as f32 * line_height - scroll_top;
 703                    let end_y = start_y + line_height;
 704
 705                    let width = diff_style.removed_width_em * line_height;
 706                    let highlight_origin = bounds.origin() + vec2f(-width, start_y);
 707                    let highlight_size = vec2f(width * 2., end_y - start_y);
 708                    let highlight_bounds = RectF::new(highlight_origin, highlight_size);
 709
 710                    cx.scene().push_quad(Quad {
 711                        bounds: highlight_bounds,
 712                        background: Some(diff_style.modified),
 713                        border: Border::new(0., Color::transparent_black()).into(),
 714                        corner_radii: (1. * line_height).into(),
 715                    });
 716
 717                    continue;
 718                }
 719
 720                DisplayDiffHunk::Unfolded {
 721                    display_row_range,
 722                    status,
 723                } => (display_row_range, status),
 724            };
 725
 726            let color = match status {
 727                DiffHunkStatus::Added => diff_style.inserted,
 728                DiffHunkStatus::Modified => diff_style.modified,
 729
 730                //TODO: This rendering is entirely a horrible hack
 731                DiffHunkStatus::Removed => {
 732                    let row = display_row_range.start;
 733
 734                    let offset = line_height / 2.;
 735                    let start_y = row as f32 * line_height - offset - scroll_top;
 736                    let end_y = start_y + line_height;
 737
 738                    let width = diff_style.removed_width_em * line_height;
 739                    let highlight_origin = bounds.origin() + vec2f(-width, start_y);
 740                    let highlight_size = vec2f(width * 2., end_y - start_y);
 741                    let highlight_bounds = RectF::new(highlight_origin, highlight_size);
 742
 743                    cx.scene().push_quad(Quad {
 744                        bounds: highlight_bounds,
 745                        background: Some(diff_style.deleted),
 746                        border: Border::new(0., Color::transparent_black()).into(),
 747                        corner_radii: (1. * line_height).into(),
 748                    });
 749
 750                    continue;
 751                }
 752            };
 753
 754            let start_row = display_row_range.start;
 755            let end_row = display_row_range.end;
 756
 757            let start_y = start_row as f32 * line_height - scroll_top;
 758            let end_y = end_row as f32 * line_height - scroll_top;
 759
 760            let width = diff_style.width_em * line_height;
 761            let highlight_origin = bounds.origin() + vec2f(-width, start_y);
 762            let highlight_size = vec2f(width * 2., end_y - start_y);
 763            let highlight_bounds = RectF::new(highlight_origin, highlight_size);
 764
 765            cx.scene().push_quad(Quad {
 766                bounds: highlight_bounds,
 767                background: Some(color),
 768                border: Border::new(0., Color::transparent_black()).into(),
 769                corner_radii: (diff_style.corner_radius * line_height).into(),
 770            });
 771        }
 772    }
 773
 774    fn paint_text(
 775        &mut self,
 776        bounds: RectF,
 777        visible_bounds: RectF,
 778        layout: &mut LayoutState,
 779        editor: &mut Editor,
 780        cx: &mut ViewContext<Editor>,
 781    ) {
 782        let style = &self.style;
 783        let scroll_position = layout.position_map.snapshot.scroll_position();
 784        let start_row = layout.visible_display_row_range.start;
 785        let scroll_top = scroll_position.y() * layout.position_map.line_height;
 786        let max_glyph_width = layout.position_map.em_width;
 787        let scroll_left = scroll_position.x() * max_glyph_width;
 788        let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.);
 789        let line_end_overshoot = 0.15 * layout.position_map.line_height;
 790        let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
 791
 792        cx.scene().push_layer(Some(bounds));
 793
 794        cx.scene().push_cursor_region(CursorRegion {
 795            bounds,
 796            style: if !editor.link_go_to_definition_state.definitions.is_empty() {
 797                CursorStyle::PointingHand
 798            } else {
 799                CursorStyle::IBeam
 800            },
 801        });
 802
 803        let fold_corner_radius =
 804            self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height;
 805        for (id, range, color) in layout.fold_ranges.iter() {
 806            self.paint_highlighted_range(
 807                range.clone(),
 808                *color,
 809                fold_corner_radius,
 810                fold_corner_radius * 2.,
 811                layout,
 812                content_origin,
 813                scroll_top,
 814                scroll_left,
 815                bounds,
 816                cx,
 817            );
 818
 819            for bound in range_to_bounds(
 820                &range,
 821                content_origin,
 822                scroll_left,
 823                scroll_top,
 824                &layout.visible_display_row_range,
 825                line_end_overshoot,
 826                &layout.position_map,
 827            ) {
 828                cx.scene().push_cursor_region(CursorRegion {
 829                    bounds: bound,
 830                    style: CursorStyle::PointingHand,
 831                });
 832
 833                let display_row = range.start.row();
 834
 835                let buffer_row = DisplayPoint::new(display_row, 0)
 836                    .to_point(&layout.position_map.snapshot.display_snapshot)
 837                    .row;
 838
 839                let view_id = cx.view_id();
 840                cx.scene().push_mouse_region(
 841                    MouseRegion::new::<FoldMarkers>(view_id, *id as usize, bound)
 842                        .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| {
 843                            editor.unfold_at(&UnfoldAt { buffer_row }, cx)
 844                        })
 845                        .with_notify_on_hover(true)
 846                        .with_notify_on_click(true),
 847                )
 848            }
 849        }
 850
 851        for (range, color) in &layout.highlighted_ranges {
 852            self.paint_highlighted_range(
 853                range.clone(),
 854                *color,
 855                0.,
 856                line_end_overshoot,
 857                layout,
 858                content_origin,
 859                scroll_top,
 860                scroll_left,
 861                bounds,
 862                cx,
 863            );
 864        }
 865
 866        let mut cursors = SmallVec::<[Cursor; 32]>::new();
 867        let corner_radius = 0.15 * layout.position_map.line_height;
 868        let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
 869
 870        for (selection_style, selections) in &layout.selections {
 871            for selection in selections {
 872                self.paint_highlighted_range(
 873                    selection.range.clone(),
 874                    selection_style.selection,
 875                    corner_radius,
 876                    corner_radius * 2.,
 877                    layout,
 878                    content_origin,
 879                    scroll_top,
 880                    scroll_left,
 881                    bounds,
 882                    cx,
 883                );
 884
 885                if selection.is_local && !selection.range.is_empty() {
 886                    invisible_display_ranges.push(selection.range.clone());
 887                }
 888                if !selection.is_local || editor.show_local_cursors(cx) {
 889                    let cursor_position = selection.head;
 890                    if layout
 891                        .visible_display_row_range
 892                        .contains(&cursor_position.row())
 893                    {
 894                        let cursor_row_layout = &layout.position_map.line_layouts
 895                            [(cursor_position.row() - start_row) as usize]
 896                            .line;
 897                        let cursor_column = cursor_position.column() as usize;
 898
 899                        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 900                        let mut block_width =
 901                            cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x;
 902                        if block_width == 0.0 {
 903                            block_width = layout.position_map.em_width;
 904                        }
 905                        let block_text = if let CursorShape::Block = selection.cursor_shape {
 906                            layout
 907                                .position_map
 908                                .snapshot
 909                                .chars_at(cursor_position)
 910                                .next()
 911                                .and_then(|(character, _)| {
 912                                    let font_id =
 913                                        cursor_row_layout.font_for_index(cursor_column)?;
 914                                    let text = character.to_string();
 915
 916                                    Some(cx.text_layout_cache().layout_str(
 917                                        &text,
 918                                        cursor_row_layout.font_size(),
 919                                        &[(
 920                                            text.chars().count(),
 921                                            RunStyle {
 922                                                font_id,
 923                                                color: style.background,
 924                                                underline: Default::default(),
 925                                            },
 926                                        )],
 927                                    ))
 928                                })
 929                        } else {
 930                            None
 931                        };
 932
 933                        let x = cursor_character_x - scroll_left;
 934                        let y = cursor_position.row() as f32 * layout.position_map.line_height
 935                            - scroll_top;
 936                        if selection.is_newest {
 937                            editor.pixel_position_of_newest_cursor = Some(vec2f(
 938                                bounds.origin_x() + x + block_width / 2.,
 939                                bounds.origin_y() + y + layout.position_map.line_height / 2.,
 940                            ));
 941                        }
 942                        cursors.push(Cursor {
 943                            color: selection_style.cursor,
 944                            block_width,
 945                            origin: vec2f(x, y),
 946                            line_height: layout.position_map.line_height,
 947                            shape: selection.cursor_shape,
 948                            block_text,
 949                        });
 950                    }
 951                }
 952            }
 953        }
 954
 955        if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) {
 956            for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
 957                let row = start_row + ix as u32;
 958                line_with_invisibles.draw(
 959                    layout,
 960                    row,
 961                    scroll_top,
 962                    content_origin,
 963                    scroll_left,
 964                    visible_text_bounds,
 965                    whitespace_setting,
 966                    &invisible_display_ranges,
 967                    visible_bounds,
 968                    cx,
 969                )
 970            }
 971        }
 972
 973        cx.scene().push_layer(Some(bounds));
 974        for cursor in cursors {
 975            cursor.paint(content_origin, cx);
 976        }
 977        cx.scene().pop_layer();
 978
 979        if let Some((position, context_menu)) = layout.context_menu.as_mut() {
 980            cx.scene().push_stacking_context(None, None);
 981            let cursor_row_layout =
 982                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
 983            let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
 984            let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top;
 985            let mut list_origin = content_origin + vec2f(x, y);
 986            let list_width = context_menu.size().x();
 987            let list_height = context_menu.size().y();
 988
 989            // Snap the right edge of the list to the right edge of the window if
 990            // its horizontal bounds overflow.
 991            if list_origin.x() + list_width > cx.window_size().x() {
 992                list_origin.set_x((cx.window_size().x() - list_width).max(0.));
 993            }
 994
 995            if list_origin.y() + list_height > bounds.max_y() {
 996                list_origin.set_y(list_origin.y() - layout.position_map.line_height - list_height);
 997            }
 998
 999            context_menu.paint(
1000                list_origin,
1001                RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
1002                editor,
1003                cx,
1004            );
1005
1006            cx.scene().pop_stacking_context();
1007        }
1008
1009        if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
1010            cx.scene().push_stacking_context(None, None);
1011
1012            // This is safe because we check on layout whether the required row is available
1013            let hovered_row_layout =
1014                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
1015
1016            // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
1017            // height. This is the size we will use to decide whether to render popovers above or below
1018            // the hovered line.
1019            let first_size = hover_popovers[0].size();
1020            let height_to_reserve = first_size.y()
1021                + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height;
1022
1023            // Compute Hovered Point
1024            let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left;
1025            let y = position.row() as f32 * layout.position_map.line_height - scroll_top;
1026            let hovered_point = content_origin + vec2f(x, y);
1027
1028            if hovered_point.y() - height_to_reserve > 0.0 {
1029                // There is enough space above. Render popovers above the hovered point
1030                let mut current_y = hovered_point.y();
1031                for hover_popover in hover_popovers {
1032                    let size = hover_popover.size();
1033                    let mut popover_origin = vec2f(hovered_point.x(), current_y - size.y());
1034
1035                    let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x());
1036                    if x_out_of_bounds < 0.0 {
1037                        popover_origin.set_x(popover_origin.x() + x_out_of_bounds);
1038                    }
1039
1040                    hover_popover.paint(
1041                        popover_origin,
1042                        RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
1043                        editor,
1044                        cx,
1045                    );
1046
1047                    current_y = popover_origin.y() - HOVER_POPOVER_GAP;
1048                }
1049            } else {
1050                // There is not enough space above. Render popovers below the hovered point
1051                let mut current_y = hovered_point.y() + layout.position_map.line_height;
1052                for hover_popover in hover_popovers {
1053                    let size = hover_popover.size();
1054                    let mut popover_origin = vec2f(hovered_point.x(), current_y);
1055
1056                    let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x());
1057                    if x_out_of_bounds < 0.0 {
1058                        popover_origin.set_x(popover_origin.x() + x_out_of_bounds);
1059                    }
1060
1061                    hover_popover.paint(
1062                        popover_origin,
1063                        RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
1064                        editor,
1065                        cx,
1066                    );
1067
1068                    current_y = popover_origin.y() + size.y() + HOVER_POPOVER_GAP;
1069                }
1070            }
1071
1072            cx.scene().pop_stacking_context();
1073        }
1074
1075        cx.scene().pop_layer();
1076    }
1077
1078    fn scrollbar_left(&self, bounds: &RectF) -> f32 {
1079        bounds.max_x() - self.style.theme.scrollbar.width
1080    }
1081
1082    fn paint_scrollbar(
1083        &mut self,
1084        bounds: RectF,
1085        layout: &mut LayoutState,
1086        editor: &Editor,
1087        cx: &mut ViewContext<Editor>,
1088    ) {
1089        enum ScrollbarMouseHandlers {}
1090        if layout.mode != EditorMode::Full {
1091            return;
1092        }
1093
1094        let style = &self.style.theme.scrollbar;
1095
1096        let top = bounds.min_y();
1097        let bottom = bounds.max_y();
1098        let right = bounds.max_x();
1099        let left = self.scrollbar_left(&bounds);
1100        let row_range = &layout.scrollbar_row_range;
1101        let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
1102
1103        let mut height = bounds.height();
1104        let mut first_row_y_offset = 0.0;
1105
1106        // Impose a minimum height on the scrollbar thumb
1107        let row_height = height / max_row;
1108        let min_thumb_height =
1109            style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size);
1110        let thumb_height = (row_range.end - row_range.start) * row_height;
1111        if thumb_height < min_thumb_height {
1112            first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
1113            height -= min_thumb_height - thumb_height;
1114        }
1115
1116        let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * row_height };
1117
1118        let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
1119        let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
1120        let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom));
1121        let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom));
1122
1123        if layout.show_scrollbars {
1124            cx.scene().push_quad(Quad {
1125                bounds: track_bounds,
1126                border: style.track.border.into(),
1127                background: style.track.background_color,
1128                ..Default::default()
1129            });
1130            let scrollbar_settings = settings::get::<EditorSettings>(cx).scrollbar;
1131            let theme = theme::current(cx);
1132            let scrollbar_theme = &theme.editor.scrollbar;
1133            if layout.is_singleton && scrollbar_settings.selections {
1134                let start_anchor = Anchor::min();
1135                let end_anchor = Anchor::max();
1136                let color = scrollbar_theme.selections;
1137                let border = Border {
1138                    width: 1.,
1139                    color: style.thumb.border.color,
1140                    overlay: false,
1141                    top: false,
1142                    right: true,
1143                    bottom: false,
1144                    left: true,
1145                };
1146                let mut push_region = |start: DisplayPoint, end: DisplayPoint| {
1147                    let start_y = y_for_row(start.row() as f32);
1148                    let mut end_y = y_for_row(end.row() as f32);
1149                    if end_y - start_y < 1. {
1150                        end_y = start_y + 1.;
1151                    }
1152                    let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
1153
1154                    cx.scene().push_quad(Quad {
1155                        bounds,
1156                        background: Some(color),
1157                        border: border.into(),
1158                        corner_radii: style.thumb.corner_radii.into(),
1159                    })
1160                };
1161                let background_ranges = editor
1162                    .background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
1163                        start_anchor..end_anchor,
1164                        &layout.position_map.snapshot,
1165                        50000,
1166                    );
1167                for row in background_ranges {
1168                    let start = row.start();
1169                    let end = row.end();
1170                    push_region(*start, *end);
1171                }
1172            }
1173
1174            if layout.is_singleton && scrollbar_settings.git_diff {
1175                let diff_style = scrollbar_theme.git.clone();
1176                for hunk in layout
1177                    .position_map
1178                    .snapshot
1179                    .buffer_snapshot
1180                    .git_diff_hunks_in_range(0..(max_row.floor() as u32))
1181                {
1182                    let start_display = Point::new(hunk.buffer_range.start, 0)
1183                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1184                    let end_display = Point::new(hunk.buffer_range.end, 0)
1185                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
1186                    let start_y = y_for_row(start_display.row() as f32);
1187                    let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
1188                        y_for_row((end_display.row() + 1) as f32)
1189                    } else {
1190                        y_for_row((end_display.row()) as f32)
1191                    };
1192
1193                    if end_y - start_y < 1. {
1194                        end_y = start_y + 1.;
1195                    }
1196                    let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
1197
1198                    let color = match hunk.status() {
1199                        DiffHunkStatus::Added => diff_style.inserted,
1200                        DiffHunkStatus::Modified => diff_style.modified,
1201                        DiffHunkStatus::Removed => diff_style.deleted,
1202                    };
1203
1204                    let border = Border {
1205                        width: 1.,
1206                        color: style.thumb.border.color,
1207                        overlay: false,
1208                        top: false,
1209                        right: true,
1210                        bottom: false,
1211                        left: true,
1212                    };
1213
1214                    cx.scene().push_quad(Quad {
1215                        bounds,
1216                        background: Some(color),
1217                        border: border.into(),
1218                        corner_radii: style.thumb.corner_radii.into(),
1219                    })
1220                }
1221            }
1222
1223            cx.scene().push_quad(Quad {
1224                bounds: thumb_bounds,
1225                border: style.thumb.border.into(),
1226                background: style.thumb.background_color,
1227                corner_radii: style.thumb.corner_radii.into(),
1228            });
1229        }
1230
1231        cx.scene().push_cursor_region(CursorRegion {
1232            bounds: track_bounds,
1233            style: CursorStyle::Arrow,
1234        });
1235        let region_id = cx.view_id();
1236        cx.scene().push_mouse_region(
1237            MouseRegion::new::<ScrollbarMouseHandlers>(region_id, region_id, track_bounds)
1238                .on_move(move |event, editor: &mut Editor, cx| {
1239                    if event.pressed_button.is_none() {
1240                        editor.scroll_manager.show_scrollbar(cx);
1241                    }
1242                })
1243                .on_down(MouseButton::Left, {
1244                    let row_range = row_range.clone();
1245                    move |event, editor: &mut Editor, cx| {
1246                        let y = event.position.y();
1247                        if y < thumb_top || thumb_bottom < y {
1248                            let center_row = ((y - top) * max_row as f32 / height).round() as u32;
1249                            let top_row = center_row
1250                                .saturating_sub((row_range.end - row_range.start) as u32 / 2);
1251                            let mut position = editor.scroll_position(cx);
1252                            position.set_y(top_row as f32);
1253                            editor.set_scroll_position(position, cx);
1254                        } else {
1255                            editor.scroll_manager.show_scrollbar(cx);
1256                        }
1257                    }
1258                })
1259                .on_drag(MouseButton::Left, {
1260                    move |event, editor: &mut Editor, cx| {
1261                        if event.end {
1262                            return;
1263                        }
1264
1265                        let y = event.prev_mouse_position.y();
1266                        let new_y = event.position.y();
1267                        if thumb_top < y && y < thumb_bottom {
1268                            let mut position = editor.scroll_position(cx);
1269                            position.set_y(position.y() + (new_y - y) * (max_row as f32) / height);
1270                            if position.y() < 0.0 {
1271                                position.set_y(0.);
1272                            }
1273                            editor.set_scroll_position(position, cx);
1274                        }
1275                    }
1276                }),
1277        );
1278    }
1279
1280    #[allow(clippy::too_many_arguments)]
1281    fn paint_highlighted_range(
1282        &self,
1283        range: Range<DisplayPoint>,
1284        color: Color,
1285        corner_radius: f32,
1286        line_end_overshoot: f32,
1287        layout: &LayoutState,
1288        content_origin: Vector2F,
1289        scroll_top: f32,
1290        scroll_left: f32,
1291        bounds: RectF,
1292        cx: &mut ViewContext<Editor>,
1293    ) {
1294        let start_row = layout.visible_display_row_range.start;
1295        let end_row = layout.visible_display_row_range.end;
1296        if range.start != range.end {
1297            let row_range = if range.end.column() == 0 {
1298                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
1299            } else {
1300                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
1301            };
1302
1303            let highlighted_range = HighlightedRange {
1304                color,
1305                line_height: layout.position_map.line_height,
1306                corner_radius,
1307                start_y: content_origin.y()
1308                    + row_range.start as f32 * layout.position_map.line_height
1309                    - scroll_top,
1310                lines: row_range
1311                    .into_iter()
1312                    .map(|row| {
1313                        let line_layout =
1314                            &layout.position_map.line_layouts[(row - start_row) as usize].line;
1315                        HighlightedRangeLine {
1316                            start_x: if row == range.start.row() {
1317                                content_origin.x()
1318                                    + line_layout.x_for_index(range.start.column() as usize)
1319                                    - scroll_left
1320                            } else {
1321                                content_origin.x() - scroll_left
1322                            },
1323                            end_x: if row == range.end.row() {
1324                                content_origin.x()
1325                                    + line_layout.x_for_index(range.end.column() as usize)
1326                                    - scroll_left
1327                            } else {
1328                                content_origin.x() + line_layout.width() + line_end_overshoot
1329                                    - scroll_left
1330                            },
1331                        }
1332                    })
1333                    .collect(),
1334            };
1335
1336            highlighted_range.paint(bounds, cx);
1337        }
1338    }
1339
1340    fn paint_blocks(
1341        &mut self,
1342        bounds: RectF,
1343        visible_bounds: RectF,
1344        layout: &mut LayoutState,
1345        editor: &mut Editor,
1346        cx: &mut ViewContext<Editor>,
1347    ) {
1348        let scroll_position = layout.position_map.snapshot.scroll_position();
1349        let scroll_left = scroll_position.x() * layout.position_map.em_width;
1350        let scroll_top = scroll_position.y() * layout.position_map.line_height;
1351
1352        for block in &mut layout.blocks {
1353            let mut origin = bounds.origin()
1354                + vec2f(
1355                    0.,
1356                    block.row as f32 * layout.position_map.line_height - scroll_top,
1357                );
1358            if !matches!(block.style, BlockStyle::Sticky) {
1359                origin += vec2f(-scroll_left, 0.);
1360            }
1361            block.element.paint(origin, visible_bounds, editor, cx);
1362        }
1363    }
1364
1365    fn column_pixels(&self, column: usize, cx: &ViewContext<Editor>) -> f32 {
1366        let style = &self.style;
1367
1368        cx.text_layout_cache()
1369            .layout_str(
1370                " ".repeat(column).as_str(),
1371                style.text.font_size,
1372                &[(
1373                    column,
1374                    RunStyle {
1375                        font_id: style.text.font_id,
1376                        color: Color::black(),
1377                        underline: Default::default(),
1378                    },
1379                )],
1380            )
1381            .width()
1382    }
1383
1384    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
1385        let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
1386        self.column_pixels(digit_count, cx)
1387    }
1388
1389    //Folds contained in a hunk are ignored apart from shrinking visual size
1390    //If a fold contains any hunks then that fold line is marked as modified
1391    fn layout_git_gutters(
1392        &self,
1393        display_rows: Range<u32>,
1394        snapshot: &EditorSnapshot,
1395    ) -> Vec<DisplayDiffHunk> {
1396        let buffer_snapshot = &snapshot.buffer_snapshot;
1397
1398        let buffer_start_row = DisplayPoint::new(display_rows.start, 0)
1399            .to_point(snapshot)
1400            .row;
1401        let buffer_end_row = DisplayPoint::new(display_rows.end, 0)
1402            .to_point(snapshot)
1403            .row;
1404
1405        buffer_snapshot
1406            .git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
1407            .map(|hunk| diff_hunk_to_display(hunk, snapshot))
1408            .dedup()
1409            .collect()
1410    }
1411
1412    fn calculate_relative_line_numbers(
1413        &self,
1414        snapshot: &EditorSnapshot,
1415        rows: &Range<u32>,
1416        relative_to: Option<u32>,
1417    ) -> HashMap<u32, u32> {
1418        let mut relative_rows: HashMap<u32, u32> = Default::default();
1419        let Some(relative_to) = relative_to else {
1420            return relative_rows;
1421        };
1422
1423        let start = rows.start.min(relative_to);
1424        let end = rows.end.max(relative_to);
1425
1426        let buffer_rows = snapshot
1427            .buffer_rows(start)
1428            .take(1 + (end - start) as usize)
1429            .collect::<Vec<_>>();
1430
1431        let head_idx = relative_to - start;
1432        let mut delta = 1;
1433        let mut i = head_idx + 1;
1434        while i < buffer_rows.len() as u32 {
1435            if buffer_rows[i as usize].is_some() {
1436                if rows.contains(&(i + start)) {
1437                    relative_rows.insert(i + start, delta);
1438                }
1439                delta += 1;
1440            }
1441            i += 1;
1442        }
1443        delta = 1;
1444        i = head_idx.min(buffer_rows.len() as u32 - 1);
1445        while i > 0 && buffer_rows[i as usize].is_none() {
1446            i -= 1;
1447        }
1448
1449        while i > 0 {
1450            i -= 1;
1451            if buffer_rows[i as usize].is_some() {
1452                if rows.contains(&(i + start)) {
1453                    relative_rows.insert(i + start, delta);
1454                }
1455                delta += 1;
1456            }
1457        }
1458
1459        relative_rows
1460    }
1461
1462    fn layout_line_numbers(
1463        &self,
1464        rows: Range<u32>,
1465        active_rows: &BTreeMap<u32, bool>,
1466        newest_selection_head: DisplayPoint,
1467        is_singleton: bool,
1468        snapshot: &EditorSnapshot,
1469        cx: &ViewContext<Editor>,
1470    ) -> (
1471        Vec<Option<text_layout::Line>>,
1472        Vec<Option<(FoldStatus, BufferRow, bool)>>,
1473    ) {
1474        let style = &self.style;
1475        let include_line_numbers = snapshot.mode == EditorMode::Full;
1476        let mut line_number_layouts = Vec::with_capacity(rows.len());
1477        let mut fold_statuses = Vec::with_capacity(rows.len());
1478        let mut line_number = String::new();
1479        let is_relative = settings::get::<EditorSettings>(cx).relative_line_numbers;
1480        let relative_to = if is_relative {
1481            Some(newest_selection_head.row())
1482        } else {
1483            None
1484        };
1485
1486        let relative_rows = self.calculate_relative_line_numbers(&snapshot, &rows, relative_to);
1487
1488        for (ix, row) in snapshot
1489            .buffer_rows(rows.start)
1490            .take((rows.end - rows.start) as usize)
1491            .enumerate()
1492        {
1493            let display_row = rows.start + ix as u32;
1494            let (active, color) = if active_rows.contains_key(&display_row) {
1495                (true, style.line_number_active)
1496            } else {
1497                (false, style.line_number)
1498            };
1499            if let Some(buffer_row) = row {
1500                if include_line_numbers {
1501                    line_number.clear();
1502                    let default_number = buffer_row + 1;
1503                    let number = relative_rows
1504                        .get(&(ix as u32 + rows.start))
1505                        .unwrap_or(&default_number);
1506                    write!(&mut line_number, "{}", number).unwrap();
1507                    line_number_layouts.push(Some(cx.text_layout_cache().layout_str(
1508                        &line_number,
1509                        style.text.font_size,
1510                        &[(
1511                            line_number.len(),
1512                            RunStyle {
1513                                font_id: style.text.font_id,
1514                                color,
1515                                underline: Default::default(),
1516                            },
1517                        )],
1518                    )));
1519                    fold_statuses.push(
1520                        is_singleton
1521                            .then(|| {
1522                                snapshot
1523                                    .fold_for_line(buffer_row)
1524                                    .map(|fold_status| (fold_status, buffer_row, active))
1525                            })
1526                            .flatten(),
1527                    )
1528                }
1529            } else {
1530                fold_statuses.push(None);
1531                line_number_layouts.push(None);
1532            }
1533        }
1534
1535        (line_number_layouts, fold_statuses)
1536    }
1537
1538    fn layout_lines(
1539        &mut self,
1540        rows: Range<u32>,
1541        line_number_layouts: &[Option<Line>],
1542        snapshot: &EditorSnapshot,
1543        cx: &ViewContext<Editor>,
1544    ) -> Vec<LineWithInvisibles> {
1545        if rows.start >= rows.end {
1546            return Vec::new();
1547        }
1548
1549        // When the editor is empty and unfocused, then show the placeholder.
1550        if snapshot.is_empty() {
1551            let placeholder_style = self
1552                .style
1553                .placeholder_text
1554                .as_ref()
1555                .unwrap_or(&self.style.text);
1556            let placeholder_text = snapshot.placeholder_text();
1557            let placeholder_lines = placeholder_text
1558                .as_ref()
1559                .map_or("", AsRef::as_ref)
1560                .split('\n')
1561                .skip(rows.start as usize)
1562                .chain(iter::repeat(""))
1563                .take(rows.len());
1564            placeholder_lines
1565                .map(|line| {
1566                    cx.text_layout_cache().layout_str(
1567                        line,
1568                        placeholder_style.font_size,
1569                        &[(
1570                            line.len(),
1571                            RunStyle {
1572                                font_id: placeholder_style.font_id,
1573                                color: placeholder_style.color,
1574                                underline: Default::default(),
1575                            },
1576                        )],
1577                    )
1578                })
1579                .map(|line| LineWithInvisibles {
1580                    line,
1581                    invisibles: Vec::new(),
1582                })
1583                .collect()
1584        } else {
1585            let style = &self.style;
1586            let chunks = snapshot.highlighted_chunks(rows.clone(), true, style);
1587
1588            LineWithInvisibles::from_chunks(
1589                chunks,
1590                &style.text,
1591                cx.text_layout_cache(),
1592                cx.font_cache(),
1593                MAX_LINE_LEN,
1594                rows.len() as usize,
1595                line_number_layouts,
1596                snapshot.mode,
1597            )
1598        }
1599    }
1600
1601    #[allow(clippy::too_many_arguments)]
1602    fn layout_blocks(
1603        &mut self,
1604        rows: Range<u32>,
1605        snapshot: &EditorSnapshot,
1606        editor_width: f32,
1607        scroll_width: f32,
1608        gutter_padding: f32,
1609        gutter_width: f32,
1610        em_width: f32,
1611        text_x: f32,
1612        line_height: f32,
1613        style: &EditorStyle,
1614        line_layouts: &[LineWithInvisibles],
1615        editor: &mut Editor,
1616        cx: &mut ViewContext<Editor>,
1617    ) -> (f32, Vec<BlockLayout>) {
1618        let mut block_id = 0;
1619        let scroll_x = snapshot.scroll_anchor.offset.x();
1620        let (fixed_blocks, non_fixed_blocks) = snapshot
1621            .blocks_in_range(rows.clone())
1622            .partition::<Vec<_>, _>(|(_, block)| match block {
1623                TransformBlock::ExcerptHeader { .. } => false,
1624                TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
1625            });
1626        let mut render_block = |block: &TransformBlock, width: f32, block_id: usize| {
1627            let mut element = match block {
1628                TransformBlock::Custom(block) => {
1629                    let align_to = block
1630                        .position()
1631                        .to_point(&snapshot.buffer_snapshot)
1632                        .to_display_point(snapshot);
1633                    let anchor_x = text_x
1634                        + if rows.contains(&align_to.row()) {
1635                            line_layouts[(align_to.row() - rows.start) as usize]
1636                                .line
1637                                .x_for_index(align_to.column() as usize)
1638                        } else {
1639                            layout_line(align_to.row(), snapshot, style, cx.text_layout_cache())
1640                                .x_for_index(align_to.column() as usize)
1641                        };
1642
1643                    block.render(&mut BlockContext {
1644                        view_context: cx,
1645                        anchor_x,
1646                        gutter_padding,
1647                        line_height,
1648                        scroll_x,
1649                        gutter_width,
1650                        em_width,
1651                        block_id,
1652                    })
1653                }
1654                TransformBlock::ExcerptHeader {
1655                    id,
1656                    buffer,
1657                    range,
1658                    starts_new_buffer,
1659                    ..
1660                } => {
1661                    let tooltip_style = theme::current(cx).tooltip.clone();
1662                    let include_root = editor
1663                        .project
1664                        .as_ref()
1665                        .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
1666                        .unwrap_or_default();
1667                    let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
1668                        let jump_path = ProjectPath {
1669                            worktree_id: file.worktree_id(cx),
1670                            path: file.path.clone(),
1671                        };
1672                        let jump_anchor = range
1673                            .primary
1674                            .as_ref()
1675                            .map_or(range.context.start, |primary| primary.start);
1676                        let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
1677
1678                        enum JumpIcon {}
1679                        MouseEventHandler::new::<JumpIcon, _>((*id).into(), cx, |state, _| {
1680                            let style = style.jump_icon.style_for(state);
1681                            Svg::new("icons/arrow_up_right.svg")
1682                                .with_color(style.color)
1683                                .constrained()
1684                                .with_width(style.icon_width)
1685                                .aligned()
1686                                .contained()
1687                                .with_style(style.container)
1688                                .constrained()
1689                                .with_width(style.button_width)
1690                                .with_height(style.button_width)
1691                        })
1692                        .with_cursor_style(CursorStyle::PointingHand)
1693                        .on_click(MouseButton::Left, move |_, editor, cx| {
1694                            if let Some(workspace) = editor
1695                                .workspace
1696                                .as_ref()
1697                                .and_then(|(workspace, _)| workspace.upgrade(cx))
1698                            {
1699                                workspace.update(cx, |workspace, cx| {
1700                                    Editor::jump(
1701                                        workspace,
1702                                        jump_path.clone(),
1703                                        jump_position,
1704                                        jump_anchor,
1705                                        cx,
1706                                    );
1707                                });
1708                            }
1709                        })
1710                        .with_tooltip::<JumpIcon>(
1711                            (*id).into(),
1712                            "Jump to Buffer".to_string(),
1713                            Some(Box::new(crate::OpenExcerpts)),
1714                            tooltip_style.clone(),
1715                            cx,
1716                        )
1717                        .aligned()
1718                        .flex_float()
1719                    });
1720
1721                    if *starts_new_buffer {
1722                        let editor_font_size = style.text.font_size;
1723                        let style = &style.diagnostic_path_header;
1724                        let font_size = (style.text_scale_factor * editor_font_size).round();
1725
1726                        let path = buffer.resolve_file_path(cx, include_root);
1727                        let mut filename = None;
1728                        let mut parent_path = None;
1729                        // Can't use .and_then() because `.file_name()` and `.parent()` return references :(
1730                        if let Some(path) = path {
1731                            filename = path.file_name().map(|f| f.to_string_lossy().to_string());
1732                            parent_path =
1733                                path.parent().map(|p| p.to_string_lossy().to_string() + "/");
1734                        }
1735
1736                        Flex::row()
1737                            .with_child(
1738                                Label::new(
1739                                    filename.unwrap_or_else(|| "untitled".to_string()),
1740                                    style.filename.text.clone().with_font_size(font_size),
1741                                )
1742                                .contained()
1743                                .with_style(style.filename.container)
1744                                .aligned(),
1745                            )
1746                            .with_children(parent_path.map(|path| {
1747                                Label::new(path, style.path.text.clone().with_font_size(font_size))
1748                                    .contained()
1749                                    .with_style(style.path.container)
1750                                    .aligned()
1751                            }))
1752                            .with_children(jump_icon)
1753                            .contained()
1754                            .with_style(style.container)
1755                            .with_padding_left(gutter_padding)
1756                            .with_padding_right(gutter_padding)
1757                            .expanded()
1758                            .into_any_named("path header block")
1759                    } else {
1760                        let text_style = style.text.clone();
1761                        Flex::row()
1762                            .with_child(Label::new("", text_style))
1763                            .with_children(jump_icon)
1764                            .contained()
1765                            .with_padding_left(gutter_padding)
1766                            .with_padding_right(gutter_padding)
1767                            .expanded()
1768                            .into_any_named("collapsed context")
1769                    }
1770                }
1771            };
1772
1773            element.layout(
1774                SizeConstraint {
1775                    min: Vector2F::zero(),
1776                    max: vec2f(width, block.height() as f32 * line_height),
1777                },
1778                editor,
1779                cx,
1780            );
1781            element
1782        };
1783
1784        let mut fixed_block_max_width = 0f32;
1785        let mut blocks = Vec::new();
1786        for (row, block) in fixed_blocks {
1787            let element = render_block(block, f32::INFINITY, block_id);
1788            block_id += 1;
1789            fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width);
1790            blocks.push(BlockLayout {
1791                row,
1792                element,
1793                style: BlockStyle::Fixed,
1794            });
1795        }
1796        for (row, block) in non_fixed_blocks {
1797            let style = match block {
1798                TransformBlock::Custom(block) => block.style(),
1799                TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
1800            };
1801            let width = match style {
1802                BlockStyle::Sticky => editor_width,
1803                BlockStyle::Flex => editor_width
1804                    .max(fixed_block_max_width)
1805                    .max(gutter_width + scroll_width),
1806                BlockStyle::Fixed => unreachable!(),
1807            };
1808            let element = render_block(block, width, block_id);
1809            block_id += 1;
1810            blocks.push(BlockLayout {
1811                row,
1812                element,
1813                style,
1814            });
1815        }
1816        (
1817            scroll_width.max(fixed_block_max_width - gutter_width),
1818            blocks,
1819        )
1820    }
1821}
1822
1823#[derive(Debug)]
1824pub struct LineWithInvisibles {
1825    pub line: Line,
1826    invisibles: Vec<Invisible>,
1827}
1828
1829impl LineWithInvisibles {
1830    fn from_chunks<'a>(
1831        chunks: impl Iterator<Item = HighlightedChunk<'a>>,
1832        text_style: &TextStyle,
1833        text_layout_cache: &TextLayoutCache,
1834        font_cache: &Arc<FontCache>,
1835        max_line_len: usize,
1836        max_line_count: usize,
1837        line_number_layouts: &[Option<Line>],
1838        editor_mode: EditorMode,
1839    ) -> Vec<Self> {
1840        let mut layouts = Vec::with_capacity(max_line_count);
1841        let mut line = String::new();
1842        let mut invisibles = Vec::new();
1843        let mut styles = Vec::new();
1844        let mut non_whitespace_added = false;
1845        let mut row = 0;
1846        let mut line_exceeded_max_len = false;
1847        for highlighted_chunk in chunks.chain([HighlightedChunk {
1848            chunk: "\n",
1849            style: None,
1850            is_tab: false,
1851        }]) {
1852            for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
1853                if ix > 0 {
1854                    layouts.push(Self {
1855                        line: text_layout_cache.layout_str(&line, text_style.font_size, &styles),
1856                        invisibles: invisibles.drain(..).collect(),
1857                    });
1858
1859                    line.clear();
1860                    styles.clear();
1861                    row += 1;
1862                    line_exceeded_max_len = false;
1863                    non_whitespace_added = false;
1864                    if row == max_line_count {
1865                        return layouts;
1866                    }
1867                }
1868
1869                if !line_chunk.is_empty() && !line_exceeded_max_len {
1870                    let text_style = if let Some(style) = highlighted_chunk.style {
1871                        text_style
1872                            .clone()
1873                            .highlight(style, font_cache)
1874                            .map(Cow::Owned)
1875                            .unwrap_or_else(|_| Cow::Borrowed(text_style))
1876                    } else {
1877                        Cow::Borrowed(text_style)
1878                    };
1879
1880                    if line.len() + line_chunk.len() > max_line_len {
1881                        let mut chunk_len = max_line_len - line.len();
1882                        while !line_chunk.is_char_boundary(chunk_len) {
1883                            chunk_len -= 1;
1884                        }
1885                        line_chunk = &line_chunk[..chunk_len];
1886                        line_exceeded_max_len = true;
1887                    }
1888
1889                    styles.push((
1890                        line_chunk.len(),
1891                        RunStyle {
1892                            font_id: text_style.font_id,
1893                            color: text_style.color,
1894                            underline: text_style.underline,
1895                        },
1896                    ));
1897
1898                    if editor_mode == EditorMode::Full {
1899                        // Line wrap pads its contents with fake whitespaces,
1900                        // avoid printing them
1901                        let inside_wrapped_string = line_number_layouts
1902                            .get(row)
1903                            .and_then(|layout| layout.as_ref())
1904                            .is_none();
1905                        if highlighted_chunk.is_tab {
1906                            if non_whitespace_added || !inside_wrapped_string {
1907                                invisibles.push(Invisible::Tab {
1908                                    line_start_offset: line.len(),
1909                                });
1910                            }
1911                        } else {
1912                            invisibles.extend(
1913                                line_chunk
1914                                    .chars()
1915                                    .enumerate()
1916                                    .filter(|(_, line_char)| {
1917                                        let is_whitespace = line_char.is_whitespace();
1918                                        non_whitespace_added |= !is_whitespace;
1919                                        is_whitespace
1920                                            && (non_whitespace_added || !inside_wrapped_string)
1921                                    })
1922                                    .map(|(whitespace_index, _)| Invisible::Whitespace {
1923                                        line_offset: line.len() + whitespace_index,
1924                                    }),
1925                            )
1926                        }
1927                    }
1928
1929                    line.push_str(line_chunk);
1930                }
1931            }
1932        }
1933
1934        layouts
1935    }
1936
1937    fn draw(
1938        &self,
1939        layout: &LayoutState,
1940        row: u32,
1941        scroll_top: f32,
1942        content_origin: Vector2F,
1943        scroll_left: f32,
1944        visible_text_bounds: RectF,
1945        whitespace_setting: ShowWhitespaceSetting,
1946        selection_ranges: &[Range<DisplayPoint>],
1947        visible_bounds: RectF,
1948        cx: &mut ViewContext<Editor>,
1949    ) {
1950        let line_height = layout.position_map.line_height;
1951        let line_y = row as f32 * line_height - scroll_top;
1952
1953        self.line.paint(
1954            content_origin + vec2f(-scroll_left, line_y),
1955            visible_text_bounds,
1956            line_height,
1957            cx,
1958        );
1959
1960        self.draw_invisibles(
1961            &selection_ranges,
1962            layout,
1963            content_origin,
1964            scroll_left,
1965            line_y,
1966            row,
1967            visible_bounds,
1968            line_height,
1969            whitespace_setting,
1970            cx,
1971        );
1972    }
1973
1974    fn draw_invisibles(
1975        &self,
1976        selection_ranges: &[Range<DisplayPoint>],
1977        layout: &LayoutState,
1978        content_origin: Vector2F,
1979        scroll_left: f32,
1980        line_y: f32,
1981        row: u32,
1982        visible_bounds: RectF,
1983        line_height: f32,
1984        whitespace_setting: ShowWhitespaceSetting,
1985        cx: &mut ViewContext<Editor>,
1986    ) {
1987        let allowed_invisibles_regions = match whitespace_setting {
1988            ShowWhitespaceSetting::None => return,
1989            ShowWhitespaceSetting::Selection => Some(selection_ranges),
1990            ShowWhitespaceSetting::All => None,
1991        };
1992
1993        for invisible in &self.invisibles {
1994            let (&token_offset, invisible_symbol) = match invisible {
1995                Invisible::Tab { line_start_offset } => (line_start_offset, &layout.tab_invisible),
1996                Invisible::Whitespace { line_offset } => (line_offset, &layout.space_invisible),
1997            };
1998
1999            let x_offset = self.line.x_for_index(token_offset);
2000            let invisible_offset =
2001                (layout.position_map.em_width - invisible_symbol.width()).max(0.0) / 2.0;
2002            let origin = content_origin + vec2f(-scroll_left + x_offset + invisible_offset, line_y);
2003
2004            if let Some(allowed_regions) = allowed_invisibles_regions {
2005                let invisible_point = DisplayPoint::new(row, token_offset as u32);
2006                if !allowed_regions
2007                    .iter()
2008                    .any(|region| region.start <= invisible_point && invisible_point < region.end)
2009                {
2010                    continue;
2011                }
2012            }
2013            invisible_symbol.paint(origin, visible_bounds, line_height, cx);
2014        }
2015    }
2016}
2017
2018#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2019enum Invisible {
2020    Tab { line_start_offset: usize },
2021    Whitespace { line_offset: usize },
2022}
2023
2024impl Element<Editor> for EditorElement {
2025    type LayoutState = LayoutState;
2026    type PaintState = ();
2027
2028    fn layout(
2029        &mut self,
2030        constraint: SizeConstraint,
2031        editor: &mut Editor,
2032        cx: &mut ViewContext<Editor>,
2033    ) -> (Vector2F, Self::LayoutState) {
2034        let mut size = constraint.max;
2035        if size.x().is_infinite() {
2036            unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
2037        }
2038
2039        let snapshot = editor.snapshot(cx);
2040        let style = self.style.clone();
2041
2042        let line_height = (style.text.font_size * style.line_height_scalar).round();
2043
2044        let gutter_padding;
2045        let gutter_width;
2046        let gutter_margin;
2047        if snapshot.show_gutter {
2048            let em_width = style.text.em_width(cx.font_cache());
2049            gutter_padding = (em_width * style.gutter_padding_factor).round();
2050            gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
2051            gutter_margin = -style.text.descent(cx.font_cache());
2052        } else {
2053            gutter_padding = 0.0;
2054            gutter_width = 0.0;
2055            gutter_margin = 0.0;
2056        };
2057
2058        let text_width = size.x() - gutter_width;
2059        let em_width = style.text.em_width(cx.font_cache());
2060        let em_advance = style.text.em_advance(cx.font_cache());
2061        let overscroll = vec2f(em_width, 0.);
2062        let snapshot = {
2063            editor.set_visible_line_count(size.y() / line_height, cx);
2064
2065            let editor_width = text_width - gutter_margin - overscroll.x() - em_width;
2066            let wrap_width = match editor.soft_wrap_mode(cx) {
2067                SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
2068                SoftWrap::EditorWidth => editor_width,
2069                SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
2070            };
2071
2072            if editor.set_wrap_width(Some(wrap_width), cx) {
2073                editor.snapshot(cx)
2074            } else {
2075                snapshot
2076            }
2077        };
2078
2079        let wrap_guides = editor
2080            .wrap_guides(cx)
2081            .iter()
2082            .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
2083            .collect();
2084
2085        let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
2086        if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
2087            size.set_y(
2088                scroll_height
2089                    .min(constraint.max_along(Axis::Vertical))
2090                    .max(constraint.min_along(Axis::Vertical))
2091                    .max(line_height)
2092                    .min(line_height * max_lines as f32),
2093            )
2094        } else if let EditorMode::SingleLine = snapshot.mode {
2095            size.set_y(line_height.max(constraint.min_along(Axis::Vertical)))
2096        } else if size.y().is_infinite() {
2097            size.set_y(scroll_height);
2098        }
2099        let gutter_size = vec2f(gutter_width, size.y());
2100        let text_size = vec2f(text_width, size.y());
2101
2102        let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), line_height, cx);
2103        let mut snapshot = editor.snapshot(cx);
2104
2105        let scroll_position = snapshot.scroll_position();
2106        // The scroll position is a fractional point, the whole number of which represents
2107        // the top of the window in terms of display rows.
2108        let start_row = scroll_position.y() as u32;
2109        let height_in_lines = size.y() / line_height;
2110        let max_row = snapshot.max_point().row();
2111
2112        // Add 1 to ensure selections bleed off screen
2113        let end_row = 1 + cmp::min(
2114            (scroll_position.y() + height_in_lines).ceil() as u32,
2115            max_row,
2116        );
2117
2118        let start_anchor = if start_row == 0 {
2119            Anchor::min()
2120        } else {
2121            snapshot
2122                .buffer_snapshot
2123                .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
2124        };
2125        let end_anchor = if end_row > max_row {
2126            Anchor::max()
2127        } else {
2128            snapshot
2129                .buffer_snapshot
2130                .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
2131        };
2132
2133        let mut selections: Vec<(SelectionStyle, Vec<SelectionLayout>)> = Vec::new();
2134        let mut active_rows = BTreeMap::new();
2135        let mut fold_ranges = Vec::new();
2136        let is_singleton = editor.is_singleton(cx);
2137
2138        let highlighted_rows = editor.highlighted_rows();
2139        let theme = theme::current(cx);
2140        let highlighted_ranges = editor.background_highlights_in_range(
2141            start_anchor..end_anchor,
2142            &snapshot.display_snapshot,
2143            theme.as_ref(),
2144        );
2145
2146        fold_ranges.extend(
2147            snapshot
2148                .folds_in_range(start_anchor..end_anchor)
2149                .map(|anchor| {
2150                    let start = anchor.start.to_point(&snapshot.buffer_snapshot);
2151                    (
2152                        start.row,
2153                        start.to_display_point(&snapshot.display_snapshot)
2154                            ..anchor.end.to_display_point(&snapshot),
2155                    )
2156                }),
2157        );
2158
2159        let mut newest_selection_head = None;
2160
2161        if editor.show_local_selections {
2162            let mut local_selections: Vec<Selection<Point>> = editor
2163                .selections
2164                .disjoint_in_range(start_anchor..end_anchor, cx);
2165            local_selections.extend(editor.selections.pending(cx));
2166            let mut layouts = Vec::new();
2167            let newest = editor.selections.newest(cx);
2168            for selection in local_selections.drain(..) {
2169                let is_empty = selection.start == selection.end;
2170                let is_newest = selection == newest;
2171
2172                let layout = SelectionLayout::new(
2173                    selection,
2174                    editor.selections.line_mode,
2175                    editor.cursor_shape,
2176                    &snapshot.display_snapshot,
2177                    is_newest,
2178                    true,
2179                );
2180                if is_newest {
2181                    newest_selection_head = Some(layout.head);
2182                }
2183
2184                for row in cmp::max(layout.active_rows.start, start_row)
2185                    ..=cmp::min(layout.active_rows.end, end_row)
2186                {
2187                    let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
2188                    *contains_non_empty_selection |= !is_empty;
2189                }
2190                layouts.push(layout);
2191            }
2192
2193            selections.push((style.selection, layouts));
2194        }
2195
2196        if let Some(collaboration_hub) = &editor.collaboration_hub {
2197            // When following someone, render the local selections in their color.
2198            if let Some(leader_id) = editor.leader_peer_id {
2199                if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) {
2200                    if let Some(participant_index) = collaboration_hub
2201                        .user_participant_indices(cx)
2202                        .get(&collaborator.user_id)
2203                    {
2204                        if let Some((local_selection_style, _)) = selections.first_mut() {
2205                            *local_selection_style =
2206                                style.selection_style_for_room_participant(participant_index.0);
2207                        }
2208                    }
2209                }
2210            }
2211
2212            let mut remote_selections = HashMap::default();
2213            for selection in snapshot.remote_selections_in_range(
2214                &(start_anchor..end_anchor),
2215                collaboration_hub.as_ref(),
2216                cx,
2217            ) {
2218                let selection_style = if let Some(participant_index) = selection.participant_index {
2219                    style.selection_style_for_room_participant(participant_index.0)
2220                } else {
2221                    style.absent_selection
2222                };
2223
2224                // Don't re-render the leader's selections, since the local selections
2225                // match theirs.
2226                if Some(selection.peer_id) == editor.leader_peer_id {
2227                    continue;
2228                }
2229
2230                remote_selections
2231                    .entry(selection.replica_id)
2232                    .or_insert((selection_style, Vec::new()))
2233                    .1
2234                    .push(SelectionLayout::new(
2235                        selection.selection,
2236                        selection.line_mode,
2237                        selection.cursor_shape,
2238                        &snapshot.display_snapshot,
2239                        false,
2240                        false,
2241                    ));
2242            }
2243
2244            selections.extend(remote_selections.into_values());
2245        }
2246
2247        let scrollbar_settings = &settings::get::<EditorSettings>(cx).scrollbar;
2248        let show_scrollbars = match scrollbar_settings.show {
2249            ShowScrollbar::Auto => {
2250                // Git
2251                (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
2252                ||
2253                // Selections
2254                (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
2255                // Scrollmanager
2256                || editor.scroll_manager.scrollbars_visible()
2257            }
2258            ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
2259            ShowScrollbar::Always => true,
2260            ShowScrollbar::Never => false,
2261        };
2262
2263        let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)> = fold_ranges
2264            .into_iter()
2265            .map(|(id, fold)| {
2266                let color = self
2267                    .style
2268                    .folds
2269                    .ellipses
2270                    .background
2271                    .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
2272                    .color;
2273
2274                (id, fold, color)
2275            })
2276            .collect();
2277
2278        let head_for_relative = newest_selection_head.unwrap_or_else(|| {
2279            let newest = editor.selections.newest::<Point>(cx);
2280            SelectionLayout::new(
2281                newest,
2282                editor.selections.line_mode,
2283                editor.cursor_shape,
2284                &snapshot.display_snapshot,
2285                true,
2286                true,
2287            )
2288            .head
2289        });
2290
2291        let (line_number_layouts, fold_statuses) = self.layout_line_numbers(
2292            start_row..end_row,
2293            &active_rows,
2294            head_for_relative,
2295            is_singleton,
2296            &snapshot,
2297            cx,
2298        );
2299
2300        let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
2301
2302        let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines);
2303
2304        let mut max_visible_line_width = 0.0;
2305        let line_layouts =
2306            self.layout_lines(start_row..end_row, &line_number_layouts, &snapshot, cx);
2307        for line_with_invisibles in &line_layouts {
2308            if line_with_invisibles.line.width() > max_visible_line_width {
2309                max_visible_line_width = line_with_invisibles.line.width();
2310            }
2311        }
2312
2313        let style = self.style.clone();
2314        let longest_line_width = layout_line(
2315            snapshot.longest_row(),
2316            &snapshot,
2317            &style,
2318            cx.text_layout_cache(),
2319        )
2320        .width();
2321        let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x();
2322        let em_width = style.text.em_width(cx.font_cache());
2323        let (scroll_width, blocks) = self.layout_blocks(
2324            start_row..end_row,
2325            &snapshot,
2326            size.x(),
2327            scroll_width,
2328            gutter_padding,
2329            gutter_width,
2330            em_width,
2331            gutter_width + gutter_margin,
2332            line_height,
2333            &style,
2334            &line_layouts,
2335            editor,
2336            cx,
2337        );
2338
2339        let scroll_max = vec2f(
2340            ((scroll_width - text_size.x()) / em_width).max(0.0),
2341            max_row as f32,
2342        );
2343
2344        let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x());
2345
2346        let autoscrolled = if autoscroll_horizontally {
2347            editor.autoscroll_horizontally(
2348                start_row,
2349                text_size.x(),
2350                scroll_width,
2351                em_width,
2352                &line_layouts,
2353                cx,
2354            )
2355        } else {
2356            false
2357        };
2358
2359        if clamped || autoscrolled {
2360            snapshot = editor.snapshot(cx);
2361        }
2362
2363        let style = editor.style(cx);
2364
2365        let mut context_menu = None;
2366        let mut code_actions_indicator = None;
2367        if let Some(newest_selection_head) = newest_selection_head {
2368            if (start_row..end_row).contains(&newest_selection_head.row()) {
2369                if editor.context_menu_visible() {
2370                    context_menu =
2371                        editor.render_context_menu(newest_selection_head, style.clone(), cx);
2372                }
2373
2374                let active = matches!(
2375                    editor.context_menu.read().as_ref(),
2376                    Some(crate::ContextMenu::CodeActions(_))
2377                );
2378
2379                code_actions_indicator = editor
2380                    .render_code_actions_indicator(&style, active, cx)
2381                    .map(|indicator| (newest_selection_head.row(), indicator));
2382            }
2383        }
2384
2385        let visible_rows = start_row..start_row + line_layouts.len() as u32;
2386        let mut hover = editor.hover_state.render(
2387            &snapshot,
2388            &style,
2389            visible_rows,
2390            editor.workspace.as_ref().map(|(w, _)| w.clone()),
2391            cx,
2392        );
2393        let mode = editor.mode;
2394
2395        let mut fold_indicators = editor.render_fold_indicators(
2396            fold_statuses,
2397            &style,
2398            editor.gutter_hovered,
2399            line_height,
2400            gutter_margin,
2401            cx,
2402        );
2403
2404        if let Some((_, context_menu)) = context_menu.as_mut() {
2405            context_menu.layout(
2406                SizeConstraint {
2407                    min: Vector2F::zero(),
2408                    max: vec2f(
2409                        cx.window_size().x() * 0.7,
2410                        (12. * line_height).min((size.y() - line_height) / 2.),
2411                    ),
2412                },
2413                editor,
2414                cx,
2415            );
2416        }
2417
2418        if let Some((_, indicator)) = code_actions_indicator.as_mut() {
2419            indicator.layout(
2420                SizeConstraint::strict_along(
2421                    Axis::Vertical,
2422                    line_height * style.code_actions.vertical_scale,
2423                ),
2424                editor,
2425                cx,
2426            );
2427        }
2428
2429        for fold_indicator in fold_indicators.iter_mut() {
2430            if let Some(indicator) = fold_indicator.as_mut() {
2431                indicator.layout(
2432                    SizeConstraint::strict_along(
2433                        Axis::Vertical,
2434                        line_height * style.code_actions.vertical_scale,
2435                    ),
2436                    editor,
2437                    cx,
2438                );
2439            }
2440        }
2441
2442        if let Some((_, hover_popovers)) = hover.as_mut() {
2443            for hover_popover in hover_popovers.iter_mut() {
2444                hover_popover.layout(
2445                    SizeConstraint {
2446                        min: Vector2F::zero(),
2447                        max: vec2f(
2448                            (120. * em_width) // Default size
2449                                .min(size.x() / 2.) // Shrink to half of the editor width
2450                                .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
2451                            (16. * line_height) // Default size
2452                                .min(size.y() / 2.) // Shrink to half of the editor height
2453                                .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
2454                        ),
2455                    },
2456                    editor,
2457                    cx,
2458                );
2459            }
2460        }
2461
2462        let invisible_symbol_font_size = self.style.text.font_size / 2.0;
2463        let invisible_symbol_style = RunStyle {
2464            color: self.style.whitespace,
2465            font_id: self.style.text.font_id,
2466            underline: Default::default(),
2467        };
2468
2469        (
2470            size,
2471            LayoutState {
2472                mode,
2473                position_map: Arc::new(PositionMap {
2474                    size,
2475                    scroll_max,
2476                    line_layouts,
2477                    line_height,
2478                    em_width,
2479                    em_advance,
2480                    snapshot,
2481                }),
2482                visible_display_row_range: start_row..end_row,
2483                wrap_guides,
2484                gutter_size,
2485                gutter_padding,
2486                text_size,
2487                scrollbar_row_range,
2488                show_scrollbars,
2489                is_singleton,
2490                max_row,
2491                gutter_margin,
2492                active_rows,
2493                highlighted_rows,
2494                highlighted_ranges,
2495                fold_ranges,
2496                line_number_layouts,
2497                display_hunks,
2498                blocks,
2499                selections,
2500                context_menu,
2501                code_actions_indicator,
2502                fold_indicators,
2503                tab_invisible: cx.text_layout_cache().layout_str(
2504                    "",
2505                    invisible_symbol_font_size,
2506                    &[("".len(), invisible_symbol_style)],
2507                ),
2508                space_invisible: cx.text_layout_cache().layout_str(
2509                    "",
2510                    invisible_symbol_font_size,
2511                    &[("".len(), invisible_symbol_style)],
2512                ),
2513                hover_popovers: hover,
2514            },
2515        )
2516    }
2517
2518    fn paint(
2519        &mut self,
2520        bounds: RectF,
2521        visible_bounds: RectF,
2522        layout: &mut Self::LayoutState,
2523        editor: &mut Editor,
2524        cx: &mut ViewContext<Editor>,
2525    ) -> Self::PaintState {
2526        let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
2527        cx.scene().push_layer(Some(visible_bounds));
2528
2529        let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
2530        let text_bounds = RectF::new(
2531            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2532            layout.text_size,
2533        );
2534
2535        Self::attach_mouse_handlers(
2536            &layout.position_map,
2537            layout.hover_popovers.is_some(),
2538            visible_bounds,
2539            text_bounds,
2540            gutter_bounds,
2541            bounds,
2542            cx,
2543        );
2544
2545        self.paint_background(gutter_bounds, text_bounds, layout, cx);
2546        if layout.gutter_size.x() > 0. {
2547            self.paint_gutter(gutter_bounds, visible_bounds, layout, editor, cx);
2548        }
2549        self.paint_text(text_bounds, visible_bounds, layout, editor, cx);
2550
2551        cx.scene().push_layer(Some(bounds));
2552        if !layout.blocks.is_empty() {
2553            self.paint_blocks(bounds, visible_bounds, layout, editor, cx);
2554        }
2555        self.paint_scrollbar(bounds, layout, &editor, cx);
2556        cx.scene().pop_layer();
2557        cx.scene().pop_layer();
2558    }
2559
2560    fn rect_for_text_range(
2561        &self,
2562        range_utf16: Range<usize>,
2563        bounds: RectF,
2564        _: RectF,
2565        layout: &Self::LayoutState,
2566        _: &Self::PaintState,
2567        _: &Editor,
2568        _: &ViewContext<Editor>,
2569    ) -> Option<RectF> {
2570        let text_bounds = RectF::new(
2571            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2572            layout.text_size,
2573        );
2574        let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.);
2575        let scroll_position = layout.position_map.snapshot.scroll_position();
2576        let start_row = scroll_position.y() as u32;
2577        let scroll_top = scroll_position.y() * layout.position_map.line_height;
2578        let scroll_left = scroll_position.x() * layout.position_map.em_width;
2579
2580        let range_start = OffsetUtf16(range_utf16.start)
2581            .to_display_point(&layout.position_map.snapshot.display_snapshot);
2582        if range_start.row() < start_row {
2583            return None;
2584        }
2585
2586        let line = &layout
2587            .position_map
2588            .line_layouts
2589            .get((range_start.row() - start_row) as usize)?
2590            .line;
2591        let range_start_x = line.x_for_index(range_start.column() as usize);
2592        let range_start_y = range_start.row() as f32 * layout.position_map.line_height;
2593        Some(RectF::new(
2594            content_origin
2595                + vec2f(
2596                    range_start_x,
2597                    range_start_y + layout.position_map.line_height,
2598                )
2599                - vec2f(scroll_left, scroll_top),
2600            vec2f(
2601                layout.position_map.em_width,
2602                layout.position_map.line_height,
2603            ),
2604        ))
2605    }
2606
2607    fn debug(
2608        &self,
2609        bounds: RectF,
2610        _: &Self::LayoutState,
2611        _: &Self::PaintState,
2612        _: &Editor,
2613        _: &ViewContext<Editor>,
2614    ) -> json::Value {
2615        json!({
2616            "type": "BufferElement",
2617            "bounds": bounds.to_json()
2618        })
2619    }
2620}
2621
2622type BufferRow = u32;
2623
2624pub struct LayoutState {
2625    position_map: Arc<PositionMap>,
2626    gutter_size: Vector2F,
2627    gutter_padding: f32,
2628    gutter_margin: f32,
2629    text_size: Vector2F,
2630    mode: EditorMode,
2631    wrap_guides: SmallVec<[(f32, bool); 2]>,
2632    visible_display_row_range: Range<u32>,
2633    active_rows: BTreeMap<u32, bool>,
2634    highlighted_rows: Option<Range<u32>>,
2635    line_number_layouts: Vec<Option<text_layout::Line>>,
2636    display_hunks: Vec<DisplayDiffHunk>,
2637    blocks: Vec<BlockLayout>,
2638    highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
2639    fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)>,
2640    selections: Vec<(SelectionStyle, Vec<SelectionLayout>)>,
2641    scrollbar_row_range: Range<f32>,
2642    show_scrollbars: bool,
2643    is_singleton: bool,
2644    max_row: u32,
2645    context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
2646    code_actions_indicator: Option<(u32, AnyElement<Editor>)>,
2647    hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
2648    fold_indicators: Vec<Option<AnyElement<Editor>>>,
2649    tab_invisible: Line,
2650    space_invisible: Line,
2651}
2652
2653struct PositionMap {
2654    size: Vector2F,
2655    line_height: f32,
2656    scroll_max: Vector2F,
2657    em_width: f32,
2658    em_advance: f32,
2659    line_layouts: Vec<LineWithInvisibles>,
2660    snapshot: EditorSnapshot,
2661}
2662
2663#[derive(Debug, Copy, Clone)]
2664pub struct PointForPosition {
2665    pub previous_valid: DisplayPoint,
2666    pub next_valid: DisplayPoint,
2667    pub exact_unclipped: DisplayPoint,
2668    pub column_overshoot_after_line_end: u32,
2669}
2670
2671impl PointForPosition {
2672    #[cfg(test)]
2673    pub fn valid(valid: DisplayPoint) -> Self {
2674        Self {
2675            previous_valid: valid,
2676            next_valid: valid,
2677            exact_unclipped: valid,
2678            column_overshoot_after_line_end: 0,
2679        }
2680    }
2681
2682    pub fn as_valid(&self) -> Option<DisplayPoint> {
2683        if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
2684            Some(self.previous_valid)
2685        } else {
2686            None
2687        }
2688    }
2689}
2690
2691impl PositionMap {
2692    fn point_for_position(&self, text_bounds: RectF, position: Vector2F) -> PointForPosition {
2693        let scroll_position = self.snapshot.scroll_position();
2694        let position = position - text_bounds.origin();
2695        let y = position.y().max(0.0).min(self.size.y());
2696        let x = position.x() + (scroll_position.x() * self.em_width);
2697        let row = (y / self.line_height + scroll_position.y()) as u32;
2698        let (column, x_overshoot_after_line_end) = if let Some(line) = self
2699            .line_layouts
2700            .get(row as usize - scroll_position.y() as usize)
2701            .map(|line_with_spaces| &line_with_spaces.line)
2702        {
2703            if let Some(ix) = line.index_for_x(x) {
2704                (ix as u32, 0.0)
2705            } else {
2706                (line.len() as u32, 0f32.max(x - line.width()))
2707            }
2708        } else {
2709            (0, x)
2710        };
2711
2712        let mut exact_unclipped = DisplayPoint::new(row, column);
2713        let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
2714        let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
2715
2716        let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
2717        *exact_unclipped.column_mut() += column_overshoot_after_line_end;
2718        PointForPosition {
2719            previous_valid,
2720            next_valid,
2721            exact_unclipped,
2722            column_overshoot_after_line_end,
2723        }
2724    }
2725}
2726
2727struct BlockLayout {
2728    row: u32,
2729    element: AnyElement<Editor>,
2730    style: BlockStyle,
2731}
2732
2733fn layout_line(
2734    row: u32,
2735    snapshot: &EditorSnapshot,
2736    style: &EditorStyle,
2737    layout_cache: &TextLayoutCache,
2738) -> text_layout::Line {
2739    let mut line = snapshot.line(row);
2740
2741    if line.len() > MAX_LINE_LEN {
2742        let mut len = MAX_LINE_LEN;
2743        while !line.is_char_boundary(len) {
2744            len -= 1;
2745        }
2746
2747        line.truncate(len);
2748    }
2749
2750    layout_cache.layout_str(
2751        &line,
2752        style.text.font_size,
2753        &[(
2754            snapshot.line_len(row) as usize,
2755            RunStyle {
2756                font_id: style.text.font_id,
2757                color: Color::black(),
2758                underline: Default::default(),
2759            },
2760        )],
2761    )
2762}
2763
2764#[derive(Debug)]
2765pub struct Cursor {
2766    origin: Vector2F,
2767    block_width: f32,
2768    line_height: f32,
2769    color: Color,
2770    shape: CursorShape,
2771    block_text: Option<Line>,
2772}
2773
2774impl Cursor {
2775    pub fn new(
2776        origin: Vector2F,
2777        block_width: f32,
2778        line_height: f32,
2779        color: Color,
2780        shape: CursorShape,
2781        block_text: Option<Line>,
2782    ) -> Cursor {
2783        Cursor {
2784            origin,
2785            block_width,
2786            line_height,
2787            color,
2788            shape,
2789            block_text,
2790        }
2791    }
2792
2793    pub fn bounding_rect(&self, origin: Vector2F) -> RectF {
2794        RectF::new(
2795            self.origin + origin,
2796            vec2f(self.block_width, self.line_height),
2797        )
2798    }
2799
2800    pub fn paint(&self, origin: Vector2F, cx: &mut WindowContext) {
2801        let bounds = match self.shape {
2802            CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
2803            CursorShape::Block | CursorShape::Hollow => RectF::new(
2804                self.origin + origin,
2805                vec2f(self.block_width, self.line_height),
2806            ),
2807            CursorShape::Underscore => RectF::new(
2808                self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
2809                vec2f(self.block_width, 2.0),
2810            ),
2811        };
2812
2813        //Draw background or border quad
2814        if matches!(self.shape, CursorShape::Hollow) {
2815            cx.scene().push_quad(Quad {
2816                bounds,
2817                background: None,
2818                border: Border::all(1., self.color).into(),
2819                corner_radii: Default::default(),
2820            });
2821        } else {
2822            cx.scene().push_quad(Quad {
2823                bounds,
2824                background: Some(self.color),
2825                border: Default::default(),
2826                corner_radii: Default::default(),
2827            });
2828        }
2829
2830        if let Some(block_text) = &self.block_text {
2831            block_text.paint(self.origin + origin, bounds, self.line_height, cx);
2832        }
2833    }
2834
2835    pub fn shape(&self) -> CursorShape {
2836        self.shape
2837    }
2838}
2839
2840#[derive(Debug)]
2841pub struct HighlightedRange {
2842    pub start_y: f32,
2843    pub line_height: f32,
2844    pub lines: Vec<HighlightedRangeLine>,
2845    pub color: Color,
2846    pub corner_radius: f32,
2847}
2848
2849#[derive(Debug)]
2850pub struct HighlightedRangeLine {
2851    pub start_x: f32,
2852    pub end_x: f32,
2853}
2854
2855impl HighlightedRange {
2856    pub fn paint(&self, bounds: RectF, cx: &mut WindowContext) {
2857        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
2858            self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx);
2859            self.paint_lines(
2860                self.start_y + self.line_height,
2861                &self.lines[1..],
2862                bounds,
2863                cx,
2864            );
2865        } else {
2866            self.paint_lines(self.start_y, &self.lines, bounds, cx);
2867        }
2868    }
2869
2870    fn paint_lines(
2871        &self,
2872        start_y: f32,
2873        lines: &[HighlightedRangeLine],
2874        bounds: RectF,
2875        cx: &mut WindowContext,
2876    ) {
2877        if lines.is_empty() {
2878            return;
2879        }
2880
2881        let mut path = PathBuilder::new();
2882        let first_line = lines.first().unwrap();
2883        let last_line = lines.last().unwrap();
2884
2885        let first_top_left = vec2f(first_line.start_x, start_y);
2886        let first_top_right = vec2f(first_line.end_x, start_y);
2887
2888        let curve_height = vec2f(0., self.corner_radius);
2889        let curve_width = |start_x: f32, end_x: f32| {
2890            let max = (end_x - start_x) / 2.;
2891            let width = if max < self.corner_radius {
2892                max
2893            } else {
2894                self.corner_radius
2895            };
2896
2897            vec2f(width, 0.)
2898        };
2899
2900        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
2901        path.reset(first_top_right - top_curve_width);
2902        path.curve_to(first_top_right + curve_height, first_top_right);
2903
2904        let mut iter = lines.iter().enumerate().peekable();
2905        while let Some((ix, line)) = iter.next() {
2906            let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
2907
2908            if let Some((_, next_line)) = iter.peek() {
2909                let next_top_right = vec2f(next_line.end_x, bottom_right.y());
2910
2911                match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
2912                    Ordering::Equal => {
2913                        path.line_to(bottom_right);
2914                    }
2915                    Ordering::Less => {
2916                        let curve_width = curve_width(next_top_right.x(), bottom_right.x());
2917                        path.line_to(bottom_right - curve_height);
2918                        if self.corner_radius > 0. {
2919                            path.curve_to(bottom_right - curve_width, bottom_right);
2920                        }
2921                        path.line_to(next_top_right + curve_width);
2922                        if self.corner_radius > 0. {
2923                            path.curve_to(next_top_right + curve_height, next_top_right);
2924                        }
2925                    }
2926                    Ordering::Greater => {
2927                        let curve_width = curve_width(bottom_right.x(), next_top_right.x());
2928                        path.line_to(bottom_right - curve_height);
2929                        if self.corner_radius > 0. {
2930                            path.curve_to(bottom_right + curve_width, bottom_right);
2931                        }
2932                        path.line_to(next_top_right - curve_width);
2933                        if self.corner_radius > 0. {
2934                            path.curve_to(next_top_right + curve_height, next_top_right);
2935                        }
2936                    }
2937                }
2938            } else {
2939                let curve_width = curve_width(line.start_x, line.end_x);
2940                path.line_to(bottom_right - curve_height);
2941                if self.corner_radius > 0. {
2942                    path.curve_to(bottom_right - curve_width, bottom_right);
2943                }
2944
2945                let bottom_left = vec2f(line.start_x, bottom_right.y());
2946                path.line_to(bottom_left + curve_width);
2947                if self.corner_radius > 0. {
2948                    path.curve_to(bottom_left - curve_height, bottom_left);
2949                }
2950            }
2951        }
2952
2953        if first_line.start_x > last_line.start_x {
2954            let curve_width = curve_width(last_line.start_x, first_line.start_x);
2955            let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
2956            path.line_to(second_top_left + curve_height);
2957            if self.corner_radius > 0. {
2958                path.curve_to(second_top_left + curve_width, second_top_left);
2959            }
2960            let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
2961            path.line_to(first_bottom_left - curve_width);
2962            if self.corner_radius > 0. {
2963                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
2964            }
2965        }
2966
2967        path.line_to(first_top_left + curve_height);
2968        if self.corner_radius > 0. {
2969            path.curve_to(first_top_left + top_curve_width, first_top_left);
2970        }
2971        path.line_to(first_top_right - top_curve_width);
2972
2973        cx.scene().push_path(path.build(self.color, Some(bounds)));
2974    }
2975}
2976
2977fn range_to_bounds(
2978    range: &Range<DisplayPoint>,
2979    content_origin: Vector2F,
2980    scroll_left: f32,
2981    scroll_top: f32,
2982    visible_row_range: &Range<u32>,
2983    line_end_overshoot: f32,
2984    position_map: &PositionMap,
2985) -> impl Iterator<Item = RectF> {
2986    let mut bounds: SmallVec<[RectF; 1]> = SmallVec::new();
2987
2988    if range.start == range.end {
2989        return bounds.into_iter();
2990    }
2991
2992    let start_row = visible_row_range.start;
2993    let end_row = visible_row_range.end;
2994
2995    let row_range = if range.end.column() == 0 {
2996        cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
2997    } else {
2998        cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
2999    };
3000
3001    let first_y =
3002        content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top;
3003
3004    for (idx, row) in row_range.enumerate() {
3005        let line_layout = &position_map.line_layouts[(row - start_row) as usize].line;
3006
3007        let start_x = if row == range.start.row() {
3008            content_origin.x() + line_layout.x_for_index(range.start.column() as usize)
3009                - scroll_left
3010        } else {
3011            content_origin.x() - scroll_left
3012        };
3013
3014        let end_x = if row == range.end.row() {
3015            content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left
3016        } else {
3017            content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left
3018        };
3019
3020        bounds.push(RectF::from_points(
3021            vec2f(start_x, first_y + position_map.line_height * idx as f32),
3022            vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32),
3023        ))
3024    }
3025
3026    bounds.into_iter()
3027}
3028
3029pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
3030    delta.powf(1.5) / 100.0
3031}
3032
3033fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
3034    delta.powf(1.2) / 300.0
3035}
3036
3037#[cfg(test)]
3038mod tests {
3039    use super::*;
3040    use crate::{
3041        display_map::{BlockDisposition, BlockProperties},
3042        editor_tests::{init_test, update_test_language_settings},
3043        Editor, MultiBuffer,
3044    };
3045    use gpui::TestAppContext;
3046    use language::language_settings;
3047    use log::info;
3048    use std::{num::NonZeroU32, sync::Arc};
3049    use util::test::sample_text;
3050
3051    #[gpui::test]
3052    fn test_layout_line_numbers(cx: &mut TestAppContext) {
3053        init_test(cx, |_| {});
3054        let editor = cx
3055            .add_window(|cx| {
3056                let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
3057                Editor::new(EditorMode::Full, buffer, None, None, cx)
3058            })
3059            .root(cx);
3060        let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3061
3062        let layouts = editor.update(cx, |editor, cx| {
3063            let snapshot = editor.snapshot(cx);
3064            element
3065                .layout_line_numbers(
3066                    0..6,
3067                    &Default::default(),
3068                    DisplayPoint::new(0, 0),
3069                    false,
3070                    &snapshot,
3071                    cx,
3072                )
3073                .0
3074        });
3075        assert_eq!(layouts.len(), 6);
3076
3077        let relative_rows = editor.update(cx, |editor, cx| {
3078            let snapshot = editor.snapshot(cx);
3079            element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3))
3080        });
3081        assert_eq!(relative_rows[&0], 3);
3082        assert_eq!(relative_rows[&1], 2);
3083        assert_eq!(relative_rows[&2], 1);
3084        // current line has no relative number
3085        assert_eq!(relative_rows[&4], 1);
3086        assert_eq!(relative_rows[&5], 2);
3087
3088        // works if cursor is before screen
3089        let relative_rows = editor.update(cx, |editor, cx| {
3090            let snapshot = editor.snapshot(cx);
3091
3092            element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1))
3093        });
3094        assert_eq!(relative_rows.len(), 3);
3095        assert_eq!(relative_rows[&3], 2);
3096        assert_eq!(relative_rows[&4], 3);
3097        assert_eq!(relative_rows[&5], 4);
3098
3099        // works if cursor is after screen
3100        let relative_rows = editor.update(cx, |editor, cx| {
3101            let snapshot = editor.snapshot(cx);
3102
3103            element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6))
3104        });
3105        assert_eq!(relative_rows.len(), 3);
3106        assert_eq!(relative_rows[&0], 5);
3107        assert_eq!(relative_rows[&1], 4);
3108        assert_eq!(relative_rows[&2], 3);
3109    }
3110
3111    #[gpui::test]
3112    async fn test_vim_visual_selections(cx: &mut TestAppContext) {
3113        init_test(cx, |_| {});
3114
3115        let editor = cx
3116            .add_window(|cx| {
3117                let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
3118                Editor::new(EditorMode::Full, buffer, None, None, cx)
3119            })
3120            .root(cx);
3121        let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3122        let (_, state) = editor.update(cx, |editor, cx| {
3123            editor.cursor_shape = CursorShape::Block;
3124            editor.change_selections(None, cx, |s| {
3125                s.select_ranges([
3126                    Point::new(0, 0)..Point::new(1, 0),
3127                    Point::new(3, 2)..Point::new(3, 3),
3128                    Point::new(5, 6)..Point::new(6, 0),
3129                ]);
3130            });
3131            element.layout(
3132                SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3133                editor,
3134                cx,
3135            )
3136        });
3137        assert_eq!(state.selections.len(), 1);
3138        let local_selections = &state.selections[0].1;
3139        assert_eq!(local_selections.len(), 3);
3140        // moves cursor back one line
3141        assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6));
3142        assert_eq!(
3143            local_selections[0].range,
3144            DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0)
3145        );
3146
3147        // moves cursor back one column
3148        assert_eq!(
3149            local_selections[1].range,
3150            DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3)
3151        );
3152        assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2));
3153
3154        // leaves cursor on the max point
3155        assert_eq!(
3156            local_selections[2].range,
3157            DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0)
3158        );
3159        assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0));
3160
3161        // active lines does not include 1 (even though the range of the selection does)
3162        assert_eq!(
3163            state.active_rows.keys().cloned().collect::<Vec<u32>>(),
3164            vec![0, 3, 5, 6]
3165        );
3166
3167        // multi-buffer support
3168        // in DisplayPoint co-ordinates, this is what we're dealing with:
3169        //  0: [[file
3170        //  1:   header]]
3171        //  2: aaaaaa
3172        //  3: bbbbbb
3173        //  4: cccccc
3174        //  5:
3175        //  6: ...
3176        //  7: ffffff
3177        //  8: gggggg
3178        //  9: hhhhhh
3179        // 10:
3180        // 11: [[file
3181        // 12:   header]]
3182        // 13: bbbbbb
3183        // 14: cccccc
3184        // 15: dddddd
3185        let editor = cx
3186            .add_window(|cx| {
3187                let buffer = MultiBuffer::build_multi(
3188                    [
3189                        (
3190                            &(sample_text(8, 6, 'a') + "\n"),
3191                            vec![
3192                                Point::new(0, 0)..Point::new(3, 0),
3193                                Point::new(4, 0)..Point::new(7, 0),
3194                            ],
3195                        ),
3196                        (
3197                            &(sample_text(8, 6, 'a') + "\n"),
3198                            vec![Point::new(1, 0)..Point::new(3, 0)],
3199                        ),
3200                    ],
3201                    cx,
3202                );
3203                Editor::new(EditorMode::Full, buffer, None, None, cx)
3204            })
3205            .root(cx);
3206        let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3207        let (_, state) = editor.update(cx, |editor, cx| {
3208            editor.cursor_shape = CursorShape::Block;
3209            editor.change_selections(None, cx, |s| {
3210                s.select_display_ranges([
3211                    DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0),
3212                    DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
3213                ]);
3214            });
3215            element.layout(
3216                SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3217                editor,
3218                cx,
3219            )
3220        });
3221
3222        assert_eq!(state.selections.len(), 1);
3223        let local_selections = &state.selections[0].1;
3224        assert_eq!(local_selections.len(), 2);
3225
3226        // moves cursor on excerpt boundary back a line
3227        // and doesn't allow selection to bleed through
3228        assert_eq!(
3229            local_selections[0].range,
3230            DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0)
3231        );
3232        assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0));
3233
3234        // moves cursor on buffer boundary back two lines
3235        // and doesn't allow selection to bleed through
3236        assert_eq!(
3237            local_selections[1].range,
3238            DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0)
3239        );
3240        assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0));
3241    }
3242
3243    #[gpui::test]
3244    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
3245        init_test(cx, |_| {});
3246
3247        let editor = cx
3248            .add_window(|cx| {
3249                let buffer = MultiBuffer::build_simple("", cx);
3250                Editor::new(EditorMode::Full, buffer, None, None, cx)
3251            })
3252            .root(cx);
3253
3254        editor.update(cx, |editor, cx| {
3255            editor.set_placeholder_text("hello", cx);
3256            editor.insert_blocks(
3257                [BlockProperties {
3258                    style: BlockStyle::Fixed,
3259                    disposition: BlockDisposition::Above,
3260                    height: 3,
3261                    position: Anchor::min(),
3262                    render: Arc::new(|_| Empty::new().into_any()),
3263                }],
3264                None,
3265                cx,
3266            );
3267
3268            // Blur the editor so that it displays placeholder text.
3269            cx.blur();
3270        });
3271
3272        let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3273        let (size, mut state) = editor.update(cx, |editor, cx| {
3274            element.layout(
3275                SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3276                editor,
3277                cx,
3278            )
3279        });
3280
3281        assert_eq!(state.position_map.line_layouts.len(), 4);
3282        assert_eq!(
3283            state
3284                .line_number_layouts
3285                .iter()
3286                .map(Option::is_some)
3287                .collect::<Vec<_>>(),
3288            &[false, false, false, true]
3289        );
3290
3291        // Don't panic.
3292        let bounds = RectF::new(Default::default(), size);
3293        editor.update(cx, |editor, cx| {
3294            element.paint(bounds, bounds, &mut state, editor, cx);
3295        });
3296    }
3297
3298    #[gpui::test]
3299    fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
3300        const TAB_SIZE: u32 = 4;
3301
3302        let input_text = "\t \t|\t| a b";
3303        let expected_invisibles = vec![
3304            Invisible::Tab {
3305                line_start_offset: 0,
3306            },
3307            Invisible::Whitespace {
3308                line_offset: TAB_SIZE as usize,
3309            },
3310            Invisible::Tab {
3311                line_start_offset: TAB_SIZE as usize + 1,
3312            },
3313            Invisible::Tab {
3314                line_start_offset: TAB_SIZE as usize * 2 + 1,
3315            },
3316            Invisible::Whitespace {
3317                line_offset: TAB_SIZE as usize * 3 + 1,
3318            },
3319            Invisible::Whitespace {
3320                line_offset: TAB_SIZE as usize * 3 + 3,
3321            },
3322        ];
3323        assert_eq!(
3324            expected_invisibles.len(),
3325            input_text
3326                .chars()
3327                .filter(|initial_char| initial_char.is_whitespace())
3328                .count(),
3329            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3330        );
3331
3332        init_test(cx, |s| {
3333            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3334            s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
3335        });
3336
3337        let actual_invisibles =
3338            collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0);
3339
3340        assert_eq!(expected_invisibles, actual_invisibles);
3341    }
3342
3343    #[gpui::test]
3344    fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
3345        init_test(cx, |s| {
3346            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3347            s.defaults.tab_size = NonZeroU32::new(4);
3348        });
3349
3350        for editor_mode_without_invisibles in [
3351            EditorMode::SingleLine,
3352            EditorMode::AutoHeight { max_lines: 100 },
3353        ] {
3354            let invisibles = collect_invisibles_from_new_editor(
3355                cx,
3356                editor_mode_without_invisibles,
3357                "\t\t\t| | a b",
3358                500.0,
3359            );
3360            assert!(invisibles.is_empty(),
3361                "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}");
3362        }
3363    }
3364
3365    #[gpui::test]
3366    fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) {
3367        let tab_size = 4;
3368        let input_text = "a\tbcd   ".repeat(9);
3369        let repeated_invisibles = [
3370            Invisible::Tab {
3371                line_start_offset: 1,
3372            },
3373            Invisible::Whitespace {
3374                line_offset: tab_size as usize + 3,
3375            },
3376            Invisible::Whitespace {
3377                line_offset: tab_size as usize + 4,
3378            },
3379            Invisible::Whitespace {
3380                line_offset: tab_size as usize + 5,
3381            },
3382        ];
3383        let expected_invisibles = std::iter::once(repeated_invisibles)
3384            .cycle()
3385            .take(9)
3386            .flatten()
3387            .collect::<Vec<_>>();
3388        assert_eq!(
3389            expected_invisibles.len(),
3390            input_text
3391                .chars()
3392                .filter(|initial_char| initial_char.is_whitespace())
3393                .count(),
3394            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3395        );
3396        info!("Expected invisibles: {expected_invisibles:?}");
3397
3398        init_test(cx, |_| {});
3399
3400        // Put the same string with repeating whitespace pattern into editors of various size,
3401        // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
3402        let resize_step = 10.0;
3403        let mut editor_width = 200.0;
3404        while editor_width <= 1000.0 {
3405            update_test_language_settings(cx, |s| {
3406                s.defaults.tab_size = NonZeroU32::new(tab_size);
3407                s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3408                s.defaults.preferred_line_length = Some(editor_width as u32);
3409                s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
3410            });
3411
3412            let actual_invisibles =
3413                collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, editor_width);
3414
3415            // Whatever the editor size is, ensure it has the same invisible kinds in the same order
3416            // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets).
3417            let mut i = 0;
3418            for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() {
3419                i = actual_index;
3420                match expected_invisibles.get(i) {
3421                    Some(expected_invisible) => match (expected_invisible, actual_invisible) {
3422                        (Invisible::Whitespace { .. }, Invisible::Whitespace { .. })
3423                        | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {}
3424                        _ => {
3425                            panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}")
3426                        }
3427                    },
3428                    None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"),
3429                }
3430            }
3431            let missing_expected_invisibles = &expected_invisibles[i + 1..];
3432            assert!(
3433                missing_expected_invisibles.is_empty(),
3434                "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}"
3435            );
3436
3437            editor_width += resize_step;
3438        }
3439    }
3440
3441    fn collect_invisibles_from_new_editor(
3442        cx: &mut TestAppContext,
3443        editor_mode: EditorMode,
3444        input_text: &str,
3445        editor_width: f32,
3446    ) -> Vec<Invisible> {
3447        info!(
3448            "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'"
3449        );
3450        let editor = cx
3451            .add_window(|cx| {
3452                let buffer = MultiBuffer::build_simple(&input_text, cx);
3453                Editor::new(editor_mode, buffer, None, None, cx)
3454            })
3455            .root(cx);
3456
3457        let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3458        let (_, layout_state) = editor.update(cx, |editor, cx| {
3459            editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
3460            editor.set_wrap_width(Some(editor_width), cx);
3461
3462            element.layout(
3463                SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)),
3464                editor,
3465                cx,
3466            )
3467        });
3468
3469        layout_state
3470            .position_map
3471            .line_layouts
3472            .iter()
3473            .map(|line_with_invisibles| &line_with_invisibles.invisibles)
3474            .flatten()
3475            .cloned()
3476            .collect()
3477    }
3478}