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_path = ProjectPath {
1439                            worktree_id: file.worktree_id(cx),
1440                            path: file.path.clone(),
1441                        };
1442                        let jump_anchor = range
1443                            .primary
1444                            .as_ref()
1445                            .map_or(range.context.start, |primary| primary.start);
1446                        let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
1447
1448                        enum JumpIcon {}
1449                        MouseEventHandler::<JumpIcon, _>::new(id.into(), cx, |state, _| {
1450                            let style = style.jump_icon.style_for(state, false);
1451                            Svg::new("icons/arrow_up_right_8.svg")
1452                                .with_color(style.color)
1453                                .constrained()
1454                                .with_width(style.icon_width)
1455                                .aligned()
1456                                .contained()
1457                                .with_style(style.container)
1458                                .constrained()
1459                                .with_width(style.button_width)
1460                                .with_height(style.button_width)
1461                        })
1462                        .with_cursor_style(CursorStyle::PointingHand)
1463                        .on_click(MouseButton::Left, move |_, editor, cx| {
1464                            if let Some(workspace) = editor
1465                                .workspace
1466                                .as_ref()
1467                                .and_then(|(workspace, _)| workspace.upgrade(cx))
1468                            {
1469                                workspace.update(cx, |workspace, cx| {
1470                                    Editor::jump(
1471                                        workspace,
1472                                        jump_path.clone(),
1473                                        jump_position,
1474                                        jump_anchor,
1475                                        cx,
1476                                    );
1477                                });
1478                            }
1479                        })
1480                        .with_tooltip::<JumpIcon>(
1481                            id.into(),
1482                            "Jump to Buffer".to_string(),
1483                            Some(Box::new(crate::OpenExcerpts)),
1484                            tooltip_style.clone(),
1485                            cx,
1486                        )
1487                        .aligned()
1488                        .flex_float()
1489                    });
1490
1491                    if *starts_new_buffer {
1492                        let style = &self.style.diagnostic_path_header;
1493                        let font_size =
1494                            (style.text_scale_factor * self.style.text.font_size).round();
1495
1496                        let path = buffer.resolve_file_path(cx, include_root);
1497                        let mut filename = None;
1498                        let mut parent_path = None;
1499                        // Can't use .and_then() because `.file_name()` and `.parent()` return references :(
1500                        if let Some(path) = path {
1501                            filename = path.file_name().map(|f| f.to_string_lossy().to_string());
1502                            parent_path =
1503                                path.parent().map(|p| p.to_string_lossy().to_string() + "/");
1504                        }
1505
1506                        Flex::row()
1507                            .with_child(
1508                                Label::new(
1509                                    filename.unwrap_or_else(|| "untitled".to_string()),
1510                                    style.filename.text.clone().with_font_size(font_size),
1511                                )
1512                                .contained()
1513                                .with_style(style.filename.container)
1514                                .aligned(),
1515                            )
1516                            .with_children(parent_path.map(|path| {
1517                                Label::new(path, style.path.text.clone().with_font_size(font_size))
1518                                    .contained()
1519                                    .with_style(style.path.container)
1520                                    .aligned()
1521                            }))
1522                            .with_children(jump_icon)
1523                            .contained()
1524                            .with_style(style.container)
1525                            .with_padding_left(gutter_padding)
1526                            .with_padding_right(gutter_padding)
1527                            .expanded()
1528                            .into_any_named("path header block")
1529                    } else {
1530                        let text_style = self.style.text.clone();
1531                        Flex::row()
1532                            .with_child(Label::new("", text_style))
1533                            .with_children(jump_icon)
1534                            .contained()
1535                            .with_padding_left(gutter_padding)
1536                            .with_padding_right(gutter_padding)
1537                            .expanded()
1538                            .into_any_named("collapsed context")
1539                    }
1540                }
1541            };
1542
1543            element.layout(
1544                SizeConstraint {
1545                    min: Vector2F::zero(),
1546                    max: vec2f(width, block.height() as f32 * line_height),
1547                },
1548                editor,
1549                cx,
1550            );
1551            element
1552        };
1553
1554        let mut fixed_block_max_width = 0f32;
1555        let mut blocks = Vec::new();
1556        for (row, block) in fixed_blocks {
1557            let element = render_block(block, f32::INFINITY);
1558            fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width);
1559            blocks.push(BlockLayout {
1560                row,
1561                element,
1562                style: BlockStyle::Fixed,
1563            });
1564        }
1565        for (row, block) in non_fixed_blocks {
1566            let style = match block {
1567                TransformBlock::Custom(block) => block.style(),
1568                TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
1569            };
1570            let width = match style {
1571                BlockStyle::Sticky => editor_width,
1572                BlockStyle::Flex => editor_width
1573                    .max(fixed_block_max_width)
1574                    .max(gutter_width + scroll_width),
1575                BlockStyle::Fixed => unreachable!(),
1576            };
1577            let element = render_block(block, width);
1578            blocks.push(BlockLayout {
1579                row,
1580                element,
1581                style,
1582            });
1583        }
1584        (
1585            scroll_width.max(fixed_block_max_width - gutter_width),
1586            blocks,
1587        )
1588    }
1589}
1590
1591impl Element<Editor> for EditorElement {
1592    type LayoutState = LayoutState;
1593    type PaintState = ();
1594
1595    fn layout(
1596        &mut self,
1597        constraint: SizeConstraint,
1598        editor: &mut Editor,
1599        cx: &mut ViewContext<Editor>,
1600    ) -> (Vector2F, Self::LayoutState) {
1601        let mut size = constraint.max;
1602        if size.x().is_infinite() {
1603            unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
1604        }
1605
1606        let snapshot = editor.snapshot(cx);
1607        let style = self.style.clone();
1608        let line_height = style.text.line_height(cx.font_cache());
1609
1610        let gutter_padding;
1611        let gutter_width;
1612        let gutter_margin;
1613        if snapshot.mode == EditorMode::Full {
1614            gutter_padding = style.text.em_width(cx.font_cache()) * style.gutter_padding_factor;
1615            gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
1616            gutter_margin = -style.text.descent(cx.font_cache());
1617        } else {
1618            gutter_padding = 0.0;
1619            gutter_width = 0.0;
1620            gutter_margin = 0.0;
1621        };
1622
1623        let text_width = size.x() - gutter_width;
1624        let em_width = style.text.em_width(cx.font_cache());
1625        let em_advance = style.text.em_advance(cx.font_cache());
1626        let overscroll = vec2f(em_width, 0.);
1627        let snapshot = {
1628            editor.set_visible_line_count(size.y() / line_height);
1629
1630            let editor_width = text_width - gutter_margin - overscroll.x() - em_width;
1631            let wrap_width = match editor.soft_wrap_mode(cx) {
1632                SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
1633                SoftWrap::EditorWidth => editor_width,
1634                SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
1635            };
1636
1637            if editor.set_wrap_width(Some(wrap_width), cx) {
1638                editor.snapshot(cx)
1639            } else {
1640                snapshot
1641            }
1642        };
1643
1644        let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
1645        if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
1646            size.set_y(
1647                scroll_height
1648                    .min(constraint.max_along(Axis::Vertical))
1649                    .max(constraint.min_along(Axis::Vertical))
1650                    .min(line_height * max_lines as f32),
1651            )
1652        } else if let EditorMode::SingleLine = snapshot.mode {
1653            size.set_y(
1654                line_height
1655                    .min(constraint.max_along(Axis::Vertical))
1656                    .max(constraint.min_along(Axis::Vertical)),
1657            )
1658        } else if size.y().is_infinite() {
1659            size.set_y(scroll_height);
1660        }
1661        let gutter_size = vec2f(gutter_width, size.y());
1662        let text_size = vec2f(text_width, size.y());
1663
1664        let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), line_height, cx);
1665        let mut snapshot = editor.snapshot(cx);
1666
1667        let scroll_position = snapshot.scroll_position();
1668        // The scroll position is a fractional point, the whole number of which represents
1669        // the top of the window in terms of display rows.
1670        let start_row = scroll_position.y() as u32;
1671        let height_in_lines = size.y() / line_height;
1672        let max_row = snapshot.max_point().row();
1673
1674        // Add 1 to ensure selections bleed off screen
1675        let end_row = 1 + cmp::min(
1676            (scroll_position.y() + height_in_lines).ceil() as u32,
1677            max_row,
1678        );
1679
1680        let start_anchor = if start_row == 0 {
1681            Anchor::min()
1682        } else {
1683            snapshot
1684                .buffer_snapshot
1685                .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
1686        };
1687        let end_anchor = if end_row > max_row {
1688            Anchor::max()
1689        } else {
1690            snapshot
1691                .buffer_snapshot
1692                .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
1693        };
1694
1695        let mut selections: Vec<(ReplicaId, Vec<SelectionLayout>)> = Vec::new();
1696        let mut active_rows = BTreeMap::new();
1697        let mut fold_ranges = Vec::new();
1698        let is_singleton = editor.is_singleton(cx);
1699
1700        let highlighted_rows = editor.highlighted_rows();
1701        let theme = cx.global::<Settings>().theme.as_ref();
1702        let highlighted_ranges = editor.background_highlights_in_range(
1703            start_anchor..end_anchor,
1704            &snapshot.display_snapshot,
1705            theme,
1706        );
1707
1708        fold_ranges.extend(
1709            snapshot
1710                .folds_in_range(start_anchor..end_anchor)
1711                .map(|anchor| {
1712                    let start = anchor.start.to_point(&snapshot.buffer_snapshot);
1713                    (
1714                        start.row,
1715                        start.to_display_point(&snapshot.display_snapshot)
1716                            ..anchor.end.to_display_point(&snapshot),
1717                    )
1718                }),
1719        );
1720
1721        let mut remote_selections = HashMap::default();
1722        for (replica_id, line_mode, cursor_shape, selection) in snapshot
1723            .buffer_snapshot
1724            .remote_selections_in_range(&(start_anchor..end_anchor))
1725        {
1726            // The local selections match the leader's selections.
1727            if Some(replica_id) == editor.leader_replica_id {
1728                continue;
1729            }
1730            remote_selections
1731                .entry(replica_id)
1732                .or_insert(Vec::new())
1733                .push(SelectionLayout::new(
1734                    selection,
1735                    line_mode,
1736                    cursor_shape,
1737                    &snapshot.display_snapshot,
1738                ));
1739        }
1740        selections.extend(remote_selections);
1741
1742        if editor.show_local_selections {
1743            let mut local_selections = editor
1744                .selections
1745                .disjoint_in_range(start_anchor..end_anchor, cx);
1746            local_selections.extend(editor.selections.pending(cx));
1747            for selection in &local_selections {
1748                let is_empty = selection.start == selection.end;
1749                let selection_start = snapshot.prev_line_boundary(selection.start).1;
1750                let selection_end = snapshot.next_line_boundary(selection.end).1;
1751                for row in cmp::max(selection_start.row(), start_row)
1752                    ..=cmp::min(selection_end.row(), end_row)
1753                {
1754                    let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
1755                    *contains_non_empty_selection |= !is_empty;
1756                }
1757            }
1758
1759            // Render the local selections in the leader's color when following.
1760            let local_replica_id = editor
1761                .leader_replica_id
1762                .unwrap_or_else(|| editor.replica_id(cx));
1763
1764            selections.push((
1765                local_replica_id,
1766                local_selections
1767                    .into_iter()
1768                    .map(|selection| {
1769                        SelectionLayout::new(
1770                            selection,
1771                            editor.selections.line_mode,
1772                            editor.cursor_shape,
1773                            &snapshot.display_snapshot,
1774                        )
1775                    })
1776                    .collect(),
1777            ));
1778        }
1779
1780        let show_scrollbars = editor.scroll_manager.scrollbars_visible();
1781        let include_root = editor
1782            .project
1783            .as_ref()
1784            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
1785            .unwrap_or_default();
1786
1787        let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)> = fold_ranges
1788            .into_iter()
1789            .map(|(id, fold)| {
1790                let color = self
1791                    .style
1792                    .folds
1793                    .ellipses
1794                    .background
1795                    .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize), false)
1796                    .color;
1797
1798                (id, fold, color)
1799            })
1800            .collect();
1801
1802        let (line_number_layouts, fold_statuses) = self.layout_line_numbers(
1803            start_row..end_row,
1804            &active_rows,
1805            is_singleton,
1806            &snapshot,
1807            cx,
1808        );
1809
1810        let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
1811
1812        let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines);
1813
1814        let mut max_visible_line_width = 0.0;
1815        let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx);
1816        for line in &line_layouts {
1817            if line.width() > max_visible_line_width {
1818                max_visible_line_width = line.width();
1819            }
1820        }
1821
1822        let style = self.style.clone();
1823        let longest_line_width = layout_line(
1824            snapshot.longest_row(),
1825            &snapshot,
1826            &style,
1827            cx.text_layout_cache(),
1828        )
1829        .width();
1830        let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x();
1831        let em_width = style.text.em_width(cx.font_cache());
1832        let (scroll_width, blocks) = self.layout_blocks(
1833            start_row..end_row,
1834            &snapshot,
1835            size.x(),
1836            scroll_width,
1837            gutter_padding,
1838            gutter_width,
1839            em_width,
1840            gutter_width + gutter_margin,
1841            line_height,
1842            &style,
1843            &line_layouts,
1844            include_root,
1845            editor,
1846            cx,
1847        );
1848
1849        let scroll_max = vec2f(
1850            ((scroll_width - text_size.x()) / em_width).max(0.0),
1851            max_row as f32,
1852        );
1853
1854        let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x());
1855
1856        let autoscrolled = if autoscroll_horizontally {
1857            editor.autoscroll_horizontally(
1858                start_row,
1859                text_size.x(),
1860                scroll_width,
1861                em_width,
1862                &line_layouts,
1863                cx,
1864            )
1865        } else {
1866            false
1867        };
1868
1869        if clamped || autoscrolled {
1870            snapshot = editor.snapshot(cx);
1871        }
1872
1873        let newest_selection_head = editor
1874            .selections
1875            .newest::<usize>(cx)
1876            .head()
1877            .to_display_point(&snapshot);
1878        let style = editor.style(cx);
1879
1880        let mut context_menu = None;
1881        let mut code_actions_indicator = None;
1882        if (start_row..end_row).contains(&newest_selection_head.row()) {
1883            if editor.context_menu_visible() {
1884                context_menu = editor.render_context_menu(newest_selection_head, style.clone(), cx);
1885            }
1886
1887            let active = matches!(
1888                editor.context_menu,
1889                Some(crate::ContextMenu::CodeActions(_))
1890            );
1891
1892            code_actions_indicator = editor
1893                .render_code_actions_indicator(&style, active, cx)
1894                .map(|indicator| (newest_selection_head.row(), indicator));
1895        }
1896
1897        let visible_rows = start_row..start_row + line_layouts.len() as u32;
1898        let mut hover = editor
1899            .hover_state
1900            .render(&snapshot, &style, visible_rows, cx);
1901        let mode = editor.mode;
1902
1903        let mut fold_indicators = editor.render_fold_indicators(
1904            fold_statuses,
1905            &style,
1906            editor.gutter_hovered,
1907            line_height,
1908            gutter_margin,
1909            cx,
1910        );
1911
1912        if let Some((_, context_menu)) = context_menu.as_mut() {
1913            context_menu.layout(
1914                SizeConstraint {
1915                    min: Vector2F::zero(),
1916                    max: vec2f(
1917                        cx.window_size().x() * 0.7,
1918                        (12. * line_height).min((size.y() - line_height) / 2.),
1919                    ),
1920                },
1921                editor,
1922                cx,
1923            );
1924        }
1925
1926        if let Some((_, indicator)) = code_actions_indicator.as_mut() {
1927            indicator.layout(
1928                SizeConstraint::strict_along(
1929                    Axis::Vertical,
1930                    line_height * style.code_actions.vertical_scale,
1931                ),
1932                editor,
1933                cx,
1934            );
1935        }
1936
1937        for fold_indicator in fold_indicators.iter_mut() {
1938            if let Some(indicator) = fold_indicator.as_mut() {
1939                indicator.layout(
1940                    SizeConstraint::strict_along(
1941                        Axis::Vertical,
1942                        line_height * style.code_actions.vertical_scale,
1943                    ),
1944                    editor,
1945                    cx,
1946                );
1947            }
1948        }
1949
1950        if let Some((_, hover_popovers)) = hover.as_mut() {
1951            for hover_popover in hover_popovers.iter_mut() {
1952                hover_popover.layout(
1953                    SizeConstraint {
1954                        min: Vector2F::zero(),
1955                        max: vec2f(
1956                            (120. * em_width) // Default size
1957                                .min(size.x() / 2.) // Shrink to half of the editor width
1958                                .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
1959                            (16. * line_height) // Default size
1960                                .min(size.y() / 2.) // Shrink to half of the editor height
1961                                .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
1962                        ),
1963                    },
1964                    editor,
1965                    cx,
1966                );
1967            }
1968        }
1969
1970        (
1971            size,
1972            LayoutState {
1973                mode,
1974                position_map: Arc::new(PositionMap {
1975                    size,
1976                    scroll_max,
1977                    line_layouts,
1978                    line_height,
1979                    em_width,
1980                    em_advance,
1981                    snapshot,
1982                }),
1983                visible_display_row_range: start_row..end_row,
1984                gutter_size,
1985                gutter_padding,
1986                text_size,
1987                scrollbar_row_range,
1988                show_scrollbars,
1989                max_row,
1990                gutter_margin,
1991                active_rows,
1992                highlighted_rows,
1993                highlighted_ranges,
1994                fold_ranges,
1995                line_number_layouts,
1996                display_hunks,
1997                blocks,
1998                selections,
1999                context_menu,
2000                code_actions_indicator,
2001                fold_indicators,
2002                hover_popovers: hover,
2003            },
2004        )
2005    }
2006
2007    fn paint(
2008        &mut self,
2009        scene: &mut SceneBuilder,
2010        bounds: RectF,
2011        visible_bounds: RectF,
2012        layout: &mut Self::LayoutState,
2013        editor: &mut Editor,
2014        cx: &mut ViewContext<Editor>,
2015    ) -> Self::PaintState {
2016        let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
2017        scene.push_layer(Some(visible_bounds));
2018
2019        let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
2020        let text_bounds = RectF::new(
2021            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2022            layout.text_size,
2023        );
2024
2025        Self::attach_mouse_handlers(
2026            scene,
2027            &layout.position_map,
2028            layout.hover_popovers.is_some(),
2029            visible_bounds,
2030            text_bounds,
2031            gutter_bounds,
2032            bounds,
2033            cx,
2034        );
2035
2036        self.paint_background(scene, gutter_bounds, text_bounds, layout);
2037        if layout.gutter_size.x() > 0. {
2038            self.paint_gutter(scene, gutter_bounds, visible_bounds, layout, editor, cx);
2039        }
2040        self.paint_text(scene, text_bounds, visible_bounds, layout, editor, cx);
2041
2042        scene.push_layer(Some(bounds));
2043        if !layout.blocks.is_empty() {
2044            self.paint_blocks(scene, bounds, visible_bounds, layout, editor, cx);
2045        }
2046        self.paint_scrollbar(scene, bounds, layout, cx);
2047        scene.pop_layer();
2048
2049        scene.pop_layer();
2050    }
2051
2052    fn rect_for_text_range(
2053        &self,
2054        range_utf16: Range<usize>,
2055        bounds: RectF,
2056        _: RectF,
2057        layout: &Self::LayoutState,
2058        _: &Self::PaintState,
2059        _: &Editor,
2060        _: &ViewContext<Editor>,
2061    ) -> Option<RectF> {
2062        let text_bounds = RectF::new(
2063            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2064            layout.text_size,
2065        );
2066        let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.);
2067        let scroll_position = layout.position_map.snapshot.scroll_position();
2068        let start_row = scroll_position.y() as u32;
2069        let scroll_top = scroll_position.y() * layout.position_map.line_height;
2070        let scroll_left = scroll_position.x() * layout.position_map.em_width;
2071
2072        let range_start = OffsetUtf16(range_utf16.start)
2073            .to_display_point(&layout.position_map.snapshot.display_snapshot);
2074        if range_start.row() < start_row {
2075            return None;
2076        }
2077
2078        let line = layout
2079            .position_map
2080            .line_layouts
2081            .get((range_start.row() - start_row) as usize)?;
2082        let range_start_x = line.x_for_index(range_start.column() as usize);
2083        let range_start_y = range_start.row() as f32 * layout.position_map.line_height;
2084        Some(RectF::new(
2085            content_origin
2086                + vec2f(
2087                    range_start_x,
2088                    range_start_y + layout.position_map.line_height,
2089                )
2090                - vec2f(scroll_left, scroll_top),
2091            vec2f(
2092                layout.position_map.em_width,
2093                layout.position_map.line_height,
2094            ),
2095        ))
2096    }
2097
2098    fn debug(
2099        &self,
2100        bounds: RectF,
2101        _: &Self::LayoutState,
2102        _: &Self::PaintState,
2103        _: &Editor,
2104        _: &ViewContext<Editor>,
2105    ) -> json::Value {
2106        json!({
2107            "type": "BufferElement",
2108            "bounds": bounds.to_json()
2109        })
2110    }
2111}
2112
2113type BufferRow = u32;
2114
2115pub struct LayoutState {
2116    position_map: Arc<PositionMap>,
2117    gutter_size: Vector2F,
2118    gutter_padding: f32,
2119    gutter_margin: f32,
2120    text_size: Vector2F,
2121    mode: EditorMode,
2122    visible_display_row_range: Range<u32>,
2123    active_rows: BTreeMap<u32, bool>,
2124    highlighted_rows: Option<Range<u32>>,
2125    line_number_layouts: Vec<Option<text_layout::Line>>,
2126    display_hunks: Vec<DisplayDiffHunk>,
2127    blocks: Vec<BlockLayout>,
2128    highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
2129    fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)>,
2130    selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
2131    scrollbar_row_range: Range<f32>,
2132    show_scrollbars: bool,
2133    max_row: u32,
2134    context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
2135    code_actions_indicator: Option<(u32, AnyElement<Editor>)>,
2136    hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
2137    fold_indicators: Vec<Option<AnyElement<Editor>>>,
2138}
2139
2140pub struct PositionMap {
2141    size: Vector2F,
2142    line_height: f32,
2143    scroll_max: Vector2F,
2144    em_width: f32,
2145    em_advance: f32,
2146    line_layouts: Vec<text_layout::Line>,
2147    snapshot: EditorSnapshot,
2148}
2149
2150impl PositionMap {
2151    /// Returns two display points:
2152    /// 1. The nearest *valid* position in the editor
2153    /// 2. An unclipped, potentially *invalid* position that maps directly to
2154    ///    the given pixel position.
2155    fn point_for_position(
2156        &self,
2157        text_bounds: RectF,
2158        position: Vector2F,
2159    ) -> (DisplayPoint, DisplayPoint) {
2160        let scroll_position = self.snapshot.scroll_position();
2161        let position = position - text_bounds.origin();
2162        let y = position.y().max(0.0).min(self.size.y());
2163        let x = position.x() + (scroll_position.x() * self.em_width);
2164        let row = (y / self.line_height + scroll_position.y()) as u32;
2165        let (column, x_overshoot) = if let Some(line) = self
2166            .line_layouts
2167            .get(row as usize - scroll_position.y() as usize)
2168        {
2169            if let Some(ix) = line.index_for_x(x) {
2170                (ix as u32, 0.0)
2171            } else {
2172                (line.len() as u32, 0f32.max(x - line.width()))
2173            }
2174        } else {
2175            (0, x)
2176        };
2177
2178        let mut target_point = DisplayPoint::new(row, column);
2179        let point = self.snapshot.clip_point(target_point, Bias::Left);
2180        *target_point.column_mut() += (x_overshoot / self.em_advance) as u32;
2181
2182        (point, target_point)
2183    }
2184}
2185
2186struct BlockLayout {
2187    row: u32,
2188    element: AnyElement<Editor>,
2189    style: BlockStyle,
2190}
2191
2192fn layout_line(
2193    row: u32,
2194    snapshot: &EditorSnapshot,
2195    style: &EditorStyle,
2196    layout_cache: &TextLayoutCache,
2197) -> text_layout::Line {
2198    let mut line = snapshot.line(row);
2199
2200    if line.len() > MAX_LINE_LEN {
2201        let mut len = MAX_LINE_LEN;
2202        while !line.is_char_boundary(len) {
2203            len -= 1;
2204        }
2205
2206        line.truncate(len);
2207    }
2208
2209    layout_cache.layout_str(
2210        &line,
2211        style.text.font_size,
2212        &[(
2213            snapshot.line_len(row) as usize,
2214            RunStyle {
2215                font_id: style.text.font_id,
2216                color: Color::black(),
2217                underline: Default::default(),
2218            },
2219        )],
2220    )
2221}
2222
2223#[derive(Debug)]
2224pub struct Cursor {
2225    origin: Vector2F,
2226    block_width: f32,
2227    line_height: f32,
2228    color: Color,
2229    shape: CursorShape,
2230    block_text: Option<Line>,
2231}
2232
2233impl Cursor {
2234    pub fn new(
2235        origin: Vector2F,
2236        block_width: f32,
2237        line_height: f32,
2238        color: Color,
2239        shape: CursorShape,
2240        block_text: Option<Line>,
2241    ) -> Cursor {
2242        Cursor {
2243            origin,
2244            block_width,
2245            line_height,
2246            color,
2247            shape,
2248            block_text,
2249        }
2250    }
2251
2252    pub fn bounding_rect(&self, origin: Vector2F) -> RectF {
2253        RectF::new(
2254            self.origin + origin,
2255            vec2f(self.block_width, self.line_height),
2256        )
2257    }
2258
2259    pub fn paint(&self, scene: &mut SceneBuilder, origin: Vector2F, cx: &mut WindowContext) {
2260        let bounds = match self.shape {
2261            CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
2262            CursorShape::Block | CursorShape::Hollow => RectF::new(
2263                self.origin + origin,
2264                vec2f(self.block_width, self.line_height),
2265            ),
2266            CursorShape::Underscore => RectF::new(
2267                self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
2268                vec2f(self.block_width, 2.0),
2269            ),
2270        };
2271
2272        //Draw background or border quad
2273        if matches!(self.shape, CursorShape::Hollow) {
2274            scene.push_quad(Quad {
2275                bounds,
2276                background: None,
2277                border: Border::all(1., self.color),
2278                corner_radius: 0.,
2279            });
2280        } else {
2281            scene.push_quad(Quad {
2282                bounds,
2283                background: Some(self.color),
2284                border: Default::default(),
2285                corner_radius: 0.,
2286            });
2287        }
2288
2289        if let Some(block_text) = &self.block_text {
2290            block_text.paint(scene, self.origin + origin, bounds, self.line_height, cx);
2291        }
2292    }
2293
2294    pub fn shape(&self) -> CursorShape {
2295        self.shape
2296    }
2297}
2298
2299#[derive(Debug)]
2300pub struct HighlightedRange {
2301    pub start_y: f32,
2302    pub line_height: f32,
2303    pub lines: Vec<HighlightedRangeLine>,
2304    pub color: Color,
2305    pub corner_radius: f32,
2306}
2307
2308#[derive(Debug)]
2309pub struct HighlightedRangeLine {
2310    pub start_x: f32,
2311    pub end_x: f32,
2312}
2313
2314impl HighlightedRange {
2315    pub fn paint(&self, bounds: RectF, scene: &mut SceneBuilder) {
2316        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
2317            self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
2318            self.paint_lines(
2319                self.start_y + self.line_height,
2320                &self.lines[1..],
2321                bounds,
2322                scene,
2323            );
2324        } else {
2325            self.paint_lines(self.start_y, &self.lines, bounds, scene);
2326        }
2327    }
2328
2329    fn paint_lines(
2330        &self,
2331        start_y: f32,
2332        lines: &[HighlightedRangeLine],
2333        bounds: RectF,
2334        scene: &mut SceneBuilder,
2335    ) {
2336        if lines.is_empty() {
2337            return;
2338        }
2339
2340        let mut path = PathBuilder::new();
2341        let first_line = lines.first().unwrap();
2342        let last_line = lines.last().unwrap();
2343
2344        let first_top_left = vec2f(first_line.start_x, start_y);
2345        let first_top_right = vec2f(first_line.end_x, start_y);
2346
2347        let curve_height = vec2f(0., self.corner_radius);
2348        let curve_width = |start_x: f32, end_x: f32| {
2349            let max = (end_x - start_x) / 2.;
2350            let width = if max < self.corner_radius {
2351                max
2352            } else {
2353                self.corner_radius
2354            };
2355
2356            vec2f(width, 0.)
2357        };
2358
2359        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
2360        path.reset(first_top_right - top_curve_width);
2361        path.curve_to(first_top_right + curve_height, first_top_right);
2362
2363        let mut iter = lines.iter().enumerate().peekable();
2364        while let Some((ix, line)) = iter.next() {
2365            let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
2366
2367            if let Some((_, next_line)) = iter.peek() {
2368                let next_top_right = vec2f(next_line.end_x, bottom_right.y());
2369
2370                match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
2371                    Ordering::Equal => {
2372                        path.line_to(bottom_right);
2373                    }
2374                    Ordering::Less => {
2375                        let curve_width = curve_width(next_top_right.x(), bottom_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                    Ordering::Greater => {
2386                        let curve_width = curve_width(bottom_right.x(), next_top_right.x());
2387                        path.line_to(bottom_right - curve_height);
2388                        if self.corner_radius > 0. {
2389                            path.curve_to(bottom_right + curve_width, bottom_right);
2390                        }
2391                        path.line_to(next_top_right - curve_width);
2392                        if self.corner_radius > 0. {
2393                            path.curve_to(next_top_right + curve_height, next_top_right);
2394                        }
2395                    }
2396                }
2397            } else {
2398                let curve_width = curve_width(line.start_x, line.end_x);
2399                path.line_to(bottom_right - curve_height);
2400                if self.corner_radius > 0. {
2401                    path.curve_to(bottom_right - curve_width, bottom_right);
2402                }
2403
2404                let bottom_left = vec2f(line.start_x, bottom_right.y());
2405                path.line_to(bottom_left + curve_width);
2406                if self.corner_radius > 0. {
2407                    path.curve_to(bottom_left - curve_height, bottom_left);
2408                }
2409            }
2410        }
2411
2412        if first_line.start_x > last_line.start_x {
2413            let curve_width = curve_width(last_line.start_x, first_line.start_x);
2414            let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
2415            path.line_to(second_top_left + curve_height);
2416            if self.corner_radius > 0. {
2417                path.curve_to(second_top_left + curve_width, second_top_left);
2418            }
2419            let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
2420            path.line_to(first_bottom_left - curve_width);
2421            if self.corner_radius > 0. {
2422                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
2423            }
2424        }
2425
2426        path.line_to(first_top_left + curve_height);
2427        if self.corner_radius > 0. {
2428            path.curve_to(first_top_left + top_curve_width, first_top_left);
2429        }
2430        path.line_to(first_top_right - top_curve_width);
2431
2432        scene.push_path(path.build(self.color, Some(bounds)));
2433    }
2434}
2435
2436pub fn position_to_display_point(
2437    position: Vector2F,
2438    text_bounds: RectF,
2439    position_map: &PositionMap,
2440) -> Option<DisplayPoint> {
2441    if text_bounds.contains_point(position) {
2442        let (point, target_point) = position_map.point_for_position(text_bounds, position);
2443        if point == target_point {
2444            Some(point)
2445        } else {
2446            None
2447        }
2448    } else {
2449        None
2450    }
2451}
2452
2453pub fn range_to_bounds(
2454    range: &Range<DisplayPoint>,
2455    content_origin: Vector2F,
2456    scroll_left: f32,
2457    scroll_top: f32,
2458    visible_row_range: &Range<u32>,
2459    line_end_overshoot: f32,
2460    position_map: &PositionMap,
2461) -> impl Iterator<Item = RectF> {
2462    let mut bounds: SmallVec<[RectF; 1]> = SmallVec::new();
2463
2464    if range.start == range.end {
2465        return bounds.into_iter();
2466    }
2467
2468    let start_row = visible_row_range.start;
2469    let end_row = visible_row_range.end;
2470
2471    let row_range = if range.end.column() == 0 {
2472        cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
2473    } else {
2474        cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
2475    };
2476
2477    let first_y =
2478        content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top;
2479
2480    for (idx, row) in row_range.enumerate() {
2481        let line_layout = &position_map.line_layouts[(row - start_row) as usize];
2482
2483        let start_x = if row == range.start.row() {
2484            content_origin.x() + line_layout.x_for_index(range.start.column() as usize)
2485                - scroll_left
2486        } else {
2487            content_origin.x() - scroll_left
2488        };
2489
2490        let end_x = if row == range.end.row() {
2491            content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left
2492        } else {
2493            content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left
2494        };
2495
2496        bounds.push(RectF::from_points(
2497            vec2f(start_x, first_y + position_map.line_height * idx as f32),
2498            vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32),
2499        ))
2500    }
2501
2502    bounds.into_iter()
2503}
2504
2505pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
2506    delta.powf(1.5) / 100.0
2507}
2508
2509fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
2510    delta.powf(1.2) / 300.0
2511}
2512
2513#[cfg(test)]
2514mod tests {
2515    use super::*;
2516    use crate::{
2517        display_map::{BlockDisposition, BlockProperties},
2518        Editor, MultiBuffer,
2519    };
2520    use gpui::TestAppContext;
2521    use settings::Settings;
2522    use std::sync::Arc;
2523    use util::test::sample_text;
2524
2525    #[gpui::test]
2526    fn test_layout_line_numbers(cx: &mut TestAppContext) {
2527        cx.update(|cx| cx.set_global(Settings::test(cx)));
2528        let (_, editor) = cx.add_window(|cx| {
2529            let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
2530            Editor::new(EditorMode::Full, buffer, None, None, cx)
2531        });
2532        let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
2533
2534        let layouts = editor.update(cx, |editor, cx| {
2535            let snapshot = editor.snapshot(cx);
2536            element
2537                .layout_line_numbers(0..6, &Default::default(), false, &snapshot, cx)
2538                .0
2539        });
2540        assert_eq!(layouts.len(), 6);
2541    }
2542
2543    #[gpui::test]
2544    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
2545        cx.update(|cx| cx.set_global(Settings::test(cx)));
2546        let (_, editor) = cx.add_window(|cx| {
2547            let buffer = MultiBuffer::build_simple("", cx);
2548            Editor::new(EditorMode::Full, buffer, None, None, cx)
2549        });
2550
2551        editor.update(cx, |editor, cx| {
2552            editor.set_placeholder_text("hello", cx);
2553            editor.insert_blocks(
2554                [BlockProperties {
2555                    style: BlockStyle::Fixed,
2556                    disposition: BlockDisposition::Above,
2557                    height: 3,
2558                    position: Anchor::min(),
2559                    render: Arc::new(|_| Empty::new().into_any()),
2560                }],
2561                cx,
2562            );
2563
2564            // Blur the editor so that it displays placeholder text.
2565            cx.blur();
2566        });
2567
2568        let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
2569        let (size, mut state) = editor.update(cx, |editor, cx| {
2570            element.layout(
2571                SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
2572                editor,
2573                cx,
2574            )
2575        });
2576
2577        assert_eq!(state.position_map.line_layouts.len(), 4);
2578        assert_eq!(
2579            state
2580                .line_number_layouts
2581                .iter()
2582                .map(Option::is_some)
2583                .collect::<Vec<_>>(),
2584            &[false, false, false, true]
2585        );
2586
2587        // Don't panic.
2588        let mut scene = SceneBuilder::new(1.0);
2589        let bounds = RectF::new(Default::default(), size);
2590        editor.update(cx, |editor, cx| {
2591            element.paint(&mut scene, bounds, bounds, &mut state, editor, cx);
2592        });
2593    }
2594}