element.rs

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