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