element.rs

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