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