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