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 mut diff_hunks = snapshot
1046            .buffer_snapshot
1047            .git_diff_hunks_in_range(rows.clone())
1048            .peekable();
1049
1050        //Some number followed by Nones for wrapped lines
1051        //Jump in number for folded lines
1052        let mut buffer_rows = snapshot
1053            .buffer_rows(rows.start)
1054            .take((rows.end - rows.start) as usize)
1055            .enumerate()
1056            .peekable();
1057
1058        let mut layouts = Vec::new();
1059        let mut previous_buffer_row = None;
1060
1061        while let Some((idx, buffer_row)) = buffer_rows.next() {
1062            let buffer_row = match buffer_row {
1063                Some(buffer_row) => buffer_row,
1064                None => continue,
1065            };
1066
1067            let is_start_of_fold = previous_buffer_row
1068                .map(|prev| buffer_row > prev + 1)
1069                .unwrap_or(false);
1070            previous_buffer_row = Some(buffer_row);
1071
1072            if is_start_of_fold {
1073                //Consume all hunks within fold
1074                let mut consumed_hunks = false;
1075                while let Some(hunk) = diff_hunks.peek() {
1076                    let is_past = hunk.buffer_range.start > buffer_row;
1077                    let is_removal = hunk.status() == DiffHunkStatus::Removed;
1078                    let is_on_next_line = hunk.buffer_range.start == buffer_row + 1;
1079                    let is_removal_inside = is_removal && is_on_next_line;
1080
1081                    if is_past && !is_removal_inside {
1082                        break;
1083                    }
1084                    diff_hunks.next();
1085                    consumed_hunks = true;
1086                }
1087
1088                //And mark fold as modified if there were any
1089                if consumed_hunks {
1090                    let current_visual_row = rows.start + idx as u32 - 1;
1091                    layouts.push(DiffHunkLayout {
1092                        visual_range: current_visual_row..current_visual_row + 1,
1093                        status: DiffHunkStatus::Modified,
1094                    });
1095                }
1096            } else if let Some(hunk) = diff_hunks.peek() {
1097                let row_inside_hunk = hunk.buffer_range.contains(&buffer_row);
1098                let starts_on_row = hunk.buffer_range.start == buffer_row;
1099                if row_inside_hunk || starts_on_row {
1100                    let (layout, buffer_row_advancement) =
1101                        Self::layout_diff_hunk(hunk, rows.start, &mut buffer_rows);
1102                    previous_buffer_row = Some(buffer_row + buffer_row_advancement);
1103
1104                    if let Some(layout) = layout {
1105                        layouts.push(layout);
1106                    }
1107
1108                    diff_hunks.next();
1109                }
1110            }
1111        }
1112
1113        layouts
1114    }
1115
1116    fn layout_line_numbers(
1117        &self,
1118        rows: Range<u32>,
1119        active_rows: &BTreeMap<u32, bool>,
1120        snapshot: &EditorSnapshot,
1121        cx: &LayoutContext,
1122    ) -> Vec<Option<text_layout::Line>> {
1123        let style = &self.style;
1124        let include_line_numbers = snapshot.mode == EditorMode::Full;
1125        let mut line_number_layouts = Vec::with_capacity(rows.len());
1126        let mut line_number = String::new();
1127        for (ix, row) in snapshot
1128            .buffer_rows(rows.start)
1129            .take((rows.end - rows.start) as usize)
1130            .enumerate()
1131        {
1132            let display_row = rows.start + ix as u32;
1133            let color = if active_rows.contains_key(&display_row) {
1134                style.line_number_active
1135            } else {
1136                style.line_number
1137            };
1138            if let Some(buffer_row) = row {
1139                if include_line_numbers {
1140                    line_number.clear();
1141                    write!(&mut line_number, "{}", buffer_row + 1).unwrap();
1142                    line_number_layouts.push(Some(cx.text_layout_cache.layout_str(
1143                        &line_number,
1144                        style.text.font_size,
1145                        &[(
1146                            line_number.len(),
1147                            RunStyle {
1148                                font_id: style.text.font_id,
1149                                color,
1150                                underline: Default::default(),
1151                            },
1152                        )],
1153                    )));
1154                }
1155            } else {
1156                line_number_layouts.push(None);
1157            }
1158        }
1159
1160        line_number_layouts
1161    }
1162
1163    fn layout_lines(
1164        &mut self,
1165        rows: Range<u32>,
1166        snapshot: &EditorSnapshot,
1167        cx: &LayoutContext,
1168    ) -> Vec<text_layout::Line> {
1169        if rows.start >= rows.end {
1170            return Vec::new();
1171        }
1172
1173        // When the editor is empty and unfocused, then show the placeholder.
1174        if snapshot.is_empty() && !snapshot.is_focused() {
1175            let placeholder_style = self
1176                .style
1177                .placeholder_text
1178                .as_ref()
1179                .unwrap_or(&self.style.text);
1180            let placeholder_text = snapshot.placeholder_text();
1181            let placeholder_lines = placeholder_text
1182                .as_ref()
1183                .map_or("", AsRef::as_ref)
1184                .split('\n')
1185                .skip(rows.start as usize)
1186                .chain(iter::repeat(""))
1187                .take(rows.len());
1188            placeholder_lines
1189                .map(|line| {
1190                    cx.text_layout_cache.layout_str(
1191                        line,
1192                        placeholder_style.font_size,
1193                        &[(
1194                            line.len(),
1195                            RunStyle {
1196                                font_id: placeholder_style.font_id,
1197                                color: placeholder_style.color,
1198                                underline: Default::default(),
1199                            },
1200                        )],
1201                    )
1202                })
1203                .collect()
1204        } else {
1205            let style = &self.style;
1206            let chunks = snapshot.chunks(rows.clone(), true).map(|chunk| {
1207                let mut highlight_style = chunk
1208                    .syntax_highlight_id
1209                    .and_then(|id| id.style(&style.syntax));
1210
1211                if let Some(chunk_highlight) = chunk.highlight_style {
1212                    if let Some(highlight_style) = highlight_style.as_mut() {
1213                        highlight_style.highlight(chunk_highlight);
1214                    } else {
1215                        highlight_style = Some(chunk_highlight);
1216                    }
1217                }
1218
1219                let mut diagnostic_highlight = HighlightStyle::default();
1220
1221                if chunk.is_unnecessary {
1222                    diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
1223                }
1224
1225                if let Some(severity) = chunk.diagnostic_severity {
1226                    // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
1227                    if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
1228                        let diagnostic_style = super::diagnostic_style(severity, true, style);
1229                        diagnostic_highlight.underline = Some(Underline {
1230                            color: Some(diagnostic_style.message.text.color),
1231                            thickness: 1.0.into(),
1232                            squiggly: true,
1233                        });
1234                    }
1235                }
1236
1237                if let Some(highlight_style) = highlight_style.as_mut() {
1238                    highlight_style.highlight(diagnostic_highlight);
1239                } else {
1240                    highlight_style = Some(diagnostic_highlight);
1241                }
1242
1243                (chunk.text, highlight_style)
1244            });
1245            layout_highlighted_chunks(
1246                chunks,
1247                &style.text,
1248                cx.text_layout_cache,
1249                cx.font_cache,
1250                MAX_LINE_LEN,
1251                rows.len() as usize,
1252            )
1253        }
1254    }
1255
1256    #[allow(clippy::too_many_arguments)]
1257    fn layout_blocks(
1258        &mut self,
1259        rows: Range<u32>,
1260        snapshot: &EditorSnapshot,
1261        editor_width: f32,
1262        scroll_width: f32,
1263        gutter_padding: f32,
1264        gutter_width: f32,
1265        em_width: f32,
1266        text_x: f32,
1267        line_height: f32,
1268        style: &EditorStyle,
1269        line_layouts: &[text_layout::Line],
1270        cx: &mut LayoutContext,
1271    ) -> (f32, Vec<BlockLayout>) {
1272        let editor = if let Some(editor) = self.view.upgrade(cx) {
1273            editor
1274        } else {
1275            return Default::default();
1276        };
1277
1278        let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
1279        let scroll_x = snapshot.scroll_position.x();
1280        let (fixed_blocks, non_fixed_blocks) = snapshot
1281            .blocks_in_range(rows.clone())
1282            .partition::<Vec<_>, _>(|(_, block)| match block {
1283                TransformBlock::ExcerptHeader { .. } => false,
1284                TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
1285            });
1286        let mut render_block = |block: &TransformBlock, width: f32| {
1287            let mut element = match block {
1288                TransformBlock::Custom(block) => {
1289                    let align_to = block
1290                        .position()
1291                        .to_point(&snapshot.buffer_snapshot)
1292                        .to_display_point(snapshot);
1293                    let anchor_x = text_x
1294                        + if rows.contains(&align_to.row()) {
1295                            line_layouts[(align_to.row() - rows.start) as usize]
1296                                .x_for_index(align_to.column() as usize)
1297                        } else {
1298                            layout_line(align_to.row(), snapshot, style, cx.text_layout_cache)
1299                                .x_for_index(align_to.column() as usize)
1300                        };
1301
1302                    cx.render(&editor, |_, cx| {
1303                        block.render(&mut BlockContext {
1304                            cx,
1305                            anchor_x,
1306                            gutter_padding,
1307                            line_height,
1308                            scroll_x,
1309                            gutter_width,
1310                            em_width,
1311                        })
1312                    })
1313                }
1314                TransformBlock::ExcerptHeader {
1315                    key,
1316                    buffer,
1317                    range,
1318                    starts_new_buffer,
1319                    ..
1320                } => {
1321                    let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
1322                        let jump_position = range
1323                            .primary
1324                            .as_ref()
1325                            .map_or(range.context.start, |primary| primary.start);
1326                        let jump_action = crate::Jump {
1327                            path: ProjectPath {
1328                                worktree_id: file.worktree_id(cx),
1329                                path: file.path.clone(),
1330                            },
1331                            position: language::ToPoint::to_point(&jump_position, buffer),
1332                            anchor: jump_position,
1333                        };
1334
1335                        enum JumpIcon {}
1336                        cx.render(&editor, |_, cx| {
1337                            MouseEventHandler::<JumpIcon>::new(*key, cx, |state, _| {
1338                                let style = style.jump_icon.style_for(state, false);
1339                                Svg::new("icons/arrow_up_right_8.svg")
1340                                    .with_color(style.color)
1341                                    .constrained()
1342                                    .with_width(style.icon_width)
1343                                    .aligned()
1344                                    .contained()
1345                                    .with_style(style.container)
1346                                    .constrained()
1347                                    .with_width(style.button_width)
1348                                    .with_height(style.button_width)
1349                                    .boxed()
1350                            })
1351                            .with_cursor_style(CursorStyle::PointingHand)
1352                            .on_click(MouseButton::Left, move |_, cx| {
1353                                cx.dispatch_action(jump_action.clone())
1354                            })
1355                            .with_tooltip::<JumpIcon, _>(
1356                                *key,
1357                                "Jump to Buffer".to_string(),
1358                                Some(Box::new(crate::OpenExcerpts)),
1359                                tooltip_style.clone(),
1360                                cx,
1361                            )
1362                            .aligned()
1363                            .flex_float()
1364                            .boxed()
1365                        })
1366                    });
1367
1368                    if *starts_new_buffer {
1369                        let style = &self.style.diagnostic_path_header;
1370                        let font_size =
1371                            (style.text_scale_factor * self.style.text.font_size).round();
1372
1373                        let mut filename = None;
1374                        let mut parent_path = None;
1375                        if let Some(file) = buffer.file() {
1376                            let path = file.path();
1377                            filename = path.file_name().map(|f| f.to_string_lossy().to_string());
1378                            parent_path =
1379                                path.parent().map(|p| p.to_string_lossy().to_string() + "/");
1380                        }
1381
1382                        Flex::row()
1383                            .with_child(
1384                                Label::new(
1385                                    filename.unwrap_or_else(|| "untitled".to_string()),
1386                                    style.filename.text.clone().with_font_size(font_size),
1387                                )
1388                                .contained()
1389                                .with_style(style.filename.container)
1390                                .aligned()
1391                                .boxed(),
1392                            )
1393                            .with_children(parent_path.map(|path| {
1394                                Label::new(path, style.path.text.clone().with_font_size(font_size))
1395                                    .contained()
1396                                    .with_style(style.path.container)
1397                                    .aligned()
1398                                    .boxed()
1399                            }))
1400                            .with_children(jump_icon)
1401                            .contained()
1402                            .with_style(style.container)
1403                            .with_padding_left(gutter_padding)
1404                            .with_padding_right(gutter_padding)
1405                            .expanded()
1406                            .named("path header block")
1407                    } else {
1408                        let text_style = self.style.text.clone();
1409                        Flex::row()
1410                            .with_child(Label::new("".to_string(), text_style).boxed())
1411                            .with_children(jump_icon)
1412                            .contained()
1413                            .with_padding_left(gutter_padding)
1414                            .with_padding_right(gutter_padding)
1415                            .expanded()
1416                            .named("collapsed context")
1417                    }
1418                }
1419            };
1420
1421            element.layout(
1422                SizeConstraint {
1423                    min: Vector2F::zero(),
1424                    max: vec2f(width, block.height() as f32 * line_height),
1425                },
1426                cx,
1427            );
1428            element
1429        };
1430
1431        let mut fixed_block_max_width = 0f32;
1432        let mut blocks = Vec::new();
1433        for (row, block) in fixed_blocks {
1434            let element = render_block(block, f32::INFINITY);
1435            fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width);
1436            blocks.push(BlockLayout {
1437                row,
1438                element,
1439                style: BlockStyle::Fixed,
1440            });
1441        }
1442        for (row, block) in non_fixed_blocks {
1443            let style = match block {
1444                TransformBlock::Custom(block) => block.style(),
1445                TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
1446            };
1447            let width = match style {
1448                BlockStyle::Sticky => editor_width,
1449                BlockStyle::Flex => editor_width
1450                    .max(fixed_block_max_width)
1451                    .max(gutter_width + scroll_width),
1452                BlockStyle::Fixed => unreachable!(),
1453            };
1454            let element = render_block(block, width);
1455            blocks.push(BlockLayout {
1456                row,
1457                element,
1458                style,
1459            });
1460        }
1461        (
1462            scroll_width.max(fixed_block_max_width - gutter_width),
1463            blocks,
1464        )
1465    }
1466}
1467
1468/// Get the hunk that contains buffer_line, starting from start_idx
1469/// Returns none if there is none found, and
1470fn get_hunk(hunks: &[DiffHunk<u32>], buffer_line: u32) -> Option<&DiffHunk<u32>> {
1471    for i in 0..hunks.len() {
1472        // Safety: Index out of bounds is handled by the check above
1473        let hunk = hunks.get(i).unwrap();
1474        if hunk.buffer_range.contains(&(buffer_line as u32)) {
1475            return Some(hunk);
1476        } else if hunk.status() == DiffHunkStatus::Removed && buffer_line == hunk.buffer_range.start
1477        {
1478            return Some(hunk);
1479        } else if hunk.buffer_range.start > buffer_line as u32 {
1480            // If we've passed the buffer_line, just stop
1481            return None;
1482        }
1483    }
1484
1485    // We reached the end of the array without finding a hunk, just return none.
1486    return None;
1487}
1488
1489impl Element for EditorElement {
1490    type LayoutState = LayoutState;
1491    type PaintState = ();
1492
1493    fn layout(
1494        &mut self,
1495        constraint: SizeConstraint,
1496        cx: &mut LayoutContext,
1497    ) -> (Vector2F, Self::LayoutState) {
1498        let mut size = constraint.max;
1499        if size.x().is_infinite() {
1500            unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
1501        }
1502
1503        let snapshot = self.snapshot(cx.app);
1504        let style = self.style.clone();
1505        let line_height = style.text.line_height(cx.font_cache);
1506
1507        let gutter_padding;
1508        let gutter_width;
1509        let gutter_margin;
1510        if snapshot.mode == EditorMode::Full {
1511            gutter_padding = style.text.em_width(cx.font_cache) * style.gutter_padding_factor;
1512            gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
1513            gutter_margin = -style.text.descent(cx.font_cache);
1514        } else {
1515            gutter_padding = 0.0;
1516            gutter_width = 0.0;
1517            gutter_margin = 0.0;
1518        };
1519
1520        let text_width = size.x() - gutter_width;
1521        let em_width = style.text.em_width(cx.font_cache);
1522        let em_advance = style.text.em_advance(cx.font_cache);
1523        let overscroll = vec2f(em_width, 0.);
1524        let snapshot = self.update_view(cx.app, |view, cx| {
1525            view.set_visible_line_count(size.y() / line_height);
1526
1527            let wrap_width = match view.soft_wrap_mode(cx) {
1528                SoftWrap::None => Some((MAX_LINE_LEN / 2) as f32 * em_advance),
1529                SoftWrap::EditorWidth => {
1530                    Some(text_width - gutter_margin - overscroll.x() - em_width)
1531                }
1532                SoftWrap::Column(column) => Some(column as f32 * em_advance),
1533            };
1534
1535            if view.set_wrap_width(wrap_width, cx) {
1536                view.snapshot(cx)
1537            } else {
1538                snapshot
1539            }
1540        });
1541
1542        let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
1543        if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
1544            size.set_y(
1545                scroll_height
1546                    .min(constraint.max_along(Axis::Vertical))
1547                    .max(constraint.min_along(Axis::Vertical))
1548                    .min(line_height * max_lines as f32),
1549            )
1550        } else if let EditorMode::SingleLine = snapshot.mode {
1551            size.set_y(
1552                line_height
1553                    .min(constraint.max_along(Axis::Vertical))
1554                    .max(constraint.min_along(Axis::Vertical)),
1555            )
1556        } else if size.y().is_infinite() {
1557            size.set_y(scroll_height);
1558        }
1559        let gutter_size = vec2f(gutter_width, size.y());
1560        let text_size = vec2f(text_width, size.y());
1561
1562        let (autoscroll_horizontally, mut snapshot) = self.update_view(cx.app, |view, cx| {
1563            let autoscroll_horizontally = view.autoscroll_vertically(size.y(), line_height, cx);
1564            let snapshot = view.snapshot(cx);
1565            (autoscroll_horizontally, snapshot)
1566        });
1567
1568        let scroll_position = snapshot.scroll_position();
1569        // The scroll position is a fractional point, the whole number of which represents
1570        // the top of the window in terms of display rows.
1571        let start_row = scroll_position.y() as u32;
1572        let scroll_top = scroll_position.y() * line_height;
1573
1574        // Add 1 to ensure selections bleed off screen
1575        let end_row = 1 + cmp::min(
1576            ((scroll_top + size.y()) / line_height).ceil() as u32,
1577            snapshot.max_point().row(),
1578        );
1579
1580        let start_anchor = if start_row == 0 {
1581            Anchor::min()
1582        } else {
1583            snapshot
1584                .buffer_snapshot
1585                .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
1586        };
1587        let end_anchor = if end_row > snapshot.max_point().row() {
1588            Anchor::max()
1589        } else {
1590            snapshot
1591                .buffer_snapshot
1592                .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
1593        };
1594
1595        let mut selections: Vec<(ReplicaId, Vec<SelectionLayout>)> = Vec::new();
1596        let mut active_rows = BTreeMap::new();
1597        let mut highlighted_rows = None;
1598        let mut highlighted_ranges = Vec::new();
1599        self.update_view(cx.app, |view, cx| {
1600            let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx));
1601
1602            highlighted_rows = view.highlighted_rows();
1603            let theme = cx.global::<Settings>().theme.as_ref();
1604            highlighted_ranges = view.background_highlights_in_range(
1605                start_anchor.clone()..end_anchor.clone(),
1606                &display_map,
1607                theme,
1608            );
1609
1610            let mut remote_selections = HashMap::default();
1611            for (replica_id, line_mode, selection) in display_map
1612                .buffer_snapshot
1613                .remote_selections_in_range(&(start_anchor.clone()..end_anchor.clone()))
1614            {
1615                // The local selections match the leader's selections.
1616                if Some(replica_id) == view.leader_replica_id {
1617                    continue;
1618                }
1619                remote_selections
1620                    .entry(replica_id)
1621                    .or_insert(Vec::new())
1622                    .push(SelectionLayout::new(selection, line_mode, &display_map));
1623            }
1624            selections.extend(remote_selections);
1625
1626            if view.show_local_selections {
1627                let mut local_selections = view
1628                    .selections
1629                    .disjoint_in_range(start_anchor..end_anchor, cx);
1630                local_selections.extend(view.selections.pending(cx));
1631                for selection in &local_selections {
1632                    let is_empty = selection.start == selection.end;
1633                    let selection_start = snapshot.prev_line_boundary(selection.start).1;
1634                    let selection_end = snapshot.next_line_boundary(selection.end).1;
1635                    for row in cmp::max(selection_start.row(), start_row)
1636                        ..=cmp::min(selection_end.row(), end_row)
1637                    {
1638                        let contains_non_empty_selection =
1639                            active_rows.entry(row).or_insert(!is_empty);
1640                        *contains_non_empty_selection |= !is_empty;
1641                    }
1642                }
1643
1644                // Render the local selections in the leader's color when following.
1645                let local_replica_id = view
1646                    .leader_replica_id
1647                    .unwrap_or_else(|| view.replica_id(cx));
1648
1649                selections.push((
1650                    local_replica_id,
1651                    local_selections
1652                        .into_iter()
1653                        .map(|selection| {
1654                            SelectionLayout::new(selection, view.selections.line_mode, &display_map)
1655                        })
1656                        .collect(),
1657                ));
1658            }
1659        });
1660
1661        let line_number_layouts =
1662            self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx);
1663
1664        let hunk_layouts = self.layout_git_gutters(start_row..end_row, &snapshot);
1665
1666        let mut max_visible_line_width = 0.0;
1667        let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx);
1668        for line in &line_layouts {
1669            if line.width() > max_visible_line_width {
1670                max_visible_line_width = line.width();
1671            }
1672        }
1673
1674        let style = self.style.clone();
1675        let longest_line_width = layout_line(
1676            snapshot.longest_row(),
1677            &snapshot,
1678            &style,
1679            cx.text_layout_cache,
1680        )
1681        .width();
1682        let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x();
1683        let em_width = style.text.em_width(cx.font_cache);
1684        let (scroll_width, blocks) = self.layout_blocks(
1685            start_row..end_row,
1686            &snapshot,
1687            size.x(),
1688            scroll_width,
1689            gutter_padding,
1690            gutter_width,
1691            em_width,
1692            gutter_width + gutter_margin,
1693            line_height,
1694            &style,
1695            &line_layouts,
1696            cx,
1697        );
1698
1699        let max_row = snapshot.max_point().row();
1700        let scroll_max = vec2f(
1701            ((scroll_width - text_size.x()) / em_width).max(0.0),
1702            max_row.saturating_sub(1) as f32,
1703        );
1704
1705        self.update_view(cx.app, |view, cx| {
1706            let clamped = view.clamp_scroll_left(scroll_max.x());
1707
1708            let autoscrolled = if autoscroll_horizontally {
1709                view.autoscroll_horizontally(
1710                    start_row,
1711                    text_size.x(),
1712                    scroll_width,
1713                    em_width,
1714                    &line_layouts,
1715                    cx,
1716                )
1717            } else {
1718                false
1719            };
1720
1721            if clamped || autoscrolled {
1722                snapshot = view.snapshot(cx);
1723            }
1724        });
1725
1726        let mut context_menu = None;
1727        let mut code_actions_indicator = None;
1728        let mut hover = None;
1729        cx.render(&self.view.upgrade(cx).unwrap(), |view, cx| {
1730            let newest_selection_head = view
1731                .selections
1732                .newest::<usize>(cx)
1733                .head()
1734                .to_display_point(&snapshot);
1735
1736            let style = view.style(cx);
1737            if (start_row..end_row).contains(&newest_selection_head.row()) {
1738                if view.context_menu_visible() {
1739                    context_menu =
1740                        view.render_context_menu(newest_selection_head, style.clone(), cx);
1741                }
1742
1743                code_actions_indicator = view
1744                    .render_code_actions_indicator(&style, cx)
1745                    .map(|indicator| (newest_selection_head.row(), indicator));
1746            }
1747
1748            let visible_rows = start_row..start_row + line_layouts.len() as u32;
1749            hover = view.hover_state.render(&snapshot, &style, visible_rows, cx);
1750        });
1751
1752        if let Some((_, context_menu)) = context_menu.as_mut() {
1753            context_menu.layout(
1754                SizeConstraint {
1755                    min: Vector2F::zero(),
1756                    max: vec2f(
1757                        cx.window_size.x() * 0.7,
1758                        (12. * line_height).min((size.y() - line_height) / 2.),
1759                    ),
1760                },
1761                cx,
1762            );
1763        }
1764
1765        if let Some((_, indicator)) = code_actions_indicator.as_mut() {
1766            indicator.layout(
1767                SizeConstraint::strict_along(
1768                    Axis::Vertical,
1769                    line_height * style.code_actions.vertical_scale,
1770                ),
1771                cx,
1772            );
1773        }
1774
1775        if let Some((_, hover_popovers)) = hover.as_mut() {
1776            for hover_popover in hover_popovers.iter_mut() {
1777                hover_popover.layout(
1778                    SizeConstraint {
1779                        min: Vector2F::zero(),
1780                        max: vec2f(
1781                            (120. * em_width) // Default size
1782                                .min(size.x() / 2.) // Shrink to half of the editor width
1783                                .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
1784                            (16. * line_height) // Default size
1785                                .min(size.y() / 2.) // Shrink to half of the editor height
1786                                .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
1787                        ),
1788                    },
1789                    cx,
1790                );
1791            }
1792        }
1793
1794        (
1795            size,
1796            LayoutState {
1797                position_map: Arc::new(PositionMap {
1798                    size,
1799                    scroll_max,
1800                    line_layouts,
1801                    line_height,
1802                    em_width,
1803                    em_advance,
1804                    snapshot,
1805                }),
1806                gutter_size,
1807                gutter_padding,
1808                text_size,
1809                gutter_margin,
1810                active_rows,
1811                highlighted_rows,
1812                highlighted_ranges,
1813                line_number_layouts,
1814                hunk_layouts,
1815                blocks,
1816                selections,
1817                context_menu,
1818                code_actions_indicator,
1819                hover_popovers: hover,
1820            },
1821        )
1822    }
1823
1824    fn paint(
1825        &mut self,
1826        bounds: RectF,
1827        visible_bounds: RectF,
1828        layout: &mut Self::LayoutState,
1829        cx: &mut PaintContext,
1830    ) -> Self::PaintState {
1831        cx.scene.push_layer(Some(bounds));
1832
1833        let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
1834        let text_bounds = RectF::new(
1835            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
1836            layout.text_size,
1837        );
1838
1839        Self::attach_mouse_handlers(
1840            &self.view,
1841            &layout.position_map,
1842            visible_bounds,
1843            text_bounds,
1844            gutter_bounds,
1845            bounds,
1846            cx,
1847        );
1848
1849        self.paint_background(gutter_bounds, text_bounds, layout, cx);
1850        if layout.gutter_size.x() > 0. {
1851            self.paint_gutter(gutter_bounds, visible_bounds, layout, cx);
1852        }
1853        self.paint_text(text_bounds, visible_bounds, layout, cx);
1854
1855        if !layout.blocks.is_empty() {
1856            cx.scene.push_layer(Some(bounds));
1857            self.paint_blocks(bounds, visible_bounds, layout, cx);
1858            cx.scene.pop_layer();
1859        }
1860
1861        cx.scene.pop_layer();
1862    }
1863
1864    fn dispatch_event(
1865        &mut self,
1866        event: &Event,
1867        _: RectF,
1868        _: RectF,
1869        _: &mut LayoutState,
1870        _: &mut (),
1871        cx: &mut EventContext,
1872    ) -> bool {
1873        if let Event::ModifiersChanged(event) = event {
1874            self.modifiers_changed(*event, cx);
1875        }
1876
1877        false
1878    }
1879
1880    fn rect_for_text_range(
1881        &self,
1882        range_utf16: Range<usize>,
1883        bounds: RectF,
1884        _: RectF,
1885        layout: &Self::LayoutState,
1886        _: &Self::PaintState,
1887        _: &gpui::MeasurementContext,
1888    ) -> Option<RectF> {
1889        let text_bounds = RectF::new(
1890            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
1891            layout.text_size,
1892        );
1893        let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.);
1894        let scroll_position = layout.position_map.snapshot.scroll_position();
1895        let start_row = scroll_position.y() as u32;
1896        let scroll_top = scroll_position.y() * layout.position_map.line_height;
1897        let scroll_left = scroll_position.x() * layout.position_map.em_width;
1898
1899        let range_start = OffsetUtf16(range_utf16.start)
1900            .to_display_point(&layout.position_map.snapshot.display_snapshot);
1901        if range_start.row() < start_row {
1902            return None;
1903        }
1904
1905        let line = layout
1906            .position_map
1907            .line_layouts
1908            .get((range_start.row() - start_row) as usize)?;
1909        let range_start_x = line.x_for_index(range_start.column() as usize);
1910        let range_start_y = range_start.row() as f32 * layout.position_map.line_height;
1911        Some(RectF::new(
1912            content_origin
1913                + vec2f(
1914                    range_start_x,
1915                    range_start_y + layout.position_map.line_height,
1916                )
1917                - vec2f(scroll_left, scroll_top),
1918            vec2f(
1919                layout.position_map.em_width,
1920                layout.position_map.line_height,
1921            ),
1922        ))
1923    }
1924
1925    fn debug(
1926        &self,
1927        bounds: RectF,
1928        _: &Self::LayoutState,
1929        _: &Self::PaintState,
1930        _: &gpui::DebugContext,
1931    ) -> json::Value {
1932        json!({
1933            "type": "BufferElement",
1934            "bounds": bounds.to_json()
1935        })
1936    }
1937}
1938
1939pub struct LayoutState {
1940    position_map: Arc<PositionMap>,
1941    gutter_size: Vector2F,
1942    gutter_padding: f32,
1943    gutter_margin: f32,
1944    text_size: Vector2F,
1945    active_rows: BTreeMap<u32, bool>,
1946    highlighted_rows: Option<Range<u32>>,
1947    line_number_layouts: Vec<Option<text_layout::Line>>,
1948    hunk_layouts: Vec<DiffHunkLayout>,
1949    blocks: Vec<BlockLayout>,
1950    highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
1951    selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
1952    context_menu: Option<(DisplayPoint, ElementBox)>,
1953    code_actions_indicator: Option<(u32, ElementBox)>,
1954    hover_popovers: Option<(DisplayPoint, Vec<ElementBox>)>,
1955}
1956
1957pub struct PositionMap {
1958    size: Vector2F,
1959    line_height: f32,
1960    scroll_max: Vector2F,
1961    em_width: f32,
1962    em_advance: f32,
1963    line_layouts: Vec<text_layout::Line>,
1964    snapshot: EditorSnapshot,
1965}
1966
1967impl PositionMap {
1968    /// Returns two display points:
1969    /// 1. The nearest *valid* position in the editor
1970    /// 2. An unclipped, potentially *invalid* position that maps directly to
1971    ///    the given pixel position.
1972    fn point_for_position(
1973        &self,
1974        text_bounds: RectF,
1975        position: Vector2F,
1976    ) -> (DisplayPoint, DisplayPoint) {
1977        let scroll_position = self.snapshot.scroll_position();
1978        let position = position - text_bounds.origin();
1979        let y = position.y().max(0.0).min(self.size.y());
1980        let x = position.x() + (scroll_position.x() * self.em_width);
1981        let row = (y / self.line_height + scroll_position.y()) as u32;
1982        let (column, x_overshoot) = if let Some(line) = self
1983            .line_layouts
1984            .get(row as usize - scroll_position.y() as usize)
1985        {
1986            if let Some(ix) = line.index_for_x(x) {
1987                (ix as u32, 0.0)
1988            } else {
1989                (line.len() as u32, 0f32.max(x - line.width()))
1990            }
1991        } else {
1992            (0, x)
1993        };
1994
1995        let mut target_point = DisplayPoint::new(row, column);
1996        let point = self.snapshot.clip_point(target_point, Bias::Left);
1997        *target_point.column_mut() += (x_overshoot / self.em_advance) as u32;
1998
1999        (point, target_point)
2000    }
2001}
2002
2003struct BlockLayout {
2004    row: u32,
2005    element: ElementBox,
2006    style: BlockStyle,
2007}
2008
2009fn layout_line(
2010    row: u32,
2011    snapshot: &EditorSnapshot,
2012    style: &EditorStyle,
2013    layout_cache: &TextLayoutCache,
2014) -> text_layout::Line {
2015    let mut line = snapshot.line(row);
2016
2017    if line.len() > MAX_LINE_LEN {
2018        let mut len = MAX_LINE_LEN;
2019        while !line.is_char_boundary(len) {
2020            len -= 1;
2021        }
2022
2023        line.truncate(len);
2024    }
2025
2026    layout_cache.layout_str(
2027        &line,
2028        style.text.font_size,
2029        &[(
2030            snapshot.line_len(row) as usize,
2031            RunStyle {
2032                font_id: style.text.font_id,
2033                color: Color::black(),
2034                underline: Default::default(),
2035            },
2036        )],
2037    )
2038}
2039
2040#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2041pub enum CursorShape {
2042    Bar,
2043    Block,
2044    Underscore,
2045    Hollow,
2046}
2047
2048impl Default for CursorShape {
2049    fn default() -> Self {
2050        CursorShape::Bar
2051    }
2052}
2053
2054#[derive(Debug)]
2055pub struct Cursor {
2056    origin: Vector2F,
2057    block_width: f32,
2058    line_height: f32,
2059    color: Color,
2060    shape: CursorShape,
2061    block_text: Option<Line>,
2062}
2063
2064impl Cursor {
2065    pub fn new(
2066        origin: Vector2F,
2067        block_width: f32,
2068        line_height: f32,
2069        color: Color,
2070        shape: CursorShape,
2071        block_text: Option<Line>,
2072    ) -> Cursor {
2073        Cursor {
2074            origin,
2075            block_width,
2076            line_height,
2077            color,
2078            shape,
2079            block_text,
2080        }
2081    }
2082
2083    pub fn bounding_rect(&self, origin: Vector2F) -> RectF {
2084        RectF::new(
2085            self.origin + origin,
2086            vec2f(self.block_width, self.line_height),
2087        )
2088    }
2089
2090    pub fn paint(&self, origin: Vector2F, cx: &mut PaintContext) {
2091        let bounds = match self.shape {
2092            CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
2093            CursorShape::Block | CursorShape::Hollow => RectF::new(
2094                self.origin + origin,
2095                vec2f(self.block_width, self.line_height),
2096            ),
2097            CursorShape::Underscore => RectF::new(
2098                self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
2099                vec2f(self.block_width, 2.0),
2100            ),
2101        };
2102
2103        //Draw background or border quad
2104        if matches!(self.shape, CursorShape::Hollow) {
2105            cx.scene.push_quad(Quad {
2106                bounds,
2107                background: None,
2108                border: Border::all(1., self.color),
2109                corner_radius: 0.,
2110            });
2111        } else {
2112            cx.scene.push_quad(Quad {
2113                bounds,
2114                background: Some(self.color),
2115                border: Default::default(),
2116                corner_radius: 0.,
2117            });
2118        }
2119
2120        if let Some(block_text) = &self.block_text {
2121            block_text.paint(self.origin + origin, bounds, self.line_height, cx);
2122        }
2123    }
2124
2125    pub fn shape(&self) -> CursorShape {
2126        self.shape
2127    }
2128}
2129
2130#[derive(Debug)]
2131pub struct HighlightedRange {
2132    pub start_y: f32,
2133    pub line_height: f32,
2134    pub lines: Vec<HighlightedRangeLine>,
2135    pub color: Color,
2136    pub corner_radius: f32,
2137}
2138
2139#[derive(Debug)]
2140pub struct HighlightedRangeLine {
2141    pub start_x: f32,
2142    pub end_x: f32,
2143}
2144
2145impl HighlightedRange {
2146    pub fn paint(&self, bounds: RectF, scene: &mut Scene) {
2147        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
2148            self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
2149            self.paint_lines(
2150                self.start_y + self.line_height,
2151                &self.lines[1..],
2152                bounds,
2153                scene,
2154            );
2155        } else {
2156            self.paint_lines(self.start_y, &self.lines, bounds, scene);
2157        }
2158    }
2159
2160    fn paint_lines(
2161        &self,
2162        start_y: f32,
2163        lines: &[HighlightedRangeLine],
2164        bounds: RectF,
2165        scene: &mut Scene,
2166    ) {
2167        if lines.is_empty() {
2168            return;
2169        }
2170
2171        let mut path = PathBuilder::new();
2172        let first_line = lines.first().unwrap();
2173        let last_line = lines.last().unwrap();
2174
2175        let first_top_left = vec2f(first_line.start_x, start_y);
2176        let first_top_right = vec2f(first_line.end_x, start_y);
2177
2178        let curve_height = vec2f(0., self.corner_radius);
2179        let curve_width = |start_x: f32, end_x: f32| {
2180            let max = (end_x - start_x) / 2.;
2181            let width = if max < self.corner_radius {
2182                max
2183            } else {
2184                self.corner_radius
2185            };
2186
2187            vec2f(width, 0.)
2188        };
2189
2190        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
2191        path.reset(first_top_right - top_curve_width);
2192        path.curve_to(first_top_right + curve_height, first_top_right);
2193
2194        let mut iter = lines.iter().enumerate().peekable();
2195        while let Some((ix, line)) = iter.next() {
2196            let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
2197
2198            if let Some((_, next_line)) = iter.peek() {
2199                let next_top_right = vec2f(next_line.end_x, bottom_right.y());
2200
2201                match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
2202                    Ordering::Equal => {
2203                        path.line_to(bottom_right);
2204                    }
2205                    Ordering::Less => {
2206                        let curve_width = curve_width(next_top_right.x(), bottom_right.x());
2207                        path.line_to(bottom_right - curve_height);
2208                        if self.corner_radius > 0. {
2209                            path.curve_to(bottom_right - curve_width, bottom_right);
2210                        }
2211                        path.line_to(next_top_right + curve_width);
2212                        if self.corner_radius > 0. {
2213                            path.curve_to(next_top_right + curve_height, next_top_right);
2214                        }
2215                    }
2216                    Ordering::Greater => {
2217                        let curve_width = curve_width(bottom_right.x(), next_top_right.x());
2218                        path.line_to(bottom_right - curve_height);
2219                        if self.corner_radius > 0. {
2220                            path.curve_to(bottom_right + curve_width, bottom_right);
2221                        }
2222                        path.line_to(next_top_right - curve_width);
2223                        if self.corner_radius > 0. {
2224                            path.curve_to(next_top_right + curve_height, next_top_right);
2225                        }
2226                    }
2227                }
2228            } else {
2229                let curve_width = curve_width(line.start_x, line.end_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
2235                let bottom_left = vec2f(line.start_x, bottom_right.y());
2236                path.line_to(bottom_left + curve_width);
2237                if self.corner_radius > 0. {
2238                    path.curve_to(bottom_left - curve_height, bottom_left);
2239                }
2240            }
2241        }
2242
2243        if first_line.start_x > last_line.start_x {
2244            let curve_width = curve_width(last_line.start_x, first_line.start_x);
2245            let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
2246            path.line_to(second_top_left + curve_height);
2247            if self.corner_radius > 0. {
2248                path.curve_to(second_top_left + curve_width, second_top_left);
2249            }
2250            let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
2251            path.line_to(first_bottom_left - curve_width);
2252            if self.corner_radius > 0. {
2253                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
2254            }
2255        }
2256
2257        path.line_to(first_top_left + curve_height);
2258        if self.corner_radius > 0. {
2259            path.curve_to(first_top_left + top_curve_width, first_top_left);
2260        }
2261        path.line_to(first_top_right - top_curve_width);
2262
2263        scene.push_path(path.build(self.color, Some(bounds)));
2264    }
2265}
2266
2267pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
2268    delta.powf(1.5) / 100.0
2269}
2270
2271fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
2272    delta.powf(1.2) / 300.0
2273}
2274
2275#[cfg(test)]
2276mod tests {
2277    use std::sync::Arc;
2278
2279    use super::*;
2280    use crate::{
2281        display_map::{BlockDisposition, BlockProperties},
2282        Editor, MultiBuffer,
2283    };
2284    use settings::Settings;
2285    use util::test::sample_text;
2286
2287    #[gpui::test]
2288    fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
2289        cx.set_global(Settings::test(cx));
2290        let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
2291        let (window_id, editor) = cx.add_window(Default::default(), |cx| {
2292            Editor::new(EditorMode::Full, buffer, None, None, cx)
2293        });
2294        let element = EditorElement::new(
2295            editor.downgrade(),
2296            editor.read(cx).style(cx),
2297            CursorShape::Bar,
2298        );
2299
2300        let layouts = editor.update(cx, |editor, cx| {
2301            let snapshot = editor.snapshot(cx);
2302            let mut presenter = cx.build_presenter(window_id, 30., Default::default());
2303            let layout_cx = presenter.build_layout_context(Vector2F::zero(), false, cx);
2304            element.layout_line_numbers(0..6, &Default::default(), &snapshot, &layout_cx)
2305        });
2306        assert_eq!(layouts.len(), 6);
2307    }
2308
2309    #[gpui::test]
2310    fn test_layout_with_placeholder_text_and_blocks(cx: &mut gpui::MutableAppContext) {
2311        cx.set_global(Settings::test(cx));
2312        let buffer = MultiBuffer::build_simple("", cx);
2313        let (window_id, editor) = cx.add_window(Default::default(), |cx| {
2314            Editor::new(EditorMode::Full, buffer, None, None, cx)
2315        });
2316
2317        editor.update(cx, |editor, cx| {
2318            editor.set_placeholder_text("hello", cx);
2319            editor.insert_blocks(
2320                [BlockProperties {
2321                    style: BlockStyle::Fixed,
2322                    disposition: BlockDisposition::Above,
2323                    height: 3,
2324                    position: Anchor::min(),
2325                    render: Arc::new(|_| Empty::new().boxed()),
2326                }],
2327                cx,
2328            );
2329
2330            // Blur the editor so that it displays placeholder text.
2331            cx.blur();
2332        });
2333
2334        let mut element = EditorElement::new(
2335            editor.downgrade(),
2336            editor.read(cx).style(cx),
2337            CursorShape::Bar,
2338        );
2339
2340        let mut scene = Scene::new(1.0);
2341        let mut presenter = cx.build_presenter(window_id, 30., Default::default());
2342        let mut layout_cx = presenter.build_layout_context(Vector2F::zero(), false, cx);
2343        let (size, mut state) = element.layout(
2344            SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
2345            &mut layout_cx,
2346        );
2347
2348        assert_eq!(state.position_map.line_layouts.len(), 4);
2349        assert_eq!(
2350            state
2351                .line_number_layouts
2352                .iter()
2353                .map(Option::is_some)
2354                .collect::<Vec<_>>(),
2355            &[false, false, false, true]
2356        );
2357
2358        // Don't panic.
2359        let bounds = RectF::new(Default::default(), size);
2360        let mut paint_cx = presenter.build_paint_context(&mut scene, bounds.size(), cx);
2361        element.paint(bounds, bounds, &mut state, &mut paint_cx);
2362    }
2363}