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, TransformBlock},
   8    git::{diff_hunk_to_display, DisplayDiffHunk},
   9    hover_popover::{
  10        hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
  11        MIN_POPOVER_LINE_HEIGHT,
  12    },
  13    link_go_to_definition::{
  14        go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
  15    },
  16    mouse_context_menu::DeployMouseContextMenu,
  17    scroll::actions::Scroll,
  18    EditorStyle, GutterHover, UnfoldAt,
  19};
  20use clock::ReplicaId;
  21use collections::{BTreeMap, HashMap};
  22use git::diff::DiffHunkStatus;
  23use gpui::{
  24    color::Color,
  25    elements::*,
  26    fonts::{HighlightStyle, Underline},
  27    geometry::{
  28        rect::RectF,
  29        vector::{vec2f, Vector2F},
  30        PathBuilder,
  31    },
  32    json::{self, ToJson},
  33    platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
  34    text_layout::{self, Line, RunStyle, TextLayoutCache},
  35    AnyElement, Axis, Border, CursorRegion, Element, EventContext, MouseRegion, Quad, SceneBuilder,
  36    SizeConstraint, ViewContext, WindowContext,
  37};
  38use itertools::Itertools;
  39use json::json;
  40use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection};
  41use project::ProjectPath;
  42use settings::{GitGutter, Settings};
  43use smallvec::SmallVec;
  44use std::{
  45    cmp::{self, Ordering},
  46    fmt::Write,
  47    iter,
  48    ops::Range,
  49    sync::Arc,
  50};
  51use workspace::item::Item;
  52
  53enum FoldMarkers {}
  54
  55struct SelectionLayout {
  56    head: DisplayPoint,
  57    cursor_shape: CursorShape,
  58    range: Range<DisplayPoint>,
  59}
  60
  61impl SelectionLayout {
  62    fn new<T: ToPoint + ToDisplayPoint + Clone>(
  63        selection: Selection<T>,
  64        line_mode: bool,
  65        cursor_shape: CursorShape,
  66        map: &DisplaySnapshot,
  67    ) -> Self {
  68        if line_mode {
  69            let selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
  70            let point_range = map.expand_to_line(selection.range());
  71            Self {
  72                head: selection.head().to_display_point(map),
  73                cursor_shape,
  74                range: point_range.start.to_display_point(map)
  75                    ..point_range.end.to_display_point(map),
  76            }
  77        } else {
  78            let selection = selection.map(|p| p.to_display_point(map));
  79            Self {
  80                head: selection.head(),
  81                cursor_shape,
  82                range: selection.range(),
  83            }
  84        }
  85    }
  86}
  87
  88#[derive(Clone)]
  89pub struct EditorElement {
  90    style: Arc<EditorStyle>,
  91}
  92
  93impl EditorElement {
  94    pub fn new(style: EditorStyle) -> Self {
  95        Self {
  96            style: Arc::new(style),
  97        }
  98    }
  99
 100    fn attach_mouse_handlers(
 101        scene: &mut SceneBuilder,
 102        position_map: &Arc<PositionMap>,
 103        has_popovers: bool,
 104        visible_bounds: RectF,
 105        text_bounds: RectF,
 106        gutter_bounds: RectF,
 107        bounds: RectF,
 108        cx: &mut ViewContext<Editor>,
 109    ) {
 110        enum EditorElementMouseHandlers {}
 111        scene.push_mouse_region(
 112            MouseRegion::new::<EditorElementMouseHandlers>(
 113                cx.view_id(),
 114                cx.view_id(),
 115                visible_bounds,
 116            )
 117            .on_down(MouseButton::Left, {
 118                let position_map = position_map.clone();
 119                move |event, editor, cx| {
 120                    if !Self::mouse_down(
 121                        editor,
 122                        event.platform_event,
 123                        position_map.as_ref(),
 124                        text_bounds,
 125                        gutter_bounds,
 126                        cx,
 127                    ) {
 128                        cx.propagate_event();
 129                    }
 130                }
 131            })
 132            .on_down(MouseButton::Right, {
 133                let position_map = position_map.clone();
 134                move |event, _editor, cx| {
 135                    if !Self::mouse_right_down(
 136                        event.position,
 137                        position_map.as_ref(),
 138                        text_bounds,
 139                        cx,
 140                    ) {
 141                        cx.propagate_event();
 142                    }
 143                }
 144            })
 145            .on_up(MouseButton::Left, {
 146                let position_map = position_map.clone();
 147                move |event, editor, cx| {
 148                    if !Self::mouse_up(
 149                        editor,
 150                        event.position,
 151                        event.cmd,
 152                        event.shift,
 153                        position_map.as_ref(),
 154                        text_bounds,
 155                        cx,
 156                    ) {
 157                        cx.propagate_event()
 158                    }
 159                }
 160            })
 161            .on_drag(MouseButton::Left, {
 162                let position_map = position_map.clone();
 163                move |event, editor, cx| {
 164                    if !Self::mouse_dragged(
 165                        editor,
 166                        event.platform_event,
 167                        position_map.as_ref(),
 168                        text_bounds,
 169                        cx,
 170                    ) {
 171                        cx.propagate_event()
 172                    }
 173                }
 174            })
 175            .on_move({
 176                let position_map = position_map.clone();
 177                move |event, editor, cx| {
 178                    if !Self::mouse_moved(
 179                        editor,
 180                        event.platform_event,
 181                        &position_map,
 182                        text_bounds,
 183                        cx,
 184                    ) {
 185                        cx.propagate_event()
 186                    }
 187                }
 188            })
 189            .on_move_out(move |_, editor: &mut Editor, cx| {
 190                if has_popovers {
 191                    hide_hover(editor, cx);
 192                }
 193            })
 194            .on_scroll({
 195                let position_map = position_map.clone();
 196                move |e, _, cx| {
 197                    if !Self::scroll(
 198                        e.position,
 199                        *e.delta.raw(),
 200                        e.delta.precise(),
 201                        &position_map,
 202                        bounds,
 203                        cx,
 204                    ) {
 205                        cx.propagate_event()
 206                    }
 207                }
 208            }),
 209        );
 210
 211        enum GutterHandlers {}
 212        scene.push_mouse_region(
 213            MouseRegion::new::<GutterHandlers>(cx.view_id(), cx.view_id() + 1, gutter_bounds)
 214                .on_hover(|hover, _: &mut Editor, cx| {
 215                    cx.dispatch_action(GutterHover {
 216                        hovered: hover.started,
 217                    })
 218                }),
 219        )
 220    }
 221
 222    fn mouse_down(
 223        editor: &mut Editor,
 224        MouseButtonEvent {
 225            position,
 226            modifiers:
 227                Modifiers {
 228                    shift,
 229                    ctrl,
 230                    alt,
 231                    cmd,
 232                    ..
 233                },
 234            mut click_count,
 235            ..
 236        }: MouseButtonEvent,
 237        position_map: &PositionMap,
 238        text_bounds: RectF,
 239        gutter_bounds: RectF,
 240        cx: &mut EventContext<Editor>,
 241    ) -> bool {
 242        if gutter_bounds.contains_point(position) {
 243            click_count = 3; // Simulate triple-click when clicking the gutter to select lines
 244        } else if !text_bounds.contains_point(position) {
 245            return false;
 246        }
 247
 248        let (position, target_position) = position_map.point_for_position(text_bounds, position);
 249
 250        if shift && alt {
 251            editor.select(
 252                SelectPhase::BeginColumnar {
 253                    position,
 254                    goal_column: target_position.column(),
 255                },
 256                cx,
 257            );
 258        } else if shift && !ctrl && !alt && !cmd {
 259            editor.select(
 260                SelectPhase::Extend {
 261                    position,
 262                    click_count,
 263                },
 264                cx,
 265            );
 266        } else {
 267            editor.select(
 268                SelectPhase::Begin {
 269                    position,
 270                    add: alt,
 271                    click_count,
 272                },
 273                cx,
 274            );
 275        }
 276
 277        true
 278    }
 279
 280    fn mouse_right_down(
 281        position: Vector2F,
 282        position_map: &PositionMap,
 283        text_bounds: RectF,
 284        cx: &mut EventContext<Editor>,
 285    ) -> bool {
 286        if !text_bounds.contains_point(position) {
 287            return false;
 288        }
 289
 290        let (point, _) = position_map.point_for_position(text_bounds, position);
 291
 292        cx.dispatch_action(DeployMouseContextMenu { position, point });
 293        true
 294    }
 295
 296    fn mouse_up(
 297        editor: &mut Editor,
 298        position: Vector2F,
 299        cmd: bool,
 300        shift: bool,
 301        position_map: &PositionMap,
 302        text_bounds: RectF,
 303        cx: &mut EventContext<Editor>,
 304    ) -> bool {
 305        let end_selection = editor.has_pending_selection();
 306        let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 307
 308        if end_selection {
 309            editor.select(SelectPhase::End, cx);
 310        }
 311
 312        if let Some(workspace) = editor
 313            .workspace
 314            .as_ref()
 315            .and_then(|(workspace, _)| workspace.upgrade(cx))
 316        {
 317            if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) {
 318                let (point, target_point) = position_map.point_for_position(text_bounds, position);
 319
 320                if point == target_point {
 321                    workspace.update(cx, |workspace, cx| {
 322                        if shift {
 323                            go_to_fetched_type_definition(workspace, point, cx);
 324                        } else {
 325                            go_to_fetched_definition(workspace, point, cx);
 326                        }
 327                    });
 328
 329                    return true;
 330                }
 331            }
 332        }
 333
 334        end_selection
 335    }
 336
 337    fn mouse_dragged(
 338        editor: &mut Editor,
 339        MouseMovedEvent {
 340            modifiers: Modifiers { cmd, shift, .. },
 341            position,
 342            ..
 343        }: MouseMovedEvent,
 344        position_map: &PositionMap,
 345        text_bounds: RectF,
 346        cx: &mut EventContext<Editor>,
 347    ) -> bool {
 348        // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
 349        // Don't trigger hover popover if mouse is hovering over context menu
 350        let point = if text_bounds.contains_point(position) {
 351            let (point, target_point) = position_map.point_for_position(text_bounds, position);
 352            if point == target_point {
 353                Some(point)
 354            } else {
 355                None
 356            }
 357        } else {
 358            None
 359        };
 360
 361        update_go_to_definition_link(editor, point, cmd, shift, cx);
 362
 363        if editor.has_pending_selection() {
 364            let mut scroll_delta = Vector2F::zero();
 365
 366            let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0);
 367            let top = text_bounds.origin_y() + vertical_margin;
 368            let bottom = text_bounds.lower_left().y() - vertical_margin;
 369            if position.y() < top {
 370                scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y()))
 371            }
 372            if position.y() > bottom {
 373                scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom))
 374            }
 375
 376            let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0);
 377            let left = text_bounds.origin_x() + horizontal_margin;
 378            let right = text_bounds.upper_right().x() - horizontal_margin;
 379            if position.x() < left {
 380                scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta(
 381                    left - position.x(),
 382                ))
 383            }
 384            if position.x() > right {
 385                scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta(
 386                    position.x() - right,
 387                ))
 388            }
 389
 390            let (position, target_position) =
 391                position_map.point_for_position(text_bounds, position);
 392
 393            editor.select(
 394                SelectPhase::Update {
 395                    position,
 396                    goal_column: target_position.column(),
 397                    scroll_position: (position_map.snapshot.scroll_position() + scroll_delta)
 398                        .clamp(Vector2F::zero(), position_map.scroll_max),
 399                },
 400                cx,
 401            );
 402            hover_at(editor, point, cx);
 403            true
 404        } else {
 405            hover_at(editor, point, cx);
 406            false
 407        }
 408    }
 409
 410    fn mouse_moved(
 411        editor: &mut Editor,
 412        MouseMovedEvent {
 413            modifiers: Modifiers { shift, cmd, .. },
 414            position,
 415            ..
 416        }: MouseMovedEvent,
 417        position_map: &PositionMap,
 418        text_bounds: RectF,
 419        cx: &mut ViewContext<Editor>,
 420    ) -> bool {
 421        // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
 422        // Don't trigger hover popover if mouse is hovering over context menu
 423        let point = position_to_display_point(position, text_bounds, position_map);
 424
 425        update_go_to_definition_link(editor, point, cmd, shift, cx);
 426        hover_at(editor, point, cx);
 427
 428        true
 429    }
 430
 431    fn scroll(
 432        position: Vector2F,
 433        mut delta: Vector2F,
 434        precise: bool,
 435        position_map: &PositionMap,
 436        bounds: RectF,
 437        cx: &mut ViewContext<Editor>,
 438    ) -> bool {
 439        if !bounds.contains_point(position) {
 440            return false;
 441        }
 442
 443        let line_height = position_map.line_height;
 444        let max_glyph_width = position_map.em_width;
 445
 446        let axis = if precise {
 447            //Trackpad
 448            position_map.snapshot.ongoing_scroll.filter(&mut delta)
 449        } else {
 450            //Not trackpad
 451            delta *= vec2f(max_glyph_width, line_height);
 452            None //Resets ongoing scroll
 453        };
 454
 455        let scroll_position = position_map.snapshot.scroll_position();
 456        let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width;
 457        let y = (scroll_position.y() * line_height - delta.y()) / line_height;
 458        let scroll_position = vec2f(x, y).clamp(Vector2F::zero(), position_map.scroll_max);
 459
 460        cx.dispatch_action(Scroll {
 461            scroll_position,
 462            axis,
 463        });
 464
 465        true
 466    }
 467
 468    fn paint_background(
 469        &self,
 470        scene: &mut SceneBuilder,
 471        gutter_bounds: RectF,
 472        text_bounds: RectF,
 473        layout: &LayoutState,
 474    ) {
 475        let bounds = gutter_bounds.union_rect(text_bounds);
 476        let scroll_top =
 477            layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height;
 478        scene.push_quad(Quad {
 479            bounds: gutter_bounds,
 480            background: Some(self.style.gutter_background),
 481            border: Border::new(0., Color::transparent_black()),
 482            corner_radius: 0.,
 483        });
 484        scene.push_quad(Quad {
 485            bounds: text_bounds,
 486            background: Some(self.style.background),
 487            border: Border::new(0., Color::transparent_black()),
 488            corner_radius: 0.,
 489        });
 490
 491        if let EditorMode::Full = layout.mode {
 492            let mut active_rows = layout.active_rows.iter().peekable();
 493            while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
 494                let mut end_row = *start_row;
 495                while active_rows.peek().map_or(false, |r| {
 496                    *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
 497                }) {
 498                    active_rows.next().unwrap();
 499                    end_row += 1;
 500                }
 501
 502                if !contains_non_empty_selection {
 503                    let origin = vec2f(
 504                        bounds.origin_x(),
 505                        bounds.origin_y() + (layout.position_map.line_height * *start_row as f32)
 506                            - scroll_top,
 507                    );
 508                    let size = vec2f(
 509                        bounds.width(),
 510                        layout.position_map.line_height * (end_row - start_row + 1) as f32,
 511                    );
 512                    scene.push_quad(Quad {
 513                        bounds: RectF::new(origin, size),
 514                        background: Some(self.style.active_line_background),
 515                        border: Border::default(),
 516                        corner_radius: 0.,
 517                    });
 518                }
 519            }
 520
 521            if let Some(highlighted_rows) = &layout.highlighted_rows {
 522                let origin = vec2f(
 523                    bounds.origin_x(),
 524                    bounds.origin_y()
 525                        + (layout.position_map.line_height * highlighted_rows.start as f32)
 526                        - scroll_top,
 527                );
 528                let size = vec2f(
 529                    bounds.width(),
 530                    layout.position_map.line_height * highlighted_rows.len() as f32,
 531                );
 532                scene.push_quad(Quad {
 533                    bounds: RectF::new(origin, size),
 534                    background: Some(self.style.highlighted_line_background),
 535                    border: Border::default(),
 536                    corner_radius: 0.,
 537                });
 538            }
 539        }
 540    }
 541
 542    fn paint_gutter(
 543        &mut self,
 544        scene: &mut SceneBuilder,
 545        bounds: RectF,
 546        visible_bounds: RectF,
 547        layout: &mut LayoutState,
 548        editor: &mut Editor,
 549        cx: &mut ViewContext<Editor>,
 550    ) {
 551        let line_height = layout.position_map.line_height;
 552
 553        let scroll_position = layout.position_map.snapshot.scroll_position();
 554        let scroll_top = scroll_position.y() * line_height;
 555
 556        let show_gutter = matches!(
 557            &cx.global::<Settings>()
 558                .git_overrides
 559                .git_gutter
 560                .unwrap_or_default(),
 561            GitGutter::TrackedFiles
 562        );
 563
 564        if show_gutter {
 565            Self::paint_diff_hunks(scene, bounds, layout, cx);
 566        }
 567
 568        for (ix, line) in layout.line_number_layouts.iter().enumerate() {
 569            if let Some(line) = line {
 570                let line_origin = bounds.origin()
 571                    + vec2f(
 572                        bounds.width() - line.width() - layout.gutter_padding,
 573                        ix as f32 * line_height - (scroll_top % line_height),
 574                    );
 575
 576                line.paint(scene, line_origin, visible_bounds, line_height, cx);
 577            }
 578        }
 579
 580        for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() {
 581            if let Some(indicator) = fold_indicator.as_mut() {
 582                let position = vec2f(
 583                    bounds.width() - layout.gutter_padding,
 584                    ix as f32 * line_height - (scroll_top % line_height),
 585                );
 586                let centering_offset = vec2f(
 587                    (layout.gutter_padding + layout.gutter_margin - indicator.size().x()) / 2.,
 588                    (line_height - indicator.size().y()) / 2.,
 589                );
 590
 591                let indicator_origin = bounds.origin() + position + centering_offset;
 592
 593                indicator.paint(scene, indicator_origin, visible_bounds, editor, cx);
 594            }
 595        }
 596
 597        if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() {
 598            let mut x = 0.;
 599            let mut y = *row as f32 * line_height - scroll_top;
 600            x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.;
 601            y += (line_height - indicator.size().y()) / 2.;
 602            indicator.paint(
 603                scene,
 604                bounds.origin() + vec2f(x, y),
 605                visible_bounds,
 606                editor,
 607                cx,
 608            );
 609        }
 610    }
 611
 612    fn paint_diff_hunks(
 613        scene: &mut SceneBuilder,
 614        bounds: RectF,
 615        layout: &mut LayoutState,
 616        cx: &mut ViewContext<Editor>,
 617    ) {
 618        let diff_style = &cx.global::<Settings>().theme.editor.diff.clone();
 619        let line_height = layout.position_map.line_height;
 620
 621        let scroll_position = layout.position_map.snapshot.scroll_position();
 622        let scroll_top = scroll_position.y() * line_height;
 623
 624        for hunk in &layout.display_hunks {
 625            let (display_row_range, status) = match hunk {
 626                //TODO: This rendering is entirely a horrible hack
 627                &DisplayDiffHunk::Folded { display_row: row } => {
 628                    let start_y = row as f32 * line_height - scroll_top;
 629                    let end_y = start_y + line_height;
 630
 631                    let width = diff_style.removed_width_em * line_height;
 632                    let highlight_origin = bounds.origin() + vec2f(-width, start_y);
 633                    let highlight_size = vec2f(width * 2., end_y - start_y);
 634                    let highlight_bounds = RectF::new(highlight_origin, highlight_size);
 635
 636                    scene.push_quad(Quad {
 637                        bounds: highlight_bounds,
 638                        background: Some(diff_style.modified),
 639                        border: Border::new(0., Color::transparent_black()),
 640                        corner_radius: 1. * line_height,
 641                    });
 642
 643                    continue;
 644                }
 645
 646                DisplayDiffHunk::Unfolded {
 647                    display_row_range,
 648                    status,
 649                } => (display_row_range, status),
 650            };
 651
 652            let color = match status {
 653                DiffHunkStatus::Added => diff_style.inserted,
 654                DiffHunkStatus::Modified => diff_style.modified,
 655
 656                //TODO: This rendering is entirely a horrible hack
 657                DiffHunkStatus::Removed => {
 658                    let row = *display_row_range.start();
 659
 660                    let offset = line_height / 2.;
 661                    let start_y = row as f32 * line_height - offset - scroll_top;
 662                    let end_y = start_y + line_height;
 663
 664                    let width = diff_style.removed_width_em * line_height;
 665                    let highlight_origin = bounds.origin() + vec2f(-width, start_y);
 666                    let highlight_size = vec2f(width * 2., end_y - start_y);
 667                    let highlight_bounds = RectF::new(highlight_origin, highlight_size);
 668
 669                    scene.push_quad(Quad {
 670                        bounds: highlight_bounds,
 671                        background: Some(diff_style.deleted),
 672                        border: Border::new(0., Color::transparent_black()),
 673                        corner_radius: 1. * line_height,
 674                    });
 675
 676                    continue;
 677                }
 678            };
 679
 680            let start_row = *display_row_range.start();
 681            let end_row = *display_row_range.end();
 682
 683            let start_y = start_row as f32 * line_height - scroll_top;
 684            let end_y = end_row as f32 * line_height - scroll_top + line_height;
 685
 686            let width = diff_style.width_em * line_height;
 687            let highlight_origin = bounds.origin() + vec2f(-width, start_y);
 688            let highlight_size = vec2f(width * 2., end_y - start_y);
 689            let highlight_bounds = RectF::new(highlight_origin, highlight_size);
 690
 691            scene.push_quad(Quad {
 692                bounds: highlight_bounds,
 693                background: Some(color),
 694                border: Border::new(0., Color::transparent_black()),
 695                corner_radius: diff_style.corner_radius * line_height,
 696            });
 697        }
 698    }
 699
 700    fn paint_text(
 701        &mut self,
 702        scene: &mut SceneBuilder,
 703        bounds: RectF,
 704        visible_bounds: RectF,
 705        layout: &mut LayoutState,
 706        editor: &mut Editor,
 707        cx: &mut ViewContext<Editor>,
 708    ) {
 709        let style = &self.style;
 710        let local_replica_id = editor.replica_id(cx);
 711        let scroll_position = layout.position_map.snapshot.scroll_position();
 712        let start_row = layout.visible_display_row_range.start;
 713        let scroll_top = scroll_position.y() * layout.position_map.line_height;
 714        let max_glyph_width = layout.position_map.em_width;
 715        let scroll_left = scroll_position.x() * max_glyph_width;
 716        let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.);
 717        let line_end_overshoot = 0.15 * layout.position_map.line_height;
 718
 719        scene.push_layer(Some(bounds));
 720
 721        scene.push_cursor_region(CursorRegion {
 722            bounds,
 723            style: if !editor.link_go_to_definition_state.definitions.is_empty() {
 724                CursorStyle::PointingHand
 725            } else {
 726                CursorStyle::IBeam
 727            },
 728        });
 729
 730        let fold_corner_radius =
 731            self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height;
 732        for (id, range, color) in layout.fold_ranges.iter() {
 733            self.paint_highlighted_range(
 734                scene,
 735                range.clone(),
 736                *color,
 737                fold_corner_radius,
 738                fold_corner_radius * 2.,
 739                layout,
 740                content_origin,
 741                scroll_top,
 742                scroll_left,
 743                bounds,
 744            );
 745
 746            for bound in range_to_bounds(
 747                &range,
 748                content_origin,
 749                scroll_left,
 750                scroll_top,
 751                &layout.visible_display_row_range,
 752                line_end_overshoot,
 753                &layout.position_map,
 754            ) {
 755                scene.push_cursor_region(CursorRegion {
 756                    bounds: bound,
 757                    style: CursorStyle::PointingHand,
 758                });
 759
 760                let display_row = range.start.row();
 761
 762                let buffer_row = DisplayPoint::new(display_row, 0)
 763                    .to_point(&layout.position_map.snapshot.display_snapshot)
 764                    .row;
 765
 766                scene.push_mouse_region(
 767                    MouseRegion::new::<FoldMarkers>(cx.view_id(), *id as usize, bound)
 768                        .on_click(MouseButton::Left, move |_, _: &mut Editor, cx| {
 769                            cx.dispatch_action(UnfoldAt { buffer_row })
 770                        })
 771                        .with_notify_on_hover(true)
 772                        .with_notify_on_click(true),
 773                )
 774            }
 775        }
 776
 777        for (range, color) in &layout.highlighted_ranges {
 778            self.paint_highlighted_range(
 779                scene,
 780                range.clone(),
 781                *color,
 782                0.,
 783                line_end_overshoot,
 784                layout,
 785                content_origin,
 786                scroll_top,
 787                scroll_left,
 788                bounds,
 789            );
 790        }
 791
 792        let mut cursors = SmallVec::<[Cursor; 32]>::new();
 793        let corner_radius = 0.15 * layout.position_map.line_height;
 794
 795        for (replica_id, selections) in &layout.selections {
 796            let selection_style = style.replica_selection_style(*replica_id);
 797
 798            for selection in selections {
 799                self.paint_highlighted_range(
 800                    scene,
 801                    selection.range.clone(),
 802                    selection_style.selection,
 803                    corner_radius,
 804                    corner_radius * 2.,
 805                    layout,
 806                    content_origin,
 807                    scroll_top,
 808                    scroll_left,
 809                    bounds,
 810                );
 811
 812                if editor.show_local_cursors(cx) || *replica_id != local_replica_id {
 813                    let cursor_position = selection.head;
 814                    if layout
 815                        .visible_display_row_range
 816                        .contains(&cursor_position.row())
 817                    {
 818                        let cursor_row_layout = &layout.position_map.line_layouts
 819                            [(cursor_position.row() - start_row) as usize];
 820                        let cursor_column = cursor_position.column() as usize;
 821
 822                        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 823                        let mut block_width =
 824                            cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x;
 825                        if block_width == 0.0 {
 826                            block_width = layout.position_map.em_width;
 827                        }
 828                        let block_text = if let CursorShape::Block = selection.cursor_shape {
 829                            layout
 830                                .position_map
 831                                .snapshot
 832                                .chars_at(cursor_position)
 833                                .next()
 834                                .and_then(|(character, _)| {
 835                                    let font_id =
 836                                        cursor_row_layout.font_for_index(cursor_column)?;
 837                                    let text = character.to_string();
 838
 839                                    Some(cx.text_layout_cache().layout_str(
 840                                        &text,
 841                                        cursor_row_layout.font_size(),
 842                                        &[(
 843                                            text.len(),
 844                                            RunStyle {
 845                                                font_id,
 846                                                color: style.background,
 847                                                underline: Default::default(),
 848                                            },
 849                                        )],
 850                                    ))
 851                                })
 852                        } else {
 853                            None
 854                        };
 855
 856                        let x = cursor_character_x - scroll_left;
 857                        let y = cursor_position.row() as f32 * layout.position_map.line_height
 858                            - scroll_top;
 859                        cursors.push(Cursor {
 860                            color: selection_style.cursor,
 861                            block_width,
 862                            origin: vec2f(x, y),
 863                            line_height: layout.position_map.line_height,
 864                            shape: selection.cursor_shape,
 865                            block_text,
 866                        });
 867                    }
 868                }
 869            }
 870        }
 871
 872        if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) {
 873            // Draw glyphs
 874            for (ix, line) in layout.position_map.line_layouts.iter().enumerate() {
 875                let row = start_row + ix as u32;
 876                line.paint(
 877                    scene,
 878                    content_origin
 879                        + vec2f(
 880                            -scroll_left,
 881                            row as f32 * layout.position_map.line_height - scroll_top,
 882                        ),
 883                    visible_text_bounds,
 884                    layout.position_map.line_height,
 885                    cx,
 886                );
 887            }
 888        }
 889
 890        scene.paint_layer(Some(bounds), |scene| {
 891            for cursor in cursors {
 892                cursor.paint(scene, content_origin, cx);
 893            }
 894        });
 895
 896        if let Some((position, context_menu)) = layout.context_menu.as_mut() {
 897            scene.push_stacking_context(None, None);
 898            let cursor_row_layout =
 899                &layout.position_map.line_layouts[(position.row() - start_row) as usize];
 900            let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
 901            let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top;
 902            let mut list_origin = content_origin + vec2f(x, y);
 903            let list_width = context_menu.size().x();
 904            let list_height = context_menu.size().y();
 905
 906            // Snap the right edge of the list to the right edge of the window if
 907            // its horizontal bounds overflow.
 908            if list_origin.x() + list_width > cx.window_size().x() {
 909                list_origin.set_x((cx.window_size().x() - list_width).max(0.));
 910            }
 911
 912            if list_origin.y() + list_height > bounds.max_y() {
 913                list_origin.set_y(list_origin.y() - layout.position_map.line_height - list_height);
 914            }
 915
 916            context_menu.paint(
 917                scene,
 918                list_origin,
 919                RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
 920                editor,
 921                cx,
 922            );
 923
 924            scene.pop_stacking_context();
 925        }
 926
 927        if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
 928            scene.push_stacking_context(None, None);
 929
 930            // This is safe because we check on layout whether the required row is available
 931            let hovered_row_layout =
 932                &layout.position_map.line_layouts[(position.row() - start_row) as usize];
 933
 934            // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
 935            // height. This is the size we will use to decide whether to render popovers above or below
 936            // the hovered line.
 937            let first_size = hover_popovers[0].size();
 938            let height_to_reserve = first_size.y()
 939                + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height;
 940
 941            // Compute Hovered Point
 942            let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left;
 943            let y = position.row() as f32 * layout.position_map.line_height - scroll_top;
 944            let hovered_point = content_origin + vec2f(x, y);
 945
 946            if hovered_point.y() - height_to_reserve > 0.0 {
 947                // There is enough space above. Render popovers above the hovered point
 948                let mut current_y = hovered_point.y();
 949                for hover_popover in hover_popovers {
 950                    let size = hover_popover.size();
 951                    let mut popover_origin = vec2f(hovered_point.x(), current_y - size.y());
 952
 953                    let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x());
 954                    if x_out_of_bounds < 0.0 {
 955                        popover_origin.set_x(popover_origin.x() + x_out_of_bounds);
 956                    }
 957
 958                    hover_popover.paint(
 959                        scene,
 960                        popover_origin,
 961                        RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
 962                        editor,
 963                        cx,
 964                    );
 965
 966                    current_y = popover_origin.y() - HOVER_POPOVER_GAP;
 967                }
 968            } else {
 969                // There is not enough space above. Render popovers below the hovered point
 970                let mut current_y = hovered_point.y() + layout.position_map.line_height;
 971                for hover_popover in hover_popovers {
 972                    let size = hover_popover.size();
 973                    let mut popover_origin = vec2f(hovered_point.x(), current_y);
 974
 975                    let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x());
 976                    if x_out_of_bounds < 0.0 {
 977                        popover_origin.set_x(popover_origin.x() + x_out_of_bounds);
 978                    }
 979
 980                    hover_popover.paint(
 981                        scene,
 982                        popover_origin,
 983                        RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
 984                        editor,
 985                        cx,
 986                    );
 987
 988                    current_y = popover_origin.y() + size.y() + HOVER_POPOVER_GAP;
 989                }
 990            }
 991
 992            scene.pop_stacking_context();
 993        }
 994
 995        scene.pop_layer();
 996    }
 997
 998    fn paint_scrollbar(
 999        &mut self,
1000        scene: &mut SceneBuilder,
1001        bounds: RectF,
1002        layout: &mut LayoutState,
1003        cx: &mut ViewContext<Editor>,
1004    ) {
1005        enum ScrollbarMouseHandlers {}
1006        if layout.mode != EditorMode::Full {
1007            return;
1008        }
1009
1010        let style = &self.style.theme.scrollbar;
1011
1012        let top = bounds.min_y();
1013        let bottom = bounds.max_y();
1014        let right = bounds.max_x();
1015        let left = right - style.width;
1016        let row_range = &layout.scrollbar_row_range;
1017        let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
1018
1019        let mut height = bounds.height();
1020        let mut first_row_y_offset = 0.0;
1021
1022        // Impose a minimum height on the scrollbar thumb
1023        let min_thumb_height =
1024            style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size);
1025        let thumb_height = (row_range.end - row_range.start) * height / max_row;
1026        if thumb_height < min_thumb_height {
1027            first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
1028            height -= min_thumb_height - thumb_height;
1029        }
1030
1031        let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * height / max_row };
1032
1033        let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
1034        let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
1035        let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom));
1036        let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom));
1037
1038        if layout.show_scrollbars {
1039            scene.push_quad(Quad {
1040                bounds: track_bounds,
1041                border: style.track.border,
1042                background: style.track.background_color,
1043                ..Default::default()
1044            });
1045            scene.push_quad(Quad {
1046                bounds: thumb_bounds,
1047                border: style.thumb.border,
1048                background: style.thumb.background_color,
1049                corner_radius: style.thumb.corner_radius,
1050            });
1051        }
1052
1053        scene.push_cursor_region(CursorRegion {
1054            bounds: track_bounds,
1055            style: CursorStyle::Arrow,
1056        });
1057        scene.push_mouse_region(
1058            MouseRegion::new::<ScrollbarMouseHandlers>(cx.view_id(), cx.view_id(), track_bounds)
1059                .on_move(move |_, editor: &mut Editor, cx| {
1060                    editor.scroll_manager.show_scrollbar(cx);
1061                })
1062                .on_down(MouseButton::Left, {
1063                    let row_range = row_range.clone();
1064                    move |event, editor: &mut Editor, cx| {
1065                        let y = event.position.y();
1066                        if y < thumb_top || thumb_bottom < y {
1067                            let center_row = ((y - top) * max_row as f32 / height).round() as u32;
1068                            let top_row = center_row
1069                                .saturating_sub((row_range.end - row_range.start) as u32 / 2);
1070                            let mut position = editor.scroll_position(cx);
1071                            position.set_y(top_row as f32);
1072                            editor.set_scroll_position(position, cx);
1073                        } else {
1074                            editor.scroll_manager.show_scrollbar(cx);
1075                        }
1076                    }
1077                })
1078                .on_drag(MouseButton::Left, {
1079                    move |event, editor: &mut Editor, cx| {
1080                        let y = event.prev_mouse_position.y();
1081                        let new_y = event.position.y();
1082                        if thumb_top < y && y < thumb_bottom {
1083                            let mut position = editor.scroll_position(cx);
1084                            position.set_y(position.y() + (new_y - y) * (max_row as f32) / height);
1085                            if position.y() < 0.0 {
1086                                position.set_y(0.);
1087                            }
1088                            editor.set_scroll_position(position, cx);
1089                        }
1090                    }
1091                }),
1092        );
1093    }
1094
1095    #[allow(clippy::too_many_arguments)]
1096    fn paint_highlighted_range(
1097        &self,
1098        scene: &mut SceneBuilder,
1099        range: Range<DisplayPoint>,
1100        color: Color,
1101        corner_radius: f32,
1102        line_end_overshoot: f32,
1103        layout: &LayoutState,
1104        content_origin: Vector2F,
1105        scroll_top: f32,
1106        scroll_left: f32,
1107        bounds: RectF,
1108    ) {
1109        let start_row = layout.visible_display_row_range.start;
1110        let end_row = layout.visible_display_row_range.end;
1111        if range.start != range.end {
1112            let row_range = if range.end.column() == 0 {
1113                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
1114            } else {
1115                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
1116            };
1117
1118            let highlighted_range = HighlightedRange {
1119                color,
1120                line_height: layout.position_map.line_height,
1121                corner_radius,
1122                start_y: content_origin.y()
1123                    + row_range.start as f32 * layout.position_map.line_height
1124                    - scroll_top,
1125                lines: row_range
1126                    .into_iter()
1127                    .map(|row| {
1128                        let line_layout =
1129                            &layout.position_map.line_layouts[(row - start_row) as usize];
1130                        HighlightedRangeLine {
1131                            start_x: if row == range.start.row() {
1132                                content_origin.x()
1133                                    + line_layout.x_for_index(range.start.column() as usize)
1134                                    - scroll_left
1135                            } else {
1136                                content_origin.x() - scroll_left
1137                            },
1138                            end_x: if row == range.end.row() {
1139                                content_origin.x()
1140                                    + line_layout.x_for_index(range.end.column() as usize)
1141                                    - scroll_left
1142                            } else {
1143                                content_origin.x() + line_layout.width() + line_end_overshoot
1144                                    - scroll_left
1145                            },
1146                        }
1147                    })
1148                    .collect(),
1149            };
1150
1151            highlighted_range.paint(bounds, scene);
1152        }
1153    }
1154
1155    fn paint_blocks(
1156        &mut self,
1157        scene: &mut SceneBuilder,
1158        bounds: RectF,
1159        visible_bounds: RectF,
1160        layout: &mut LayoutState,
1161        editor: &mut Editor,
1162        cx: &mut ViewContext<Editor>,
1163    ) {
1164        let scroll_position = layout.position_map.snapshot.scroll_position();
1165        let scroll_left = scroll_position.x() * layout.position_map.em_width;
1166        let scroll_top = scroll_position.y() * layout.position_map.line_height;
1167
1168        for block in &mut layout.blocks {
1169            let mut origin = bounds.origin()
1170                + vec2f(
1171                    0.,
1172                    block.row as f32 * layout.position_map.line_height - scroll_top,
1173                );
1174            if !matches!(block.style, BlockStyle::Sticky) {
1175                origin += vec2f(-scroll_left, 0.);
1176            }
1177            block
1178                .element
1179                .paint(scene, origin, visible_bounds, editor, cx);
1180        }
1181    }
1182
1183    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
1184        let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1;
1185        let style = &self.style;
1186
1187        cx.text_layout_cache()
1188            .layout_str(
1189                "1".repeat(digit_count).as_str(),
1190                style.text.font_size,
1191                &[(
1192                    digit_count,
1193                    RunStyle {
1194                        font_id: style.text.font_id,
1195                        color: Color::black(),
1196                        underline: Default::default(),
1197                    },
1198                )],
1199            )
1200            .width()
1201    }
1202
1203    //Folds contained in a hunk are ignored apart from shrinking visual size
1204    //If a fold contains any hunks then that fold line is marked as modified
1205    fn layout_git_gutters(
1206        &self,
1207        display_rows: Range<u32>,
1208        snapshot: &EditorSnapshot,
1209    ) -> Vec<DisplayDiffHunk> {
1210        let buffer_snapshot = &snapshot.buffer_snapshot;
1211
1212        let buffer_start_row = DisplayPoint::new(display_rows.start, 0)
1213            .to_point(snapshot)
1214            .row;
1215        let buffer_end_row = DisplayPoint::new(display_rows.end, 0)
1216            .to_point(snapshot)
1217            .row;
1218
1219        buffer_snapshot
1220            .git_diff_hunks_in_range(buffer_start_row..buffer_end_row, false)
1221            .map(|hunk| diff_hunk_to_display(hunk, snapshot))
1222            .dedup()
1223            .collect()
1224    }
1225
1226    fn layout_line_numbers(
1227        &self,
1228        rows: Range<u32>,
1229        active_rows: &BTreeMap<u32, bool>,
1230        is_singleton: bool,
1231        snapshot: &EditorSnapshot,
1232        cx: &ViewContext<Editor>,
1233    ) -> (
1234        Vec<Option<text_layout::Line>>,
1235        Vec<Option<(FoldStatus, BufferRow, bool)>>,
1236    ) {
1237        let style = &self.style;
1238        let include_line_numbers = snapshot.mode == EditorMode::Full;
1239        let mut line_number_layouts = Vec::with_capacity(rows.len());
1240        let mut fold_statuses = Vec::with_capacity(rows.len());
1241        let mut line_number = String::new();
1242        for (ix, row) in snapshot
1243            .buffer_rows(rows.start)
1244            .take((rows.end - rows.start) as usize)
1245            .enumerate()
1246        {
1247            let display_row = rows.start + ix as u32;
1248            let (active, color) = if active_rows.contains_key(&display_row) {
1249                (true, style.line_number_active)
1250            } else {
1251                (false, style.line_number)
1252            };
1253            if let Some(buffer_row) = row {
1254                if include_line_numbers {
1255                    line_number.clear();
1256                    write!(&mut line_number, "{}", buffer_row + 1).unwrap();
1257                    line_number_layouts.push(Some(cx.text_layout_cache().layout_str(
1258                        &line_number,
1259                        style.text.font_size,
1260                        &[(
1261                            line_number.len(),
1262                            RunStyle {
1263                                font_id: style.text.font_id,
1264                                color,
1265                                underline: Default::default(),
1266                            },
1267                        )],
1268                    )));
1269                    fold_statuses.push(
1270                        is_singleton
1271                            .then(|| {
1272                                snapshot
1273                                    .fold_for_line(buffer_row)
1274                                    .map(|fold_status| (fold_status, buffer_row, active))
1275                            })
1276                            .flatten(),
1277                    )
1278                }
1279            } else {
1280                fold_statuses.push(None);
1281                line_number_layouts.push(None);
1282            }
1283        }
1284
1285        (line_number_layouts, fold_statuses)
1286    }
1287
1288    fn layout_lines(
1289        &mut self,
1290        rows: Range<u32>,
1291        snapshot: &EditorSnapshot,
1292        cx: &ViewContext<Editor>,
1293    ) -> Vec<text_layout::Line> {
1294        if rows.start >= rows.end {
1295            return Vec::new();
1296        }
1297
1298        // When the editor is empty and unfocused, then show the placeholder.
1299        if snapshot.is_empty() {
1300            let placeholder_style = self
1301                .style
1302                .placeholder_text
1303                .as_ref()
1304                .unwrap_or(&self.style.text);
1305            let placeholder_text = snapshot.placeholder_text();
1306            let placeholder_lines = placeholder_text
1307                .as_ref()
1308                .map_or("", AsRef::as_ref)
1309                .split('\n')
1310                .skip(rows.start as usize)
1311                .chain(iter::repeat(""))
1312                .take(rows.len());
1313            placeholder_lines
1314                .map(|line| {
1315                    cx.text_layout_cache().layout_str(
1316                        line,
1317                        placeholder_style.font_size,
1318                        &[(
1319                            line.len(),
1320                            RunStyle {
1321                                font_id: placeholder_style.font_id,
1322                                color: placeholder_style.color,
1323                                underline: Default::default(),
1324                            },
1325                        )],
1326                    )
1327                })
1328                .collect()
1329        } else {
1330            let style = &self.style;
1331            let chunks = snapshot
1332                .chunks(rows.clone(), true, Some(style.theme.suggestion))
1333                .map(|chunk| {
1334                    let mut highlight_style = chunk
1335                        .syntax_highlight_id
1336                        .and_then(|id| id.style(&style.syntax));
1337
1338                    if let Some(chunk_highlight) = chunk.highlight_style {
1339                        if let Some(highlight_style) = highlight_style.as_mut() {
1340                            highlight_style.highlight(chunk_highlight);
1341                        } else {
1342                            highlight_style = Some(chunk_highlight);
1343                        }
1344                    }
1345
1346                    let mut diagnostic_highlight = HighlightStyle::default();
1347
1348                    if chunk.is_unnecessary {
1349                        diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
1350                    }
1351
1352                    if let Some(severity) = chunk.diagnostic_severity {
1353                        // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
1354                        if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
1355                            let diagnostic_style = super::diagnostic_style(severity, true, style);
1356                            diagnostic_highlight.underline = Some(Underline {
1357                                color: Some(diagnostic_style.message.text.color),
1358                                thickness: 1.0.into(),
1359                                squiggly: true,
1360                            });
1361                        }
1362                    }
1363
1364                    if let Some(highlight_style) = highlight_style.as_mut() {
1365                        highlight_style.highlight(diagnostic_highlight);
1366                    } else {
1367                        highlight_style = Some(diagnostic_highlight);
1368                    }
1369
1370                    (chunk.text, highlight_style)
1371                });
1372            layout_highlighted_chunks(
1373                chunks,
1374                &style.text,
1375                cx.text_layout_cache(),
1376                cx.font_cache(),
1377                MAX_LINE_LEN,
1378                rows.len() as usize,
1379            )
1380        }
1381    }
1382
1383    #[allow(clippy::too_many_arguments)]
1384    fn layout_blocks(
1385        &mut self,
1386        rows: Range<u32>,
1387        snapshot: &EditorSnapshot,
1388        editor_width: f32,
1389        scroll_width: f32,
1390        gutter_padding: f32,
1391        gutter_width: f32,
1392        em_width: f32,
1393        text_x: f32,
1394        line_height: f32,
1395        style: &EditorStyle,
1396        line_layouts: &[text_layout::Line],
1397        include_root: bool,
1398        editor: &mut Editor,
1399        cx: &mut ViewContext<Editor>,
1400    ) -> (f32, Vec<BlockLayout>) {
1401        let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
1402        let scroll_x = snapshot.scroll_anchor.offset.x();
1403        let (fixed_blocks, non_fixed_blocks) = snapshot
1404            .blocks_in_range(rows.clone())
1405            .partition::<Vec<_>, _>(|(_, block)| match block {
1406                TransformBlock::ExcerptHeader { .. } => false,
1407                TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
1408            });
1409        let mut render_block = |block: &TransformBlock, width: f32| {
1410            let mut element = match block {
1411                TransformBlock::Custom(block) => {
1412                    let align_to = block
1413                        .position()
1414                        .to_point(&snapshot.buffer_snapshot)
1415                        .to_display_point(snapshot);
1416                    let anchor_x = text_x
1417                        + if rows.contains(&align_to.row()) {
1418                            line_layouts[(align_to.row() - rows.start) as usize]
1419                                .x_for_index(align_to.column() as usize)
1420                        } else {
1421                            layout_line(align_to.row(), snapshot, style, cx.text_layout_cache())
1422                                .x_for_index(align_to.column() as usize)
1423                        };
1424
1425                    block.render(&mut BlockContext {
1426                        view_context: cx,
1427                        anchor_x,
1428                        gutter_padding,
1429                        line_height,
1430                        scroll_x,
1431                        gutter_width,
1432                        em_width,
1433                    })
1434                }
1435                TransformBlock::ExcerptHeader {
1436                    id,
1437                    buffer,
1438                    range,
1439                    starts_new_buffer,
1440                    ..
1441                } => {
1442                    let id = *id;
1443                    let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
1444                        let jump_path = ProjectPath {
1445                            worktree_id: file.worktree_id(cx),
1446                            path: file.path.clone(),
1447                        };
1448                        let jump_anchor = range
1449                            .primary
1450                            .as_ref()
1451                            .map_or(range.context.start, |primary| primary.start);
1452                        let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
1453
1454                        enum JumpIcon {}
1455                        MouseEventHandler::<JumpIcon, _>::new(id.into(), cx, |state, _| {
1456                            let style = style.jump_icon.style_for(state, false);
1457                            Svg::new("icons/arrow_up_right_8.svg")
1458                                .with_color(style.color)
1459                                .constrained()
1460                                .with_width(style.icon_width)
1461                                .aligned()
1462                                .contained()
1463                                .with_style(style.container)
1464                                .constrained()
1465                                .with_width(style.button_width)
1466                                .with_height(style.button_width)
1467                        })
1468                        .with_cursor_style(CursorStyle::PointingHand)
1469                        .on_click(MouseButton::Left, move |_, editor, cx| {
1470                            if let Some(workspace) = editor
1471                                .workspace
1472                                .as_ref()
1473                                .and_then(|(workspace, _)| workspace.upgrade(cx))
1474                            {
1475                                workspace.update(cx, |workspace, cx| {
1476                                    Editor::jump(
1477                                        workspace,
1478                                        jump_path.clone(),
1479                                        jump_position,
1480                                        jump_anchor,
1481                                        cx,
1482                                    );
1483                                });
1484                            }
1485                        })
1486                        .with_tooltip::<JumpIcon>(
1487                            id.into(),
1488                            "Jump to Buffer".to_string(),
1489                            Some(Box::new(crate::OpenExcerpts)),
1490                            tooltip_style.clone(),
1491                            cx,
1492                        )
1493                        .aligned()
1494                        .flex_float()
1495                    });
1496
1497                    if *starts_new_buffer {
1498                        let style = &self.style.diagnostic_path_header;
1499                        let font_size =
1500                            (style.text_scale_factor * self.style.text.font_size).round();
1501
1502                        let path = buffer.resolve_file_path(cx, include_root);
1503                        let mut filename = None;
1504                        let mut parent_path = None;
1505                        // Can't use .and_then() because `.file_name()` and `.parent()` return references :(
1506                        if let Some(path) = path {
1507                            filename = path.file_name().map(|f| f.to_string_lossy().to_string());
1508                            parent_path =
1509                                path.parent().map(|p| p.to_string_lossy().to_string() + "/");
1510                        }
1511
1512                        Flex::row()
1513                            .with_child(
1514                                Label::new(
1515                                    filename.unwrap_or_else(|| "untitled".to_string()),
1516                                    style.filename.text.clone().with_font_size(font_size),
1517                                )
1518                                .contained()
1519                                .with_style(style.filename.container)
1520                                .aligned(),
1521                            )
1522                            .with_children(parent_path.map(|path| {
1523                                Label::new(path, style.path.text.clone().with_font_size(font_size))
1524                                    .contained()
1525                                    .with_style(style.path.container)
1526                                    .aligned()
1527                            }))
1528                            .with_children(jump_icon)
1529                            .contained()
1530                            .with_style(style.container)
1531                            .with_padding_left(gutter_padding)
1532                            .with_padding_right(gutter_padding)
1533                            .expanded()
1534                            .into_any_named("path header block")
1535                    } else {
1536                        let text_style = self.style.text.clone();
1537                        Flex::row()
1538                            .with_child(Label::new("", text_style))
1539                            .with_children(jump_icon)
1540                            .contained()
1541                            .with_padding_left(gutter_padding)
1542                            .with_padding_right(gutter_padding)
1543                            .expanded()
1544                            .into_any_named("collapsed context")
1545                    }
1546                }
1547            };
1548
1549            element.layout(
1550                SizeConstraint {
1551                    min: Vector2F::zero(),
1552                    max: vec2f(width, block.height() as f32 * line_height),
1553                },
1554                editor,
1555                cx,
1556            );
1557            element
1558        };
1559
1560        let mut fixed_block_max_width = 0f32;
1561        let mut blocks = Vec::new();
1562        for (row, block) in fixed_blocks {
1563            let element = render_block(block, f32::INFINITY);
1564            fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width);
1565            blocks.push(BlockLayout {
1566                row,
1567                element,
1568                style: BlockStyle::Fixed,
1569            });
1570        }
1571        for (row, block) in non_fixed_blocks {
1572            let style = match block {
1573                TransformBlock::Custom(block) => block.style(),
1574                TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
1575            };
1576            let width = match style {
1577                BlockStyle::Sticky => editor_width,
1578                BlockStyle::Flex => editor_width
1579                    .max(fixed_block_max_width)
1580                    .max(gutter_width + scroll_width),
1581                BlockStyle::Fixed => unreachable!(),
1582            };
1583            let element = render_block(block, width);
1584            blocks.push(BlockLayout {
1585                row,
1586                element,
1587                style,
1588            });
1589        }
1590        (
1591            scroll_width.max(fixed_block_max_width - gutter_width),
1592            blocks,
1593        )
1594    }
1595}
1596
1597impl Element<Editor> for EditorElement {
1598    type LayoutState = LayoutState;
1599    type PaintState = ();
1600
1601    fn layout(
1602        &mut self,
1603        constraint: SizeConstraint,
1604        editor: &mut Editor,
1605        cx: &mut ViewContext<Editor>,
1606    ) -> (Vector2F, Self::LayoutState) {
1607        let mut size = constraint.max;
1608        if size.x().is_infinite() {
1609            unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
1610        }
1611
1612        let snapshot = editor.snapshot(cx);
1613        let style = self.style.clone();
1614        let line_height = style.text.line_height(cx.font_cache());
1615
1616        let gutter_padding;
1617        let gutter_width;
1618        let gutter_margin;
1619        if snapshot.mode == EditorMode::Full {
1620            gutter_padding = style.text.em_width(cx.font_cache()) * style.gutter_padding_factor;
1621            gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
1622            gutter_margin = -style.text.descent(cx.font_cache());
1623        } else {
1624            gutter_padding = 0.0;
1625            gutter_width = 0.0;
1626            gutter_margin = 0.0;
1627        };
1628
1629        let text_width = size.x() - gutter_width;
1630        let em_width = style.text.em_width(cx.font_cache());
1631        let em_advance = style.text.em_advance(cx.font_cache());
1632        let overscroll = vec2f(em_width, 0.);
1633        let snapshot = {
1634            editor.set_visible_line_count(size.y() / line_height);
1635
1636            let editor_width = text_width - gutter_margin - overscroll.x() - em_width;
1637            let wrap_width = match editor.soft_wrap_mode(cx) {
1638                SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
1639                SoftWrap::EditorWidth => editor_width,
1640                SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
1641            };
1642
1643            if editor.set_wrap_width(Some(wrap_width), cx) {
1644                editor.snapshot(cx)
1645            } else {
1646                snapshot
1647            }
1648        };
1649
1650        let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
1651        if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
1652            size.set_y(
1653                scroll_height
1654                    .min(constraint.max_along(Axis::Vertical))
1655                    .max(constraint.min_along(Axis::Vertical))
1656                    .min(line_height * max_lines as f32),
1657            )
1658        } else if let EditorMode::SingleLine = snapshot.mode {
1659            size.set_y(
1660                line_height
1661                    .min(constraint.max_along(Axis::Vertical))
1662                    .max(constraint.min_along(Axis::Vertical)),
1663            )
1664        } else if size.y().is_infinite() {
1665            size.set_y(scroll_height);
1666        }
1667        let gutter_size = vec2f(gutter_width, size.y());
1668        let text_size = vec2f(text_width, size.y());
1669
1670        let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), line_height, cx);
1671        let mut snapshot = editor.snapshot(cx);
1672
1673        let scroll_position = snapshot.scroll_position();
1674        // The scroll position is a fractional point, the whole number of which represents
1675        // the top of the window in terms of display rows.
1676        let start_row = scroll_position.y() as u32;
1677        let height_in_lines = size.y() / line_height;
1678        let max_row = snapshot.max_point().row();
1679
1680        // Add 1 to ensure selections bleed off screen
1681        let end_row = 1 + cmp::min(
1682            (scroll_position.y() + height_in_lines).ceil() as u32,
1683            max_row,
1684        );
1685
1686        let start_anchor = if start_row == 0 {
1687            Anchor::min()
1688        } else {
1689            snapshot
1690                .buffer_snapshot
1691                .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
1692        };
1693        let end_anchor = if end_row > max_row {
1694            Anchor::max()
1695        } else {
1696            snapshot
1697                .buffer_snapshot
1698                .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
1699        };
1700
1701        let mut selections: Vec<(ReplicaId, Vec<SelectionLayout>)> = Vec::new();
1702        let mut active_rows = BTreeMap::new();
1703        let mut fold_ranges = Vec::new();
1704        let is_singleton = editor.is_singleton(cx);
1705
1706        let highlighted_rows = editor.highlighted_rows();
1707        let theme = cx.global::<Settings>().theme.as_ref();
1708        let highlighted_ranges = editor.background_highlights_in_range(
1709            start_anchor..end_anchor,
1710            &snapshot.display_snapshot,
1711            theme,
1712        );
1713
1714        fold_ranges.extend(
1715            snapshot
1716                .folds_in_range(start_anchor..end_anchor)
1717                .map(|anchor| {
1718                    let start = anchor.start.to_point(&snapshot.buffer_snapshot);
1719                    (
1720                        start.row,
1721                        start.to_display_point(&snapshot.display_snapshot)
1722                            ..anchor.end.to_display_point(&snapshot),
1723                    )
1724                }),
1725        );
1726
1727        let mut remote_selections = HashMap::default();
1728        for (replica_id, line_mode, cursor_shape, selection) in snapshot
1729            .buffer_snapshot
1730            .remote_selections_in_range(&(start_anchor..end_anchor))
1731        {
1732            // The local selections match the leader's selections.
1733            if Some(replica_id) == editor.leader_replica_id {
1734                continue;
1735            }
1736            remote_selections
1737                .entry(replica_id)
1738                .or_insert(Vec::new())
1739                .push(SelectionLayout::new(
1740                    selection,
1741                    line_mode,
1742                    cursor_shape,
1743                    &snapshot.display_snapshot,
1744                ));
1745        }
1746        selections.extend(remote_selections);
1747
1748        if editor.show_local_selections {
1749            let mut local_selections = editor
1750                .selections
1751                .disjoint_in_range(start_anchor..end_anchor, cx);
1752            local_selections.extend(editor.selections.pending(cx));
1753            for selection in &local_selections {
1754                let is_empty = selection.start == selection.end;
1755                let selection_start = snapshot.prev_line_boundary(selection.start).1;
1756                let selection_end = snapshot.next_line_boundary(selection.end).1;
1757                for row in cmp::max(selection_start.row(), start_row)
1758                    ..=cmp::min(selection_end.row(), end_row)
1759                {
1760                    let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
1761                    *contains_non_empty_selection |= !is_empty;
1762                }
1763            }
1764
1765            // Render the local selections in the leader's color when following.
1766            let local_replica_id = editor
1767                .leader_replica_id
1768                .unwrap_or_else(|| editor.replica_id(cx));
1769
1770            selections.push((
1771                local_replica_id,
1772                local_selections
1773                    .into_iter()
1774                    .map(|selection| {
1775                        SelectionLayout::new(
1776                            selection,
1777                            editor.selections.line_mode,
1778                            editor.cursor_shape,
1779                            &snapshot.display_snapshot,
1780                        )
1781                    })
1782                    .collect(),
1783            ));
1784        }
1785
1786        let show_scrollbars = editor.scroll_manager.scrollbars_visible();
1787        let include_root = editor
1788            .project
1789            .as_ref()
1790            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
1791            .unwrap_or_default();
1792
1793        let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)> = fold_ranges
1794            .into_iter()
1795            .map(|(id, fold)| {
1796                let color = self
1797                    .style
1798                    .folds
1799                    .ellipses
1800                    .background
1801                    .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize), false)
1802                    .color;
1803
1804                (id, fold, color)
1805            })
1806            .collect();
1807
1808        let (line_number_layouts, fold_statuses) = self.layout_line_numbers(
1809            start_row..end_row,
1810            &active_rows,
1811            is_singleton,
1812            &snapshot,
1813            cx,
1814        );
1815
1816        let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
1817
1818        let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines);
1819
1820        let mut max_visible_line_width = 0.0;
1821        let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx);
1822        for line in &line_layouts {
1823            if line.width() > max_visible_line_width {
1824                max_visible_line_width = line.width();
1825            }
1826        }
1827
1828        let style = self.style.clone();
1829        let longest_line_width = layout_line(
1830            snapshot.longest_row(),
1831            &snapshot,
1832            &style,
1833            cx.text_layout_cache(),
1834        )
1835        .width();
1836        let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x();
1837        let em_width = style.text.em_width(cx.font_cache());
1838        let (scroll_width, blocks) = self.layout_blocks(
1839            start_row..end_row,
1840            &snapshot,
1841            size.x(),
1842            scroll_width,
1843            gutter_padding,
1844            gutter_width,
1845            em_width,
1846            gutter_width + gutter_margin,
1847            line_height,
1848            &style,
1849            &line_layouts,
1850            include_root,
1851            editor,
1852            cx,
1853        );
1854
1855        let scroll_max = vec2f(
1856            ((scroll_width - text_size.x()) / em_width).max(0.0),
1857            max_row as f32,
1858        );
1859
1860        let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x());
1861
1862        let autoscrolled = if autoscroll_horizontally {
1863            editor.autoscroll_horizontally(
1864                start_row,
1865                text_size.x(),
1866                scroll_width,
1867                em_width,
1868                &line_layouts,
1869                cx,
1870            )
1871        } else {
1872            false
1873        };
1874
1875        if clamped || autoscrolled {
1876            snapshot = editor.snapshot(cx);
1877        }
1878
1879        let newest_selection_head = editor
1880            .selections
1881            .newest::<usize>(cx)
1882            .head()
1883            .to_display_point(&snapshot);
1884        let style = editor.style(cx);
1885
1886        let mut context_menu = None;
1887        let mut code_actions_indicator = None;
1888        if (start_row..end_row).contains(&newest_selection_head.row()) {
1889            if editor.context_menu_visible() {
1890                context_menu = editor.render_context_menu(newest_selection_head, style.clone(), cx);
1891            }
1892
1893            let active = matches!(
1894                editor.context_menu,
1895                Some(crate::ContextMenu::CodeActions(_))
1896            );
1897
1898            code_actions_indicator = editor
1899                .render_code_actions_indicator(&style, active, cx)
1900                .map(|indicator| (newest_selection_head.row(), indicator));
1901        }
1902
1903        let visible_rows = start_row..start_row + line_layouts.len() as u32;
1904        let mut hover = editor
1905            .hover_state
1906            .render(&snapshot, &style, visible_rows, cx);
1907        let mode = editor.mode;
1908
1909        let mut fold_indicators = editor.render_fold_indicators(
1910            fold_statuses,
1911            &style,
1912            editor.gutter_hovered,
1913            line_height,
1914            gutter_margin,
1915            cx,
1916        );
1917
1918        if let Some((_, context_menu)) = context_menu.as_mut() {
1919            context_menu.layout(
1920                SizeConstraint {
1921                    min: Vector2F::zero(),
1922                    max: vec2f(
1923                        cx.window_size().x() * 0.7,
1924                        (12. * line_height).min((size.y() - line_height) / 2.),
1925                    ),
1926                },
1927                editor,
1928                cx,
1929            );
1930        }
1931
1932        if let Some((_, indicator)) = code_actions_indicator.as_mut() {
1933            indicator.layout(
1934                SizeConstraint::strict_along(
1935                    Axis::Vertical,
1936                    line_height * style.code_actions.vertical_scale,
1937                ),
1938                editor,
1939                cx,
1940            );
1941        }
1942
1943        for fold_indicator in fold_indicators.iter_mut() {
1944            if let Some(indicator) = fold_indicator.as_mut() {
1945                indicator.layout(
1946                    SizeConstraint::strict_along(
1947                        Axis::Vertical,
1948                        line_height * style.code_actions.vertical_scale,
1949                    ),
1950                    editor,
1951                    cx,
1952                );
1953            }
1954        }
1955
1956        if let Some((_, hover_popovers)) = hover.as_mut() {
1957            for hover_popover in hover_popovers.iter_mut() {
1958                hover_popover.layout(
1959                    SizeConstraint {
1960                        min: Vector2F::zero(),
1961                        max: vec2f(
1962                            (120. * em_width) // Default size
1963                                .min(size.x() / 2.) // Shrink to half of the editor width
1964                                .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
1965                            (16. * line_height) // Default size
1966                                .min(size.y() / 2.) // Shrink to half of the editor height
1967                                .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
1968                        ),
1969                    },
1970                    editor,
1971                    cx,
1972                );
1973            }
1974        }
1975
1976        (
1977            size,
1978            LayoutState {
1979                mode,
1980                position_map: Arc::new(PositionMap {
1981                    size,
1982                    scroll_max,
1983                    line_layouts,
1984                    line_height,
1985                    em_width,
1986                    em_advance,
1987                    snapshot,
1988                }),
1989                visible_display_row_range: start_row..end_row,
1990                gutter_size,
1991                gutter_padding,
1992                text_size,
1993                scrollbar_row_range,
1994                show_scrollbars,
1995                max_row,
1996                gutter_margin,
1997                active_rows,
1998                highlighted_rows,
1999                highlighted_ranges,
2000                fold_ranges,
2001                line_number_layouts,
2002                display_hunks,
2003                blocks,
2004                selections,
2005                context_menu,
2006                code_actions_indicator,
2007                fold_indicators,
2008                hover_popovers: hover,
2009            },
2010        )
2011    }
2012
2013    fn paint(
2014        &mut self,
2015        scene: &mut SceneBuilder,
2016        bounds: RectF,
2017        visible_bounds: RectF,
2018        layout: &mut Self::LayoutState,
2019        editor: &mut Editor,
2020        cx: &mut ViewContext<Editor>,
2021    ) -> Self::PaintState {
2022        let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
2023        scene.push_layer(Some(visible_bounds));
2024
2025        let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
2026        let text_bounds = RectF::new(
2027            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2028            layout.text_size,
2029        );
2030
2031        Self::attach_mouse_handlers(
2032            scene,
2033            &layout.position_map,
2034            layout.hover_popovers.is_some(),
2035            visible_bounds,
2036            text_bounds,
2037            gutter_bounds,
2038            bounds,
2039            cx,
2040        );
2041
2042        self.paint_background(scene, gutter_bounds, text_bounds, layout);
2043        if layout.gutter_size.x() > 0. {
2044            self.paint_gutter(scene, gutter_bounds, visible_bounds, layout, editor, cx);
2045        }
2046        self.paint_text(scene, text_bounds, visible_bounds, layout, editor, cx);
2047
2048        scene.push_layer(Some(bounds));
2049        if !layout.blocks.is_empty() {
2050            self.paint_blocks(scene, bounds, visible_bounds, layout, editor, cx);
2051        }
2052        self.paint_scrollbar(scene, bounds, layout, cx);
2053        scene.pop_layer();
2054
2055        scene.pop_layer();
2056    }
2057
2058    fn rect_for_text_range(
2059        &self,
2060        range_utf16: Range<usize>,
2061        bounds: RectF,
2062        _: RectF,
2063        layout: &Self::LayoutState,
2064        _: &Self::PaintState,
2065        _: &Editor,
2066        _: &ViewContext<Editor>,
2067    ) -> Option<RectF> {
2068        let text_bounds = RectF::new(
2069            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2070            layout.text_size,
2071        );
2072        let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.);
2073        let scroll_position = layout.position_map.snapshot.scroll_position();
2074        let start_row = scroll_position.y() as u32;
2075        let scroll_top = scroll_position.y() * layout.position_map.line_height;
2076        let scroll_left = scroll_position.x() * layout.position_map.em_width;
2077
2078        let range_start = OffsetUtf16(range_utf16.start)
2079            .to_display_point(&layout.position_map.snapshot.display_snapshot);
2080        if range_start.row() < start_row {
2081            return None;
2082        }
2083
2084        let line = layout
2085            .position_map
2086            .line_layouts
2087            .get((range_start.row() - start_row) as usize)?;
2088        let range_start_x = line.x_for_index(range_start.column() as usize);
2089        let range_start_y = range_start.row() as f32 * layout.position_map.line_height;
2090        Some(RectF::new(
2091            content_origin
2092                + vec2f(
2093                    range_start_x,
2094                    range_start_y + layout.position_map.line_height,
2095                )
2096                - vec2f(scroll_left, scroll_top),
2097            vec2f(
2098                layout.position_map.em_width,
2099                layout.position_map.line_height,
2100            ),
2101        ))
2102    }
2103
2104    fn debug(
2105        &self,
2106        bounds: RectF,
2107        _: &Self::LayoutState,
2108        _: &Self::PaintState,
2109        _: &Editor,
2110        _: &ViewContext<Editor>,
2111    ) -> json::Value {
2112        json!({
2113            "type": "BufferElement",
2114            "bounds": bounds.to_json()
2115        })
2116    }
2117}
2118
2119type BufferRow = u32;
2120
2121pub struct LayoutState {
2122    position_map: Arc<PositionMap>,
2123    gutter_size: Vector2F,
2124    gutter_padding: f32,
2125    gutter_margin: f32,
2126    text_size: Vector2F,
2127    mode: EditorMode,
2128    visible_display_row_range: Range<u32>,
2129    active_rows: BTreeMap<u32, bool>,
2130    highlighted_rows: Option<Range<u32>>,
2131    line_number_layouts: Vec<Option<text_layout::Line>>,
2132    display_hunks: Vec<DisplayDiffHunk>,
2133    blocks: Vec<BlockLayout>,
2134    highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
2135    fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)>,
2136    selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
2137    scrollbar_row_range: Range<f32>,
2138    show_scrollbars: bool,
2139    max_row: u32,
2140    context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
2141    code_actions_indicator: Option<(u32, AnyElement<Editor>)>,
2142    hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
2143    fold_indicators: Vec<Option<AnyElement<Editor>>>,
2144}
2145
2146pub struct PositionMap {
2147    size: Vector2F,
2148    line_height: f32,
2149    scroll_max: Vector2F,
2150    em_width: f32,
2151    em_advance: f32,
2152    line_layouts: Vec<text_layout::Line>,
2153    snapshot: EditorSnapshot,
2154}
2155
2156impl PositionMap {
2157    /// Returns two display points:
2158    /// 1. The nearest *valid* position in the editor
2159    /// 2. An unclipped, potentially *invalid* position that maps directly to
2160    ///    the given pixel position.
2161    fn point_for_position(
2162        &self,
2163        text_bounds: RectF,
2164        position: Vector2F,
2165    ) -> (DisplayPoint, DisplayPoint) {
2166        let scroll_position = self.snapshot.scroll_position();
2167        let position = position - text_bounds.origin();
2168        let y = position.y().max(0.0).min(self.size.y());
2169        let x = position.x() + (scroll_position.x() * self.em_width);
2170        let row = (y / self.line_height + scroll_position.y()) as u32;
2171        let (column, x_overshoot) = if let Some(line) = self
2172            .line_layouts
2173            .get(row as usize - scroll_position.y() as usize)
2174        {
2175            if let Some(ix) = line.index_for_x(x) {
2176                (ix as u32, 0.0)
2177            } else {
2178                (line.len() as u32, 0f32.max(x - line.width()))
2179            }
2180        } else {
2181            (0, x)
2182        };
2183
2184        let mut target_point = DisplayPoint::new(row, column);
2185        let point = self.snapshot.clip_point(target_point, Bias::Left);
2186        *target_point.column_mut() += (x_overshoot / self.em_advance) as u32;
2187
2188        (point, target_point)
2189    }
2190}
2191
2192struct BlockLayout {
2193    row: u32,
2194    element: AnyElement<Editor>,
2195    style: BlockStyle,
2196}
2197
2198fn layout_line(
2199    row: u32,
2200    snapshot: &EditorSnapshot,
2201    style: &EditorStyle,
2202    layout_cache: &TextLayoutCache,
2203) -> text_layout::Line {
2204    let mut line = snapshot.line(row);
2205
2206    if line.len() > MAX_LINE_LEN {
2207        let mut len = MAX_LINE_LEN;
2208        while !line.is_char_boundary(len) {
2209            len -= 1;
2210        }
2211
2212        line.truncate(len);
2213    }
2214
2215    layout_cache.layout_str(
2216        &line,
2217        style.text.font_size,
2218        &[(
2219            snapshot.line_len(row) as usize,
2220            RunStyle {
2221                font_id: style.text.font_id,
2222                color: Color::black(),
2223                underline: Default::default(),
2224            },
2225        )],
2226    )
2227}
2228
2229#[derive(Debug)]
2230pub struct Cursor {
2231    origin: Vector2F,
2232    block_width: f32,
2233    line_height: f32,
2234    color: Color,
2235    shape: CursorShape,
2236    block_text: Option<Line>,
2237}
2238
2239impl Cursor {
2240    pub fn new(
2241        origin: Vector2F,
2242        block_width: f32,
2243        line_height: f32,
2244        color: Color,
2245        shape: CursorShape,
2246        block_text: Option<Line>,
2247    ) -> Cursor {
2248        Cursor {
2249            origin,
2250            block_width,
2251            line_height,
2252            color,
2253            shape,
2254            block_text,
2255        }
2256    }
2257
2258    pub fn bounding_rect(&self, origin: Vector2F) -> RectF {
2259        RectF::new(
2260            self.origin + origin,
2261            vec2f(self.block_width, self.line_height),
2262        )
2263    }
2264
2265    pub fn paint(&self, scene: &mut SceneBuilder, origin: Vector2F, cx: &mut WindowContext) {
2266        let bounds = match self.shape {
2267            CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
2268            CursorShape::Block | CursorShape::Hollow => RectF::new(
2269                self.origin + origin,
2270                vec2f(self.block_width, self.line_height),
2271            ),
2272            CursorShape::Underscore => RectF::new(
2273                self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
2274                vec2f(self.block_width, 2.0),
2275            ),
2276        };
2277
2278        //Draw background or border quad
2279        if matches!(self.shape, CursorShape::Hollow) {
2280            scene.push_quad(Quad {
2281                bounds,
2282                background: None,
2283                border: Border::all(1., self.color),
2284                corner_radius: 0.,
2285            });
2286        } else {
2287            scene.push_quad(Quad {
2288                bounds,
2289                background: Some(self.color),
2290                border: Default::default(),
2291                corner_radius: 0.,
2292            });
2293        }
2294
2295        if let Some(block_text) = &self.block_text {
2296            block_text.paint(scene, self.origin + origin, bounds, self.line_height, cx);
2297        }
2298    }
2299
2300    pub fn shape(&self) -> CursorShape {
2301        self.shape
2302    }
2303}
2304
2305#[derive(Debug)]
2306pub struct HighlightedRange {
2307    pub start_y: f32,
2308    pub line_height: f32,
2309    pub lines: Vec<HighlightedRangeLine>,
2310    pub color: Color,
2311    pub corner_radius: f32,
2312}
2313
2314#[derive(Debug)]
2315pub struct HighlightedRangeLine {
2316    pub start_x: f32,
2317    pub end_x: f32,
2318}
2319
2320impl HighlightedRange {
2321    pub fn paint(&self, bounds: RectF, scene: &mut SceneBuilder) {
2322        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
2323            self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
2324            self.paint_lines(
2325                self.start_y + self.line_height,
2326                &self.lines[1..],
2327                bounds,
2328                scene,
2329            );
2330        } else {
2331            self.paint_lines(self.start_y, &self.lines, bounds, scene);
2332        }
2333    }
2334
2335    fn paint_lines(
2336        &self,
2337        start_y: f32,
2338        lines: &[HighlightedRangeLine],
2339        bounds: RectF,
2340        scene: &mut SceneBuilder,
2341    ) {
2342        if lines.is_empty() {
2343            return;
2344        }
2345
2346        let mut path = PathBuilder::new();
2347        let first_line = lines.first().unwrap();
2348        let last_line = lines.last().unwrap();
2349
2350        let first_top_left = vec2f(first_line.start_x, start_y);
2351        let first_top_right = vec2f(first_line.end_x, start_y);
2352
2353        let curve_height = vec2f(0., self.corner_radius);
2354        let curve_width = |start_x: f32, end_x: f32| {
2355            let max = (end_x - start_x) / 2.;
2356            let width = if max < self.corner_radius {
2357                max
2358            } else {
2359                self.corner_radius
2360            };
2361
2362            vec2f(width, 0.)
2363        };
2364
2365        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
2366        path.reset(first_top_right - top_curve_width);
2367        path.curve_to(first_top_right + curve_height, first_top_right);
2368
2369        let mut iter = lines.iter().enumerate().peekable();
2370        while let Some((ix, line)) = iter.next() {
2371            let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
2372
2373            if let Some((_, next_line)) = iter.peek() {
2374                let next_top_right = vec2f(next_line.end_x, bottom_right.y());
2375
2376                match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
2377                    Ordering::Equal => {
2378                        path.line_to(bottom_right);
2379                    }
2380                    Ordering::Less => {
2381                        let curve_width = curve_width(next_top_right.x(), bottom_right.x());
2382                        path.line_to(bottom_right - curve_height);
2383                        if self.corner_radius > 0. {
2384                            path.curve_to(bottom_right - curve_width, bottom_right);
2385                        }
2386                        path.line_to(next_top_right + curve_width);
2387                        if self.corner_radius > 0. {
2388                            path.curve_to(next_top_right + curve_height, next_top_right);
2389                        }
2390                    }
2391                    Ordering::Greater => {
2392                        let curve_width = curve_width(bottom_right.x(), next_top_right.x());
2393                        path.line_to(bottom_right - curve_height);
2394                        if self.corner_radius > 0. {
2395                            path.curve_to(bottom_right + curve_width, bottom_right);
2396                        }
2397                        path.line_to(next_top_right - curve_width);
2398                        if self.corner_radius > 0. {
2399                            path.curve_to(next_top_right + curve_height, next_top_right);
2400                        }
2401                    }
2402                }
2403            } else {
2404                let curve_width = curve_width(line.start_x, line.end_x);
2405                path.line_to(bottom_right - curve_height);
2406                if self.corner_radius > 0. {
2407                    path.curve_to(bottom_right - curve_width, bottom_right);
2408                }
2409
2410                let bottom_left = vec2f(line.start_x, bottom_right.y());
2411                path.line_to(bottom_left + curve_width);
2412                if self.corner_radius > 0. {
2413                    path.curve_to(bottom_left - curve_height, bottom_left);
2414                }
2415            }
2416        }
2417
2418        if first_line.start_x > last_line.start_x {
2419            let curve_width = curve_width(last_line.start_x, first_line.start_x);
2420            let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
2421            path.line_to(second_top_left + curve_height);
2422            if self.corner_radius > 0. {
2423                path.curve_to(second_top_left + curve_width, second_top_left);
2424            }
2425            let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
2426            path.line_to(first_bottom_left - curve_width);
2427            if self.corner_radius > 0. {
2428                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
2429            }
2430        }
2431
2432        path.line_to(first_top_left + curve_height);
2433        if self.corner_radius > 0. {
2434            path.curve_to(first_top_left + top_curve_width, first_top_left);
2435        }
2436        path.line_to(first_top_right - top_curve_width);
2437
2438        scene.push_path(path.build(self.color, Some(bounds)));
2439    }
2440}
2441
2442pub fn position_to_display_point(
2443    position: Vector2F,
2444    text_bounds: RectF,
2445    position_map: &PositionMap,
2446) -> Option<DisplayPoint> {
2447    if text_bounds.contains_point(position) {
2448        let (point, target_point) = position_map.point_for_position(text_bounds, position);
2449        if point == target_point {
2450            Some(point)
2451        } else {
2452            None
2453        }
2454    } else {
2455        None
2456    }
2457}
2458
2459pub fn range_to_bounds(
2460    range: &Range<DisplayPoint>,
2461    content_origin: Vector2F,
2462    scroll_left: f32,
2463    scroll_top: f32,
2464    visible_row_range: &Range<u32>,
2465    line_end_overshoot: f32,
2466    position_map: &PositionMap,
2467) -> impl Iterator<Item = RectF> {
2468    let mut bounds: SmallVec<[RectF; 1]> = SmallVec::new();
2469
2470    if range.start == range.end {
2471        return bounds.into_iter();
2472    }
2473
2474    let start_row = visible_row_range.start;
2475    let end_row = visible_row_range.end;
2476
2477    let row_range = if range.end.column() == 0 {
2478        cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
2479    } else {
2480        cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
2481    };
2482
2483    let first_y =
2484        content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top;
2485
2486    for (idx, row) in row_range.enumerate() {
2487        let line_layout = &position_map.line_layouts[(row - start_row) as usize];
2488
2489        let start_x = if row == range.start.row() {
2490            content_origin.x() + line_layout.x_for_index(range.start.column() as usize)
2491                - scroll_left
2492        } else {
2493            content_origin.x() - scroll_left
2494        };
2495
2496        let end_x = if row == range.end.row() {
2497            content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left
2498        } else {
2499            content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left
2500        };
2501
2502        bounds.push(RectF::from_points(
2503            vec2f(start_x, first_y + position_map.line_height * idx as f32),
2504            vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32),
2505        ))
2506    }
2507
2508    bounds.into_iter()
2509}
2510
2511pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
2512    delta.powf(1.5) / 100.0
2513}
2514
2515fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
2516    delta.powf(1.2) / 300.0
2517}
2518
2519#[cfg(test)]
2520mod tests {
2521    use super::*;
2522    use crate::{
2523        display_map::{BlockDisposition, BlockProperties},
2524        Editor, MultiBuffer,
2525    };
2526    use gpui::TestAppContext;
2527    use settings::Settings;
2528    use std::sync::Arc;
2529    use util::test::sample_text;
2530
2531    #[gpui::test]
2532    fn test_layout_line_numbers(cx: &mut TestAppContext) {
2533        cx.update(|cx| cx.set_global(Settings::test(cx)));
2534        let (_, editor) = cx.add_window(|cx| {
2535            let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
2536            Editor::new(EditorMode::Full, buffer, None, None, cx)
2537        });
2538        let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
2539
2540        let layouts = editor.update(cx, |editor, cx| {
2541            let snapshot = editor.snapshot(cx);
2542            element
2543                .layout_line_numbers(0..6, &Default::default(), false, &snapshot, cx)
2544                .0
2545        });
2546        assert_eq!(layouts.len(), 6);
2547    }
2548
2549    #[gpui::test]
2550    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
2551        cx.update(|cx| cx.set_global(Settings::test(cx)));
2552        let (_, editor) = cx.add_window(|cx| {
2553            let buffer = MultiBuffer::build_simple("", cx);
2554            Editor::new(EditorMode::Full, buffer, None, None, cx)
2555        });
2556
2557        editor.update(cx, |editor, cx| {
2558            editor.set_placeholder_text("hello", cx);
2559            editor.insert_blocks(
2560                [BlockProperties {
2561                    style: BlockStyle::Fixed,
2562                    disposition: BlockDisposition::Above,
2563                    height: 3,
2564                    position: Anchor::min(),
2565                    render: Arc::new(|_| Empty::new().into_any()),
2566                }],
2567                cx,
2568            );
2569
2570            // Blur the editor so that it displays placeholder text.
2571            cx.blur();
2572        });
2573
2574        let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
2575        let (size, mut state) = editor.update(cx, |editor, cx| {
2576            element.layout(
2577                SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
2578                editor,
2579                cx,
2580            )
2581        });
2582
2583        assert_eq!(state.position_map.line_layouts.len(), 4);
2584        assert_eq!(
2585            state
2586                .line_number_layouts
2587                .iter()
2588                .map(Option::is_some)
2589                .collect::<Vec<_>>(),
2590            &[false, false, false, true]
2591        );
2592
2593        // Don't panic.
2594        let mut scene = SceneBuilder::new(1.0);
2595        let bounds = RectF::new(Default::default(), size);
2596        editor.update(cx, |editor, cx| {
2597            element.paint(&mut scene, bounds, bounds, &mut state, editor, cx);
2598        });
2599    }
2600}