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