element.rs

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