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