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