element.rs

   1use super::{
   2    display_map::{BlockContext, ToDisplayPoint},
   3    Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, Input, Scroll, Select, SelectPhase,
   4    SoftWrap, ToPoint, MAX_LINE_LEN,
   5};
   6use crate::{display_map::TransformBlock, EditorStyle};
   7use clock::ReplicaId;
   8use collections::{BTreeMap, HashMap};
   9use gpui::{
  10    color::Color,
  11    elements::*,
  12    fonts::{HighlightStyle, Underline},
  13    geometry::{
  14        rect::RectF,
  15        vector::{vec2f, Vector2F},
  16        PathBuilder,
  17    },
  18    json::{self, ToJson},
  19    text_layout::{self, Line, RunStyle, TextLayoutCache},
  20    AppContext, Axis, Border, Element, ElementBox, Event, EventContext, LayoutContext,
  21    MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
  22};
  23use json::json;
  24use language::{Bias, DiagnosticSeverity};
  25use settings::Settings;
  26use smallvec::SmallVec;
  27use std::{
  28    cmp::{self, Ordering},
  29    fmt::Write,
  30    iter,
  31    ops::Range,
  32};
  33
  34pub struct EditorElement {
  35    view: WeakViewHandle<Editor>,
  36    style: EditorStyle,
  37    cursor_shape: CursorShape,
  38}
  39
  40impl EditorElement {
  41    pub fn new(
  42        view: WeakViewHandle<Editor>,
  43        style: EditorStyle,
  44        cursor_shape: CursorShape,
  45    ) -> Self {
  46        Self {
  47            view,
  48            style,
  49            cursor_shape,
  50        }
  51    }
  52
  53    fn view<'a>(&self, cx: &'a AppContext) -> &'a Editor {
  54        self.view.upgrade(cx).unwrap().read(cx)
  55    }
  56
  57    fn update_view<F, T>(&self, cx: &mut MutableAppContext, f: F) -> T
  58    where
  59        F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
  60    {
  61        self.view.upgrade(cx).unwrap().update(cx, f)
  62    }
  63
  64    fn snapshot(&self, cx: &mut MutableAppContext) -> EditorSnapshot {
  65        self.update_view(cx, |view, cx| view.snapshot(cx))
  66    }
  67
  68    fn mouse_down(
  69        &self,
  70        position: Vector2F,
  71        alt: bool,
  72        shift: bool,
  73        mut click_count: usize,
  74        layout: &mut LayoutState,
  75        paint: &mut PaintState,
  76        cx: &mut EventContext,
  77    ) -> bool {
  78        if paint.gutter_bounds.contains_point(position) {
  79            click_count = 3; // Simulate triple-click when clicking the gutter to select lines
  80        } else if !paint.text_bounds.contains_point(position) {
  81            return false;
  82        }
  83
  84        let snapshot = self.snapshot(cx.app);
  85        let (position, overshoot) = paint.point_for_position(&snapshot, layout, position);
  86
  87        if shift && alt {
  88            cx.dispatch_action(Select(SelectPhase::BeginColumnar {
  89                position,
  90                overshoot,
  91            }));
  92        } else if shift {
  93            cx.dispatch_action(Select(SelectPhase::Extend {
  94                position,
  95                click_count,
  96            }));
  97        } else {
  98            cx.dispatch_action(Select(SelectPhase::Begin {
  99                position,
 100                add: alt,
 101                click_count,
 102            }));
 103        }
 104
 105        true
 106    }
 107
 108    fn mouse_up(&self, _position: Vector2F, cx: &mut EventContext) -> bool {
 109        if self.view(cx.app.as_ref()).is_selecting() {
 110            cx.dispatch_action(Select(SelectPhase::End));
 111            true
 112        } else {
 113            false
 114        }
 115    }
 116
 117    fn mouse_dragged(
 118        &self,
 119        position: Vector2F,
 120        layout: &mut LayoutState,
 121        paint: &mut PaintState,
 122        cx: &mut EventContext,
 123    ) -> bool {
 124        let view = self.view(cx.app.as_ref());
 125
 126        if view.is_selecting() {
 127            let rect = paint.text_bounds;
 128            let mut scroll_delta = Vector2F::zero();
 129
 130            let vertical_margin = layout.line_height.min(rect.height() / 3.0);
 131            let top = rect.origin_y() + vertical_margin;
 132            let bottom = rect.lower_left().y() - vertical_margin;
 133            if position.y() < top {
 134                scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y()))
 135            }
 136            if position.y() > bottom {
 137                scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom))
 138            }
 139
 140            let horizontal_margin = layout.line_height.min(rect.width() / 3.0);
 141            let left = rect.origin_x() + horizontal_margin;
 142            let right = rect.upper_right().x() - horizontal_margin;
 143            if position.x() < left {
 144                scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta(
 145                    left - position.x(),
 146                ))
 147            }
 148            if position.x() > right {
 149                scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta(
 150                    position.x() - right,
 151                ))
 152            }
 153
 154            let snapshot = self.snapshot(cx.app);
 155            let (position, overshoot) = paint.point_for_position(&snapshot, layout, position);
 156
 157            cx.dispatch_action(Select(SelectPhase::Update {
 158                position,
 159                overshoot,
 160                scroll_position: (snapshot.scroll_position() + scroll_delta)
 161                    .clamp(Vector2F::zero(), layout.scroll_max),
 162            }));
 163            true
 164        } else {
 165            false
 166        }
 167    }
 168
 169    fn key_down(&self, input: Option<&str>, cx: &mut EventContext) -> bool {
 170        let view = self.view.upgrade(cx.app).unwrap();
 171
 172        if view.is_focused(cx.app) {
 173            if let Some(input) = input {
 174                cx.dispatch_action(Input(input.to_string()));
 175                true
 176            } else {
 177                false
 178            }
 179        } else {
 180            false
 181        }
 182    }
 183
 184    fn scroll(
 185        &self,
 186        position: Vector2F,
 187        mut delta: Vector2F,
 188        precise: bool,
 189        layout: &mut LayoutState,
 190        paint: &mut PaintState,
 191        cx: &mut EventContext,
 192    ) -> bool {
 193        if !paint.bounds.contains_point(position) {
 194            return false;
 195        }
 196
 197        let snapshot = self.snapshot(cx.app);
 198        let max_glyph_width = layout.em_width;
 199        if !precise {
 200            delta *= vec2f(max_glyph_width, layout.line_height);
 201        }
 202
 203        let scroll_position = snapshot.scroll_position();
 204        let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width;
 205        let y = (scroll_position.y() * layout.line_height - delta.y()) / layout.line_height;
 206        let scroll_position = vec2f(x, y).clamp(Vector2F::zero(), layout.scroll_max);
 207
 208        cx.dispatch_action(Scroll(scroll_position));
 209
 210        true
 211    }
 212
 213    fn paint_background(
 214        &self,
 215        gutter_bounds: RectF,
 216        text_bounds: RectF,
 217        layout: &LayoutState,
 218        cx: &mut PaintContext,
 219    ) {
 220        let bounds = gutter_bounds.union_rect(text_bounds);
 221        let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
 222        let editor = self.view(cx.app);
 223        cx.scene.push_quad(Quad {
 224            bounds: gutter_bounds,
 225            background: Some(self.style.gutter_background),
 226            border: Border::new(0., Color::transparent_black()),
 227            corner_radius: 0.,
 228        });
 229        cx.scene.push_quad(Quad {
 230            bounds: text_bounds,
 231            background: Some(self.style.background),
 232            border: Border::new(0., Color::transparent_black()),
 233            corner_radius: 0.,
 234        });
 235
 236        if let EditorMode::Full = editor.mode {
 237            let mut active_rows = layout.active_rows.iter().peekable();
 238            while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
 239                let mut end_row = *start_row;
 240                while active_rows.peek().map_or(false, |r| {
 241                    *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
 242                }) {
 243                    active_rows.next().unwrap();
 244                    end_row += 1;
 245                }
 246
 247                if !contains_non_empty_selection {
 248                    let origin = vec2f(
 249                        bounds.origin_x(),
 250                        bounds.origin_y() + (layout.line_height * *start_row as f32) - scroll_top,
 251                    );
 252                    let size = vec2f(
 253                        bounds.width(),
 254                        layout.line_height * (end_row - start_row + 1) as f32,
 255                    );
 256                    cx.scene.push_quad(Quad {
 257                        bounds: RectF::new(origin, size),
 258                        background: Some(self.style.active_line_background),
 259                        border: Border::default(),
 260                        corner_radius: 0.,
 261                    });
 262                }
 263            }
 264
 265            if let Some(highlighted_rows) = &layout.highlighted_rows {
 266                let origin = vec2f(
 267                    bounds.origin_x(),
 268                    bounds.origin_y() + (layout.line_height * highlighted_rows.start as f32)
 269                        - scroll_top,
 270                );
 271                let size = vec2f(
 272                    bounds.width(),
 273                    layout.line_height * highlighted_rows.len() as f32,
 274                );
 275                cx.scene.push_quad(Quad {
 276                    bounds: RectF::new(origin, size),
 277                    background: Some(self.style.highlighted_line_background),
 278                    border: Border::default(),
 279                    corner_radius: 0.,
 280                });
 281            }
 282        }
 283    }
 284
 285    fn paint_gutter(
 286        &mut self,
 287        bounds: RectF,
 288        visible_bounds: RectF,
 289        layout: &mut LayoutState,
 290        cx: &mut PaintContext,
 291    ) {
 292        let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
 293        for (ix, line) in layout.line_number_layouts.iter().enumerate() {
 294            if let Some(line) = line {
 295                let line_origin = bounds.origin()
 296                    + vec2f(
 297                        bounds.width() - line.width() - layout.gutter_padding,
 298                        ix as f32 * layout.line_height - (scroll_top % layout.line_height),
 299                    );
 300                line.paint(line_origin, visible_bounds, layout.line_height, cx);
 301            }
 302        }
 303
 304        if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() {
 305            let mut x = bounds.width() - layout.gutter_padding;
 306            let mut y = *row as f32 * layout.line_height - scroll_top;
 307            x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.;
 308            y += (layout.line_height - indicator.size().y()) / 2.;
 309            indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx);
 310        }
 311    }
 312
 313    fn paint_text(
 314        &mut self,
 315        bounds: RectF,
 316        visible_bounds: RectF,
 317        layout: &mut LayoutState,
 318        cx: &mut PaintContext,
 319    ) {
 320        let view = self.view(cx.app);
 321        let style = &self.style;
 322        let local_replica_id = view.replica_id(cx);
 323        let scroll_position = layout.snapshot.scroll_position();
 324        let start_row = scroll_position.y() as u32;
 325        let scroll_top = scroll_position.y() * layout.line_height;
 326        let end_row = ((scroll_top + bounds.height()) / layout.line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen
 327        let max_glyph_width = layout.em_width;
 328        let scroll_left = scroll_position.x() * max_glyph_width;
 329        let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.);
 330
 331        cx.scene.push_layer(Some(bounds));
 332
 333        for (range, color) in &layout.highlighted_ranges {
 334            self.paint_highlighted_range(
 335                range.clone(),
 336                start_row,
 337                end_row,
 338                *color,
 339                0.,
 340                0.15 * layout.line_height,
 341                layout,
 342                content_origin,
 343                scroll_top,
 344                scroll_left,
 345                bounds,
 346                cx,
 347            );
 348        }
 349
 350        let mut cursors = SmallVec::<[Cursor; 32]>::new();
 351        for (replica_id, selections) in &layout.selections {
 352            let selection_style = style.replica_selection_style(*replica_id);
 353            let corner_radius = 0.15 * layout.line_height;
 354
 355            for selection in selections {
 356                self.paint_highlighted_range(
 357                    selection.start..selection.end,
 358                    start_row,
 359                    end_row,
 360                    selection_style.selection,
 361                    corner_radius,
 362                    corner_radius * 2.,
 363                    layout,
 364                    content_origin,
 365                    scroll_top,
 366                    scroll_left,
 367                    bounds,
 368                    cx,
 369                );
 370
 371                if view.show_local_cursors() || *replica_id != local_replica_id {
 372                    let cursor_position = selection.head();
 373                    if (start_row..end_row).contains(&cursor_position.row()) {
 374                        let cursor_row_layout =
 375                            &layout.line_layouts[(cursor_position.row() - start_row) as usize];
 376                        let cursor_column = cursor_position.column() as usize;
 377
 378                        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 379                        let mut block_width =
 380                            cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x;
 381                        if block_width == 0.0 {
 382                            block_width = layout.em_width;
 383                        }
 384
 385                        let block_text =
 386                            if matches!(self.cursor_shape, CursorShape::Block) {
 387                                layout.snapshot.chars_at(cursor_position).next().and_then(
 388                                    |character| {
 389                                        let font_id =
 390                                            cursor_row_layout.font_for_index(cursor_column)?;
 391                                        let text = character.to_string();
 392
 393                                        Some(cx.text_layout_cache.layout_str(
 394                                            &text,
 395                                            cursor_row_layout.font_size(),
 396                                            &[(
 397                                                text.len(),
 398                                                RunStyle {
 399                                                    font_id,
 400                                                    color: style.background,
 401                                                    underline: Default::default(),
 402                                                },
 403                                            )],
 404                                        ))
 405                                    },
 406                                )
 407                            } else {
 408                                None
 409                            };
 410
 411                        let x = cursor_character_x - scroll_left;
 412                        let y = cursor_position.row() as f32 * layout.line_height - scroll_top;
 413                        cursors.push(Cursor {
 414                            color: selection_style.cursor,
 415                            block_width,
 416                            origin: content_origin + vec2f(x, y),
 417                            line_height: layout.line_height,
 418                            shape: self.cursor_shape,
 419                            block_text,
 420                        });
 421                    }
 422                }
 423            }
 424        }
 425
 426        if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) {
 427            // Draw glyphs
 428            for (ix, line) in layout.line_layouts.iter().enumerate() {
 429                let row = start_row + ix as u32;
 430                line.paint(
 431                    content_origin
 432                        + vec2f(-scroll_left, row as f32 * layout.line_height - scroll_top),
 433                    visible_text_bounds,
 434                    layout.line_height,
 435                    cx,
 436                );
 437            }
 438        }
 439
 440        cx.scene.push_layer(Some(bounds));
 441        for cursor in cursors {
 442            cursor.paint(cx);
 443        }
 444        cx.scene.pop_layer();
 445
 446        if let Some((position, context_menu)) = layout.context_menu.as_mut() {
 447            cx.scene.push_stacking_context(None);
 448
 449            let cursor_row_layout = &layout.line_layouts[(position.row() - start_row) as usize];
 450            let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
 451            let y = (position.row() + 1) as f32 * layout.line_height - scroll_top;
 452            let mut list_origin = content_origin + vec2f(x, y);
 453            let list_height = context_menu.size().y();
 454
 455            if list_origin.y() + list_height > bounds.lower_left().y() {
 456                list_origin.set_y(list_origin.y() - layout.line_height - list_height);
 457            }
 458
 459            context_menu.paint(
 460                list_origin,
 461                RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
 462                cx,
 463            );
 464
 465            cx.scene.pop_stacking_context();
 466        }
 467
 468        cx.scene.pop_layer();
 469    }
 470
 471    fn paint_highlighted_range(
 472        &self,
 473        range: Range<DisplayPoint>,
 474        start_row: u32,
 475        end_row: u32,
 476        color: Color,
 477        corner_radius: f32,
 478        line_end_overshoot: f32,
 479        layout: &LayoutState,
 480        content_origin: Vector2F,
 481        scroll_top: f32,
 482        scroll_left: f32,
 483        bounds: RectF,
 484        cx: &mut PaintContext,
 485    ) {
 486        if range.start != range.end {
 487            let row_range = if range.end.column() == 0 {
 488                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
 489            } else {
 490                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
 491            };
 492
 493            let highlighted_range = HighlightedRange {
 494                color,
 495                line_height: layout.line_height,
 496                corner_radius,
 497                start_y: content_origin.y() + row_range.start as f32 * layout.line_height
 498                    - scroll_top,
 499                lines: row_range
 500                    .into_iter()
 501                    .map(|row| {
 502                        let line_layout = &layout.line_layouts[(row - start_row) as usize];
 503                        HighlightedRangeLine {
 504                            start_x: if row == range.start.row() {
 505                                content_origin.x()
 506                                    + line_layout.x_for_index(range.start.column() as usize)
 507                                    - scroll_left
 508                            } else {
 509                                content_origin.x() - scroll_left
 510                            },
 511                            end_x: if row == range.end.row() {
 512                                content_origin.x()
 513                                    + line_layout.x_for_index(range.end.column() as usize)
 514                                    - scroll_left
 515                            } else {
 516                                content_origin.x() + line_layout.width() + line_end_overshoot
 517                                    - scroll_left
 518                            },
 519                        }
 520                    })
 521                    .collect(),
 522            };
 523
 524            highlighted_range.paint(bounds, cx.scene);
 525        }
 526    }
 527
 528    fn paint_blocks(
 529        &mut self,
 530        bounds: RectF,
 531        visible_bounds: RectF,
 532        layout: &mut LayoutState,
 533        cx: &mut PaintContext,
 534    ) {
 535        let scroll_position = layout.snapshot.scroll_position();
 536        let scroll_left = scroll_position.x() * layout.em_width;
 537        let scroll_top = scroll_position.y() * layout.line_height;
 538
 539        for (row, element) in &mut layout.blocks {
 540            let origin = bounds.origin()
 541                + vec2f(-scroll_left, *row as f32 * layout.line_height - scroll_top);
 542            element.paint(origin, visible_bounds, cx);
 543        }
 544    }
 545
 546    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &LayoutContext) -> f32 {
 547        let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1;
 548        let style = &self.style;
 549
 550        cx.text_layout_cache
 551            .layout_str(
 552                "1".repeat(digit_count).as_str(),
 553                style.text.font_size,
 554                &[(
 555                    digit_count,
 556                    RunStyle {
 557                        font_id: style.text.font_id,
 558                        color: Color::black(),
 559                        underline: Default::default(),
 560                    },
 561                )],
 562            )
 563            .width()
 564    }
 565
 566    fn layout_line_numbers(
 567        &self,
 568        rows: Range<u32>,
 569        active_rows: &BTreeMap<u32, bool>,
 570        snapshot: &EditorSnapshot,
 571        cx: &LayoutContext,
 572    ) -> Vec<Option<text_layout::Line>> {
 573        let style = &self.style;
 574        let include_line_numbers = snapshot.mode == EditorMode::Full;
 575        let mut line_number_layouts = Vec::with_capacity(rows.len());
 576        let mut line_number = String::new();
 577        for (ix, row) in snapshot
 578            .buffer_rows(rows.start)
 579            .take((rows.end - rows.start) as usize)
 580            .enumerate()
 581        {
 582            let display_row = rows.start + ix as u32;
 583            let color = if active_rows.contains_key(&display_row) {
 584                style.line_number_active
 585            } else {
 586                style.line_number
 587            };
 588            if let Some(buffer_row) = row {
 589                if include_line_numbers {
 590                    line_number.clear();
 591                    write!(&mut line_number, "{}", buffer_row + 1).unwrap();
 592                    line_number_layouts.push(Some(cx.text_layout_cache.layout_str(
 593                        &line_number,
 594                        style.text.font_size,
 595                        &[(
 596                            line_number.len(),
 597                            RunStyle {
 598                                font_id: style.text.font_id,
 599                                color,
 600                                underline: Default::default(),
 601                            },
 602                        )],
 603                    )));
 604                }
 605            } else {
 606                line_number_layouts.push(None);
 607            }
 608        }
 609
 610        line_number_layouts
 611    }
 612
 613    fn layout_lines(
 614        &mut self,
 615        rows: Range<u32>,
 616        snapshot: &EditorSnapshot,
 617        cx: &LayoutContext,
 618    ) -> Vec<text_layout::Line> {
 619        if rows.start >= rows.end {
 620            return Vec::new();
 621        }
 622
 623        // When the editor is empty and unfocused, then show the placeholder.
 624        if snapshot.is_empty() && !snapshot.is_focused() {
 625            let placeholder_style = self
 626                .style
 627                .placeholder_text
 628                .as_ref()
 629                .unwrap_or_else(|| &self.style.text);
 630            let placeholder_text = snapshot.placeholder_text();
 631            let placeholder_lines = placeholder_text
 632                .as_ref()
 633                .map_or("", AsRef::as_ref)
 634                .split('\n')
 635                .skip(rows.start as usize)
 636                .chain(iter::repeat(""))
 637                .take(rows.len());
 638            return placeholder_lines
 639                .map(|line| {
 640                    cx.text_layout_cache.layout_str(
 641                        line,
 642                        placeholder_style.font_size,
 643                        &[(
 644                            line.len(),
 645                            RunStyle {
 646                                font_id: placeholder_style.font_id,
 647                                color: placeholder_style.color,
 648                                underline: Default::default(),
 649                            },
 650                        )],
 651                    )
 652                })
 653                .collect();
 654        } else {
 655            let style = &self.style;
 656            let chunks = snapshot.chunks(rows.clone(), true).map(|chunk| {
 657                let mut highlight_style = chunk
 658                    .syntax_highlight_id
 659                    .and_then(|id| id.style(&style.syntax));
 660
 661                if let Some(chunk_highlight) = chunk.highlight_style {
 662                    if let Some(highlight_style) = highlight_style.as_mut() {
 663                        highlight_style.highlight(chunk_highlight);
 664                    } else {
 665                        highlight_style = Some(chunk_highlight);
 666                    }
 667                }
 668
 669                let mut diagnostic_highlight = HighlightStyle::default();
 670
 671                if chunk.is_unnecessary {
 672                    diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
 673                }
 674
 675                if let Some(severity) = chunk.diagnostic_severity {
 676                    // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
 677                    if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
 678                        let diagnostic_style = super::diagnostic_style(severity, true, style);
 679                        diagnostic_highlight.underline = Some(Underline {
 680                            color: Some(diagnostic_style.message.text.color),
 681                            thickness: 1.0.into(),
 682                            squiggly: true,
 683                        });
 684                    }
 685                }
 686
 687                if let Some(highlight_style) = highlight_style.as_mut() {
 688                    highlight_style.highlight(diagnostic_highlight);
 689                } else {
 690                    highlight_style = Some(diagnostic_highlight);
 691                }
 692
 693                (chunk.text, highlight_style)
 694            });
 695            layout_highlighted_chunks(
 696                chunks,
 697                &style.text,
 698                &cx.text_layout_cache,
 699                &cx.font_cache,
 700                MAX_LINE_LEN,
 701                rows.len() as usize,
 702            )
 703        }
 704    }
 705
 706    fn layout_blocks(
 707        &mut self,
 708        rows: Range<u32>,
 709        snapshot: &EditorSnapshot,
 710        width: f32,
 711        gutter_padding: f32,
 712        gutter_width: f32,
 713        em_width: f32,
 714        text_x: f32,
 715        line_height: f32,
 716        style: &EditorStyle,
 717        line_layouts: &[text_layout::Line],
 718        cx: &mut LayoutContext,
 719    ) -> Vec<(u32, ElementBox)> {
 720        let scroll_x = snapshot.scroll_position.x();
 721        snapshot
 722            .blocks_in_range(rows.clone())
 723            .map(|(block_row, block)| {
 724                let mut element = match block {
 725                    TransformBlock::Custom(block) => {
 726                        let align_to = block
 727                            .position()
 728                            .to_point(&snapshot.buffer_snapshot)
 729                            .to_display_point(snapshot);
 730                        let anchor_x = text_x
 731                            + if rows.contains(&align_to.row()) {
 732                                line_layouts[(align_to.row() - rows.start) as usize]
 733                                    .x_for_index(align_to.column() as usize)
 734                            } else {
 735                                layout_line(align_to.row(), snapshot, style, cx.text_layout_cache)
 736                                    .x_for_index(align_to.column() as usize)
 737                            };
 738
 739                        block.render(&BlockContext {
 740                            cx,
 741                            anchor_x,
 742                            gutter_padding,
 743                            line_height,
 744                            scroll_x,
 745                            gutter_width,
 746                            em_width,
 747                        })
 748                    }
 749                    TransformBlock::ExcerptHeader {
 750                        buffer,
 751                        starts_new_buffer,
 752                        ..
 753                    } => {
 754                        if *starts_new_buffer {
 755                            let style = &self.style.diagnostic_path_header;
 756                            let font_size =
 757                                (style.text_scale_factor * self.style.text.font_size).round();
 758
 759                            let mut filename = None;
 760                            let mut parent_path = None;
 761                            if let Some(path) = buffer.path() {
 762                                filename =
 763                                    path.file_name().map(|f| f.to_string_lossy().to_string());
 764                                parent_path =
 765                                    path.parent().map(|p| p.to_string_lossy().to_string() + "/");
 766                            }
 767
 768                            Flex::row()
 769                                .with_child(
 770                                    Label::new(
 771                                        filename.unwrap_or_else(|| "untitled".to_string()),
 772                                        style.filename.text.clone().with_font_size(font_size),
 773                                    )
 774                                    .contained()
 775                                    .with_style(style.filename.container)
 776                                    .boxed(),
 777                                )
 778                                .with_children(parent_path.map(|path| {
 779                                    Label::new(
 780                                        path,
 781                                        style.path.text.clone().with_font_size(font_size),
 782                                    )
 783                                    .contained()
 784                                    .with_style(style.path.container)
 785                                    .boxed()
 786                                }))
 787                                .aligned()
 788                                .left()
 789                                .contained()
 790                                .with_style(style.container)
 791                                .with_padding_left(gutter_padding + scroll_x * em_width)
 792                                .expanded()
 793                                .named("path header block")
 794                        } else {
 795                            let text_style = self.style.text.clone();
 796                            Label::new("".to_string(), text_style)
 797                                .contained()
 798                                .with_padding_left(gutter_padding + scroll_x * em_width)
 799                                .named("collapsed context")
 800                        }
 801                    }
 802                };
 803
 804                element.layout(
 805                    SizeConstraint {
 806                        min: Vector2F::zero(),
 807                        max: vec2f(width, block.height() as f32 * line_height),
 808                    },
 809                    cx,
 810                );
 811                (block_row, element)
 812            })
 813            .collect()
 814    }
 815}
 816
 817impl Element for EditorElement {
 818    type LayoutState = LayoutState;
 819    type PaintState = PaintState;
 820
 821    fn layout(
 822        &mut self,
 823        constraint: SizeConstraint,
 824        cx: &mut LayoutContext,
 825    ) -> (Vector2F, Self::LayoutState) {
 826        let mut size = constraint.max;
 827        if size.x().is_infinite() {
 828            unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
 829        }
 830
 831        let snapshot = self.snapshot(cx.app);
 832        let style = self.style.clone();
 833        let line_height = style.text.line_height(cx.font_cache);
 834
 835        let gutter_padding;
 836        let gutter_width;
 837        let gutter_margin;
 838        if snapshot.mode == EditorMode::Full {
 839            gutter_padding = style.text.em_width(cx.font_cache) * style.gutter_padding_factor;
 840            gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
 841            gutter_margin = -style.text.descent(cx.font_cache);
 842        } else {
 843            gutter_padding = 0.0;
 844            gutter_width = 0.0;
 845            gutter_margin = 0.0;
 846        };
 847
 848        let text_width = size.x() - gutter_width;
 849        let em_width = style.text.em_width(cx.font_cache);
 850        let em_advance = style.text.em_advance(cx.font_cache);
 851        let overscroll = vec2f(em_width, 0.);
 852        let snapshot = self.update_view(cx.app, |view, cx| {
 853            let wrap_width = match view.soft_wrap_mode(cx) {
 854                SoftWrap::None => None,
 855                SoftWrap::EditorWidth => {
 856                    Some(text_width - gutter_margin - overscroll.x() - em_width)
 857                }
 858                SoftWrap::Column(column) => Some(column as f32 * em_advance),
 859            };
 860
 861            if view.set_wrap_width(wrap_width, cx) {
 862                view.snapshot(cx)
 863            } else {
 864                snapshot
 865            }
 866        });
 867
 868        let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
 869        if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
 870            size.set_y(
 871                scroll_height
 872                    .min(constraint.max_along(Axis::Vertical))
 873                    .max(constraint.min_along(Axis::Vertical))
 874                    .min(line_height * max_lines as f32),
 875            )
 876        } else if size.y().is_infinite() {
 877            size.set_y(scroll_height);
 878        }
 879        let gutter_size = vec2f(gutter_width, size.y());
 880        let text_size = vec2f(text_width, size.y());
 881
 882        let (autoscroll_horizontally, mut snapshot) = self.update_view(cx.app, |view, cx| {
 883            let autoscroll_horizontally = view.autoscroll_vertically(size.y(), line_height, cx);
 884            let snapshot = view.snapshot(cx);
 885            (autoscroll_horizontally, snapshot)
 886        });
 887
 888        let scroll_position = snapshot.scroll_position();
 889        let start_row = scroll_position.y() as u32;
 890        let scroll_top = scroll_position.y() * line_height;
 891
 892        // Add 1 to ensure selections bleed off screen
 893        let end_row = 1 + cmp::min(
 894            ((scroll_top + size.y()) / line_height).ceil() as u32,
 895            snapshot.max_point().row(),
 896        );
 897
 898        let start_anchor = if start_row == 0 {
 899            Anchor::min()
 900        } else {
 901            snapshot
 902                .buffer_snapshot
 903                .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
 904        };
 905        let end_anchor = if end_row > snapshot.max_point().row() {
 906            Anchor::max()
 907        } else {
 908            snapshot
 909                .buffer_snapshot
 910                .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
 911        };
 912
 913        let mut selections = Vec::new();
 914        let mut active_rows = BTreeMap::new();
 915        let mut highlighted_rows = None;
 916        let mut highlighted_ranges = Vec::new();
 917        self.update_view(cx.app, |view, cx| {
 918            let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx));
 919
 920            highlighted_rows = view.highlighted_rows();
 921            let theme = cx.global::<Settings>().theme.as_ref();
 922            highlighted_ranges = view.background_highlights_in_range(
 923                start_anchor.clone()..end_anchor.clone(),
 924                &display_map,
 925                theme,
 926            );
 927
 928            let mut remote_selections = HashMap::default();
 929            for (replica_id, selection) in display_map
 930                .buffer_snapshot
 931                .remote_selections_in_range(&(start_anchor.clone()..end_anchor.clone()))
 932            {
 933                // The local selections match the leader's selections.
 934                if Some(replica_id) == view.leader_replica_id {
 935                    continue;
 936                }
 937
 938                remote_selections
 939                    .entry(replica_id)
 940                    .or_insert(Vec::new())
 941                    .push(crate::Selection {
 942                        id: selection.id,
 943                        goal: selection.goal,
 944                        reversed: selection.reversed,
 945                        start: selection.start.to_display_point(&display_map),
 946                        end: selection.end.to_display_point(&display_map),
 947                    });
 948            }
 949            selections.extend(remote_selections);
 950
 951            if view.show_local_selections {
 952                let local_selections =
 953                    view.local_selections_in_range(start_anchor..end_anchor, &display_map);
 954                for selection in &local_selections {
 955                    let is_empty = selection.start == selection.end;
 956                    let selection_start = snapshot.prev_line_boundary(selection.start).1;
 957                    let selection_end = snapshot.next_line_boundary(selection.end).1;
 958                    for row in cmp::max(selection_start.row(), start_row)
 959                        ..=cmp::min(selection_end.row(), end_row)
 960                    {
 961                        let contains_non_empty_selection =
 962                            active_rows.entry(row).or_insert(!is_empty);
 963                        *contains_non_empty_selection |= !is_empty;
 964                    }
 965                }
 966
 967                // Render the local selections in the leader's color when following.
 968                let local_replica_id = view.leader_replica_id.unwrap_or(view.replica_id(cx));
 969
 970                selections.push((
 971                    local_replica_id,
 972                    local_selections
 973                        .into_iter()
 974                        .map(|selection| crate::Selection {
 975                            id: selection.id,
 976                            goal: selection.goal,
 977                            reversed: selection.reversed,
 978                            start: selection.start.to_display_point(&display_map),
 979                            end: selection.end.to_display_point(&display_map),
 980                        })
 981                        .collect(),
 982                ));
 983            }
 984        });
 985
 986        let line_number_layouts =
 987            self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx);
 988
 989        let mut max_visible_line_width = 0.0;
 990        let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx);
 991        for line in &line_layouts {
 992            if line.width() > max_visible_line_width {
 993                max_visible_line_width = line.width();
 994            }
 995        }
 996
 997        let style = self.style.clone();
 998        let longest_line_width = layout_line(
 999            snapshot.longest_row(),
1000            &snapshot,
1001            &style,
1002            cx.text_layout_cache,
1003        )
1004        .width();
1005        let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x();
1006        let em_width = style.text.em_width(cx.font_cache);
1007        let max_row = snapshot.max_point().row();
1008        let scroll_max = vec2f(
1009            ((scroll_width - text_size.x()) / em_width).max(0.0),
1010            max_row.saturating_sub(1) as f32,
1011        );
1012
1013        let mut context_menu = None;
1014        let mut code_actions_indicator = None;
1015        self.update_view(cx.app, |view, cx| {
1016            let clamped = view.clamp_scroll_left(scroll_max.x());
1017            let autoscrolled;
1018            if autoscroll_horizontally {
1019                autoscrolled = view.autoscroll_horizontally(
1020                    start_row,
1021                    text_size.x(),
1022                    scroll_width,
1023                    em_width,
1024                    &line_layouts,
1025                    cx,
1026                );
1027            } else {
1028                autoscrolled = false;
1029            }
1030
1031            if clamped || autoscrolled {
1032                snapshot = view.snapshot(cx);
1033            }
1034
1035            let newest_selection_head = view
1036                .newest_selection_with_snapshot::<usize>(&snapshot.buffer_snapshot)
1037                .head()
1038                .to_display_point(&snapshot);
1039
1040            if (start_row..end_row).contains(&newest_selection_head.row()) {
1041                let style = view.style(cx);
1042                if view.context_menu_visible() {
1043                    context_menu =
1044                        view.render_context_menu(newest_selection_head, style.clone(), cx);
1045                }
1046
1047                code_actions_indicator = view
1048                    .render_code_actions_indicator(&style, cx)
1049                    .map(|indicator| (newest_selection_head.row(), indicator));
1050            }
1051        });
1052
1053        if let Some((_, context_menu)) = context_menu.as_mut() {
1054            context_menu.layout(
1055                SizeConstraint {
1056                    min: Vector2F::zero(),
1057                    max: vec2f(
1058                        f32::INFINITY,
1059                        (12. * line_height).min((size.y() - line_height) / 2.),
1060                    ),
1061                },
1062                cx,
1063            );
1064        }
1065
1066        if let Some((_, indicator)) = code_actions_indicator.as_mut() {
1067            indicator.layout(
1068                SizeConstraint::strict_along(Axis::Vertical, line_height * 0.618),
1069                cx,
1070            );
1071        }
1072
1073        let blocks = self.layout_blocks(
1074            start_row..end_row,
1075            &snapshot,
1076            size.x().max(scroll_width + gutter_width),
1077            gutter_padding,
1078            gutter_width,
1079            em_width,
1080            gutter_width + gutter_margin,
1081            line_height,
1082            &style,
1083            &line_layouts,
1084            cx,
1085        );
1086
1087        (
1088            size,
1089            LayoutState {
1090                size,
1091                scroll_max,
1092                gutter_size,
1093                gutter_padding,
1094                text_size,
1095                gutter_margin,
1096                snapshot,
1097                active_rows,
1098                highlighted_rows,
1099                highlighted_ranges,
1100                line_layouts,
1101                line_number_layouts,
1102                blocks,
1103                line_height,
1104                em_width,
1105                em_advance,
1106                selections,
1107                context_menu,
1108                code_actions_indicator,
1109            },
1110        )
1111    }
1112
1113    fn paint(
1114        &mut self,
1115        bounds: RectF,
1116        visible_bounds: RectF,
1117        layout: &mut Self::LayoutState,
1118        cx: &mut PaintContext,
1119    ) -> Self::PaintState {
1120        cx.scene.push_layer(Some(bounds));
1121
1122        let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
1123        let text_bounds = RectF::new(
1124            bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
1125            layout.text_size,
1126        );
1127
1128        self.paint_background(gutter_bounds, text_bounds, layout, cx);
1129        if layout.gutter_size.x() > 0. {
1130            self.paint_gutter(gutter_bounds, visible_bounds, layout, cx);
1131        }
1132        self.paint_text(text_bounds, visible_bounds, layout, cx);
1133
1134        if !layout.blocks.is_empty() {
1135            cx.scene.push_layer(Some(bounds));
1136            self.paint_blocks(bounds, visible_bounds, layout, cx);
1137            cx.scene.pop_layer();
1138        }
1139
1140        cx.scene.pop_layer();
1141
1142        PaintState {
1143            bounds,
1144            gutter_bounds,
1145            text_bounds,
1146        }
1147    }
1148
1149    fn dispatch_event(
1150        &mut self,
1151        event: &Event,
1152        _: RectF,
1153        _: RectF,
1154        layout: &mut LayoutState,
1155        paint: &mut PaintState,
1156        cx: &mut EventContext,
1157    ) -> bool {
1158        if let Some((_, context_menu)) = &mut layout.context_menu {
1159            if context_menu.dispatch_event(event, cx) {
1160                return true;
1161            }
1162        }
1163
1164        if let Some((_, indicator)) = &mut layout.code_actions_indicator {
1165            if indicator.dispatch_event(event, cx) {
1166                return true;
1167            }
1168        }
1169
1170        for (_, block) in &mut layout.blocks {
1171            if block.dispatch_event(event, cx) {
1172                return true;
1173            }
1174        }
1175
1176        match event {
1177            Event::LeftMouseDown {
1178                position,
1179                alt,
1180                shift,
1181                click_count,
1182                ..
1183            } => self.mouse_down(*position, *alt, *shift, *click_count, layout, paint, cx),
1184            Event::LeftMouseUp { position } => self.mouse_up(*position, cx),
1185            Event::LeftMouseDragged { position } => {
1186                self.mouse_dragged(*position, layout, paint, cx)
1187            }
1188            Event::ScrollWheel {
1189                position,
1190                delta,
1191                precise,
1192            } => self.scroll(*position, *delta, *precise, layout, paint, cx),
1193            Event::KeyDown { input, .. } => self.key_down(input.as_deref(), cx),
1194            _ => false,
1195        }
1196    }
1197
1198    fn debug(
1199        &self,
1200        bounds: RectF,
1201        _: &Self::LayoutState,
1202        _: &Self::PaintState,
1203        _: &gpui::DebugContext,
1204    ) -> json::Value {
1205        json!({
1206            "type": "BufferElement",
1207            "bounds": bounds.to_json()
1208        })
1209    }
1210}
1211
1212pub struct LayoutState {
1213    size: Vector2F,
1214    scroll_max: Vector2F,
1215    gutter_size: Vector2F,
1216    gutter_padding: f32,
1217    gutter_margin: f32,
1218    text_size: Vector2F,
1219    snapshot: EditorSnapshot,
1220    active_rows: BTreeMap<u32, bool>,
1221    highlighted_rows: Option<Range<u32>>,
1222    line_layouts: Vec<text_layout::Line>,
1223    line_number_layouts: Vec<Option<text_layout::Line>>,
1224    blocks: Vec<(u32, ElementBox)>,
1225    line_height: f32,
1226    em_width: f32,
1227    em_advance: f32,
1228    highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
1229    selections: Vec<(ReplicaId, Vec<text::Selection<DisplayPoint>>)>,
1230    context_menu: Option<(DisplayPoint, ElementBox)>,
1231    code_actions_indicator: Option<(u32, ElementBox)>,
1232}
1233
1234fn layout_line(
1235    row: u32,
1236    snapshot: &EditorSnapshot,
1237    style: &EditorStyle,
1238    layout_cache: &TextLayoutCache,
1239) -> text_layout::Line {
1240    let mut line = snapshot.line(row);
1241
1242    if line.len() > MAX_LINE_LEN {
1243        let mut len = MAX_LINE_LEN;
1244        while !line.is_char_boundary(len) {
1245            len -= 1;
1246        }
1247
1248        line.truncate(len);
1249    }
1250
1251    layout_cache.layout_str(
1252        &line,
1253        style.text.font_size,
1254        &[(
1255            snapshot.line_len(row) as usize,
1256            RunStyle {
1257                font_id: style.text.font_id,
1258                color: Color::black(),
1259                underline: Default::default(),
1260            },
1261        )],
1262    )
1263}
1264
1265pub struct PaintState {
1266    bounds: RectF,
1267    gutter_bounds: RectF,
1268    text_bounds: RectF,
1269}
1270
1271impl PaintState {
1272    fn point_for_position(
1273        &self,
1274        snapshot: &EditorSnapshot,
1275        layout: &LayoutState,
1276        position: Vector2F,
1277    ) -> (DisplayPoint, u32) {
1278        let scroll_position = snapshot.scroll_position();
1279        let position = position - self.text_bounds.origin();
1280        let y = position.y().max(0.0).min(layout.size.y());
1281        let row = ((y / layout.line_height) + scroll_position.y()) as u32;
1282        let row = cmp::min(row, snapshot.max_point().row());
1283        let line = &layout.line_layouts[(row - scroll_position.y() as u32) as usize];
1284        let x = position.x() + (scroll_position.x() * layout.em_width);
1285
1286        let column = if x >= 0.0 {
1287            line.index_for_x(x)
1288                .map(|ix| ix as u32)
1289                .unwrap_or_else(|| snapshot.line_len(row))
1290        } else {
1291            0
1292        };
1293        let overshoot = (0f32.max(x - line.width()) / layout.em_advance) as u32;
1294
1295        (DisplayPoint::new(row, column), overshoot)
1296    }
1297}
1298
1299#[derive(Copy, Clone, PartialEq, Eq)]
1300pub enum CursorShape {
1301    Bar,
1302    Block,
1303    Underscore,
1304}
1305
1306impl Default for CursorShape {
1307    fn default() -> Self {
1308        CursorShape::Bar
1309    }
1310}
1311
1312struct Cursor {
1313    origin: Vector2F,
1314    block_width: f32,
1315    line_height: f32,
1316    color: Color,
1317    shape: CursorShape,
1318    block_text: Option<Line>,
1319}
1320
1321impl Cursor {
1322    fn paint(&self, cx: &mut PaintContext) {
1323        let bounds = match self.shape {
1324            CursorShape::Bar => RectF::new(self.origin, vec2f(2.0, self.line_height)),
1325            CursorShape::Block => {
1326                RectF::new(self.origin, vec2f(self.block_width, self.line_height))
1327            }
1328            CursorShape::Underscore => RectF::new(
1329                self.origin + Vector2F::new(0.0, self.line_height - 2.0),
1330                vec2f(self.block_width, 2.0),
1331            ),
1332        };
1333
1334        cx.scene.push_quad(Quad {
1335            bounds,
1336            background: Some(self.color),
1337            border: Border::new(0., Color::black()),
1338            corner_radius: 0.,
1339        });
1340
1341        if let Some(block_text) = &self.block_text {
1342            block_text.paint(self.origin, bounds, self.line_height, cx);
1343        }
1344    }
1345}
1346
1347#[derive(Debug)]
1348struct HighlightedRange {
1349    start_y: f32,
1350    line_height: f32,
1351    lines: Vec<HighlightedRangeLine>,
1352    color: Color,
1353    corner_radius: f32,
1354}
1355
1356#[derive(Debug)]
1357struct HighlightedRangeLine {
1358    start_x: f32,
1359    end_x: f32,
1360}
1361
1362impl HighlightedRange {
1363    fn paint(&self, bounds: RectF, scene: &mut Scene) {
1364        if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
1365            self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
1366            self.paint_lines(
1367                self.start_y + self.line_height,
1368                &self.lines[1..],
1369                bounds,
1370                scene,
1371            );
1372        } else {
1373            self.paint_lines(self.start_y, &self.lines, bounds, scene);
1374        }
1375    }
1376
1377    fn paint_lines(
1378        &self,
1379        start_y: f32,
1380        lines: &[HighlightedRangeLine],
1381        bounds: RectF,
1382        scene: &mut Scene,
1383    ) {
1384        if lines.is_empty() {
1385            return;
1386        }
1387
1388        let mut path = PathBuilder::new();
1389        let first_line = lines.first().unwrap();
1390        let last_line = lines.last().unwrap();
1391
1392        let first_top_left = vec2f(first_line.start_x, start_y);
1393        let first_top_right = vec2f(first_line.end_x, start_y);
1394
1395        let curve_height = vec2f(0., self.corner_radius);
1396        let curve_width = |start_x: f32, end_x: f32| {
1397            let max = (end_x - start_x) / 2.;
1398            let width = if max < self.corner_radius {
1399                max
1400            } else {
1401                self.corner_radius
1402            };
1403
1404            vec2f(width, 0.)
1405        };
1406
1407        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
1408        path.reset(first_top_right - top_curve_width);
1409        path.curve_to(first_top_right + curve_height, first_top_right);
1410
1411        let mut iter = lines.iter().enumerate().peekable();
1412        while let Some((ix, line)) = iter.next() {
1413            let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
1414
1415            if let Some((_, next_line)) = iter.peek() {
1416                let next_top_right = vec2f(next_line.end_x, bottom_right.y());
1417
1418                match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
1419                    Ordering::Equal => {
1420                        path.line_to(bottom_right);
1421                    }
1422                    Ordering::Less => {
1423                        let curve_width = curve_width(next_top_right.x(), bottom_right.x());
1424                        path.line_to(bottom_right - curve_height);
1425                        if self.corner_radius > 0. {
1426                            path.curve_to(bottom_right - curve_width, bottom_right);
1427                        }
1428                        path.line_to(next_top_right + curve_width);
1429                        if self.corner_radius > 0. {
1430                            path.curve_to(next_top_right + curve_height, next_top_right);
1431                        }
1432                    }
1433                    Ordering::Greater => {
1434                        let curve_width = curve_width(bottom_right.x(), next_top_right.x());
1435                        path.line_to(bottom_right - curve_height);
1436                        if self.corner_radius > 0. {
1437                            path.curve_to(bottom_right + curve_width, bottom_right);
1438                        }
1439                        path.line_to(next_top_right - curve_width);
1440                        if self.corner_radius > 0. {
1441                            path.curve_to(next_top_right + curve_height, next_top_right);
1442                        }
1443                    }
1444                }
1445            } else {
1446                let curve_width = curve_width(line.start_x, line.end_x);
1447                path.line_to(bottom_right - curve_height);
1448                if self.corner_radius > 0. {
1449                    path.curve_to(bottom_right - curve_width, bottom_right);
1450                }
1451
1452                let bottom_left = vec2f(line.start_x, bottom_right.y());
1453                path.line_to(bottom_left + curve_width);
1454                if self.corner_radius > 0. {
1455                    path.curve_to(bottom_left - curve_height, bottom_left);
1456                }
1457            }
1458        }
1459
1460        if first_line.start_x > last_line.start_x {
1461            let curve_width = curve_width(last_line.start_x, first_line.start_x);
1462            let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
1463            path.line_to(second_top_left + curve_height);
1464            if self.corner_radius > 0. {
1465                path.curve_to(second_top_left + curve_width, second_top_left);
1466            }
1467            let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
1468            path.line_to(first_bottom_left - curve_width);
1469            if self.corner_radius > 0. {
1470                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
1471            }
1472        }
1473
1474        path.line_to(first_top_left + curve_height);
1475        if self.corner_radius > 0. {
1476            path.curve_to(first_top_left + top_curve_width, first_top_left);
1477        }
1478        path.line_to(first_top_right - top_curve_width);
1479
1480        scene.push_path(path.build(self.color, Some(bounds)));
1481    }
1482}
1483
1484fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
1485    delta.powf(1.5) / 100.0
1486}
1487
1488fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
1489    delta.powf(1.2) / 300.0
1490}
1491
1492#[cfg(test)]
1493mod tests {
1494    use std::sync::Arc;
1495
1496    use super::*;
1497    use crate::{
1498        display_map::{BlockDisposition, BlockProperties},
1499        Editor, MultiBuffer,
1500    };
1501    use settings::Settings;
1502    use util::test::sample_text;
1503
1504    #[gpui::test]
1505    fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
1506        cx.set_global(Settings::test(cx));
1507        let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
1508        let (window_id, editor) = cx.add_window(Default::default(), |cx| {
1509            Editor::new(EditorMode::Full, buffer, None, None, cx)
1510        });
1511        let element = EditorElement::new(
1512            editor.downgrade(),
1513            editor.read(cx).style(cx),
1514            CursorShape::Bar,
1515        );
1516
1517        let layouts = editor.update(cx, |editor, cx| {
1518            let snapshot = editor.snapshot(cx);
1519            let mut presenter = cx.build_presenter(window_id, 30.);
1520            let mut layout_cx = presenter.build_layout_context(false, cx);
1521            element.layout_line_numbers(0..6, &Default::default(), &snapshot, &mut layout_cx)
1522        });
1523        assert_eq!(layouts.len(), 6);
1524    }
1525
1526    #[gpui::test]
1527    fn test_layout_with_placeholder_text_and_blocks(cx: &mut gpui::MutableAppContext) {
1528        cx.set_global(Settings::test(cx));
1529        let buffer = MultiBuffer::build_simple("", cx);
1530        let (window_id, editor) = cx.add_window(Default::default(), |cx| {
1531            Editor::new(EditorMode::Full, buffer, None, None, cx)
1532        });
1533
1534        editor.update(cx, |editor, cx| {
1535            editor.set_placeholder_text("hello", cx);
1536            editor.insert_blocks(
1537                [BlockProperties {
1538                    disposition: BlockDisposition::Above,
1539                    height: 3,
1540                    position: Anchor::min(),
1541                    render: Arc::new(|_| Empty::new().boxed()),
1542                }],
1543                cx,
1544            );
1545
1546            // Blur the editor so that it displays placeholder text.
1547            cx.blur();
1548        });
1549
1550        let mut element = EditorElement::new(
1551            editor.downgrade(),
1552            editor.read(cx).style(cx),
1553            CursorShape::Bar,
1554        );
1555
1556        let mut scene = Scene::new(1.0);
1557        let mut presenter = cx.build_presenter(window_id, 30.);
1558        let mut layout_cx = presenter.build_layout_context(false, cx);
1559        let (size, mut state) = element.layout(
1560            SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
1561            &mut layout_cx,
1562        );
1563
1564        assert_eq!(state.line_layouts.len(), 4);
1565        assert_eq!(
1566            state
1567                .line_number_layouts
1568                .iter()
1569                .map(Option::is_some)
1570                .collect::<Vec<_>>(),
1571            &[false, false, false, true]
1572        );
1573
1574        // Don't panic.
1575        let bounds = RectF::new(Default::default(), size);
1576        let mut paint_cx = presenter.build_paint_context(&mut scene, cx);
1577        element.paint(bounds, bounds, &mut state, &mut paint_cx);
1578    }
1579}