1use super::{
2 display_map::{BlockContext, ToDisplayPoint},
3 Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, SelectPhase, SoftWrap, ToPoint,
4 MAX_LINE_LEN,
5};
6use crate::{
7 display_map::{BlockStyle, DisplaySnapshot, FoldStatus, InlayOffset, TransformBlock},
8 editor_settings::ShowScrollbar,
9 git::{diff_hunk_to_display, DisplayDiffHunk},
10 hover_popover::{
11 hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
12 MIN_POPOVER_LINE_HEIGHT,
13 },
14 link_go_to_definition::{
15 go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
16 GoToDefinitionTrigger,
17 },
18 mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,
19};
20use clock::ReplicaId;
21use collections::{BTreeMap, HashMap};
22use git::diff::DiffHunkStatus;
23use gpui::{
24 color::Color,
25 elements::*,
26 fonts::{HighlightStyle, TextStyle, Underline},
27 geometry::{
28 rect::RectF,
29 vector::{vec2f, Vector2F},
30 PathBuilder,
31 },
32 json::{self, ToJson},
33 platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
34 text_layout::{self, Line, RunStyle, TextLayoutCache},
35 AnyElement, Axis, Border, CursorRegion, Element, EventContext, FontCache, LayoutContext,
36 MouseRegion, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,
37};
38use itertools::Itertools;
39use json::json;
40use language::{
41 language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16,
42 Selection,
43};
44use project::{
45 project_settings::{GitGutterSetting, ProjectSettings},
46 InlayHintLabelPart, Location, LocationLink, ProjectPath, ResolveState,
47};
48use smallvec::SmallVec;
49use std::{
50 borrow::Cow,
51 cmp::{self, Ordering},
52 fmt::Write,
53 iter,
54 ops::Range,
55 sync::Arc,
56};
57use text::Point;
58use workspace::item::Item;
59
60enum FoldMarkers {}
61
62struct SelectionLayout {
63 head: DisplayPoint,
64 cursor_shape: CursorShape,
65 is_newest: bool,
66 is_local: bool,
67 range: Range<DisplayPoint>,
68 active_rows: Range<u32>,
69}
70
71impl SelectionLayout {
72 fn new<T: ToPoint + ToDisplayPoint + Clone>(
73 selection: Selection<T>,
74 line_mode: bool,
75 cursor_shape: CursorShape,
76 map: &DisplaySnapshot,
77 is_newest: bool,
78 is_local: bool,
79 ) -> Self {
80 let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
81 let display_selection = point_selection.map(|p| p.to_display_point(map));
82 let mut range = display_selection.range();
83 let mut head = display_selection.head();
84 let mut active_rows = map.prev_line_boundary(point_selection.start).1.row()
85 ..map.next_line_boundary(point_selection.end).1.row();
86
87 // vim visual line mode
88 if line_mode {
89 let point_range = map.expand_to_line(point_selection.range());
90 range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map);
91 }
92
93 // any vim visual mode (including line mode)
94 if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed {
95 if head.column() > 0 {
96 head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left)
97 } else if head.row() > 0 && head != map.max_point() {
98 head = map.clip_point(
99 DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)),
100 Bias::Left,
101 );
102 // updating range.end is a no-op unless you're cursor is
103 // on the newline containing a multi-buffer divider
104 // in which case the clip_point may have moved the head up
105 // an additional row.
106 range.end = DisplayPoint::new(head.row() + 1, 0);
107 active_rows.end = head.row();
108 }
109 }
110
111 Self {
112 head,
113 cursor_shape,
114 is_newest,
115 is_local,
116 range,
117 active_rows,
118 }
119 }
120}
121
122pub struct EditorElement {
123 style: Arc<EditorStyle>,
124}
125
126impl EditorElement {
127 pub fn new(style: EditorStyle) -> Self {
128 Self {
129 style: Arc::new(style),
130 }
131 }
132
133 fn attach_mouse_handlers(
134 scene: &mut SceneBuilder,
135 position_map: &Arc<PositionMap>,
136 has_popovers: bool,
137 visible_bounds: RectF,
138 text_bounds: RectF,
139 gutter_bounds: RectF,
140 bounds: RectF,
141 cx: &mut ViewContext<Editor>,
142 ) {
143 enum EditorElementMouseHandlers {}
144 scene.push_mouse_region(
145 MouseRegion::new::<EditorElementMouseHandlers>(
146 cx.view_id(),
147 cx.view_id(),
148 visible_bounds,
149 )
150 .on_down(MouseButton::Left, {
151 let position_map = position_map.clone();
152 move |event, editor, cx| {
153 if !Self::mouse_down(
154 editor,
155 event.platform_event,
156 position_map.as_ref(),
157 text_bounds,
158 gutter_bounds,
159 cx,
160 ) {
161 cx.propagate_event();
162 }
163 }
164 })
165 .on_down(MouseButton::Right, {
166 let position_map = position_map.clone();
167 move |event, editor, cx| {
168 if !Self::mouse_right_down(
169 editor,
170 event.position,
171 position_map.as_ref(),
172 text_bounds,
173 cx,
174 ) {
175 cx.propagate_event();
176 }
177 }
178 })
179 .on_up(MouseButton::Left, {
180 let position_map = position_map.clone();
181 move |event, editor, cx| {
182 if !Self::mouse_up(
183 editor,
184 event.position,
185 event.cmd,
186 event.shift,
187 event.alt,
188 position_map.as_ref(),
189 text_bounds,
190 cx,
191 ) {
192 cx.propagate_event()
193 }
194 }
195 })
196 .on_drag(MouseButton::Left, {
197 let position_map = position_map.clone();
198 move |event, editor, cx| {
199 if event.end {
200 return;
201 }
202
203 if !Self::mouse_dragged(
204 editor,
205 event.platform_event,
206 position_map.as_ref(),
207 text_bounds,
208 cx,
209 ) {
210 cx.propagate_event()
211 }
212 }
213 })
214 .on_move({
215 let position_map = position_map.clone();
216 move |event, editor, cx| {
217 if !Self::mouse_moved(
218 editor,
219 event.platform_event,
220 &position_map,
221 text_bounds,
222 cx,
223 ) {
224 cx.propagate_event()
225 }
226 }
227 })
228 .on_move_out(move |_, editor: &mut Editor, cx| {
229 if has_popovers {
230 hide_hover(editor, cx);
231 }
232 })
233 .on_scroll({
234 let position_map = position_map.clone();
235 move |event, editor, cx| {
236 if !Self::scroll(
237 editor,
238 event.position,
239 *event.delta.raw(),
240 event.delta.precise(),
241 &position_map,
242 bounds,
243 cx,
244 ) {
245 cx.propagate_event()
246 }
247 }
248 }),
249 );
250
251 enum GutterHandlers {}
252 scene.push_mouse_region(
253 MouseRegion::new::<GutterHandlers>(cx.view_id(), cx.view_id() + 1, gutter_bounds)
254 .on_hover(|hover, editor: &mut Editor, cx| {
255 editor.gutter_hover(
256 &GutterHover {
257 hovered: hover.started,
258 },
259 cx,
260 );
261 }),
262 )
263 }
264
265 fn mouse_down(
266 editor: &mut Editor,
267 MouseButtonEvent {
268 position,
269 modifiers:
270 Modifiers {
271 shift,
272 ctrl,
273 alt,
274 cmd,
275 ..
276 },
277 mut click_count,
278 ..
279 }: MouseButtonEvent,
280 position_map: &PositionMap,
281 text_bounds: RectF,
282 gutter_bounds: RectF,
283 cx: &mut EventContext<Editor>,
284 ) -> bool {
285 if gutter_bounds.contains_point(position) {
286 click_count = 3; // Simulate triple-click when clicking the gutter to select lines
287 } else if !text_bounds.contains_point(position) {
288 return false;
289 }
290
291 let point_for_position = position_map.point_for_position(text_bounds, position);
292 let position = point_for_position.previous_valid;
293 if shift && alt {
294 editor.select(
295 SelectPhase::BeginColumnar {
296 position,
297 goal_column: point_for_position.exact_unclipped.column(),
298 },
299 cx,
300 );
301 } else if shift && !ctrl && !alt && !cmd {
302 editor.select(
303 SelectPhase::Extend {
304 position,
305 click_count,
306 },
307 cx,
308 );
309 } else {
310 editor.select(
311 SelectPhase::Begin {
312 position,
313 add: alt,
314 click_count,
315 },
316 cx,
317 );
318 }
319
320 true
321 }
322
323 fn mouse_right_down(
324 editor: &mut Editor,
325 position: Vector2F,
326 position_map: &PositionMap,
327 text_bounds: RectF,
328 cx: &mut EventContext<Editor>,
329 ) -> bool {
330 if !text_bounds.contains_point(position) {
331 return false;
332 }
333 let point_for_position = position_map.point_for_position(text_bounds, position);
334 mouse_context_menu::deploy_context_menu(
335 editor,
336 position,
337 point_for_position.previous_valid,
338 cx,
339 );
340 true
341 }
342
343 fn mouse_up(
344 editor: &mut Editor,
345 position: Vector2F,
346 cmd: bool,
347 shift: bool,
348 alt: bool,
349 position_map: &PositionMap,
350 text_bounds: RectF,
351 cx: &mut EventContext<Editor>,
352 ) -> bool {
353 let end_selection = editor.has_pending_selection();
354 let pending_nonempty_selections = editor.has_pending_nonempty_selection();
355
356 if end_selection {
357 editor.select(SelectPhase::End, cx);
358 }
359
360 if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) {
361 let point = position_map.point_for_position(text_bounds, position);
362 let could_be_inlay = point.as_valid().is_none();
363 if shift || could_be_inlay {
364 go_to_fetched_type_definition(editor, point, alt, cx);
365 } else {
366 go_to_fetched_definition(editor, point, alt, cx);
367 }
368
369 return true;
370 }
371
372 end_selection
373 }
374
375 fn mouse_dragged(
376 editor: &mut Editor,
377 MouseMovedEvent {
378 modifiers: Modifiers { cmd, shift, .. },
379 position,
380 ..
381 }: MouseMovedEvent,
382 position_map: &PositionMap,
383 text_bounds: RectF,
384 cx: &mut EventContext<Editor>,
385 ) -> bool {
386 // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
387 // Don't trigger hover popover if mouse is hovering over context menu
388 let point = if text_bounds.contains_point(position) {
389 position_map
390 .point_for_position(text_bounds, position)
391 .as_valid()
392 } else {
393 None
394 };
395
396 update_go_to_definition_link(
397 editor,
398 point
399 .map(GoToDefinitionTrigger::Text)
400 .unwrap_or(GoToDefinitionTrigger::None),
401 cmd,
402 shift,
403 cx,
404 );
405
406 if editor.has_pending_selection() {
407 let mut scroll_delta = Vector2F::zero();
408
409 let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0);
410 let top = text_bounds.origin_y() + vertical_margin;
411 let bottom = text_bounds.lower_left().y() - vertical_margin;
412 if position.y() < top {
413 scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y()))
414 }
415 if position.y() > bottom {
416 scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom))
417 }
418
419 let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0);
420 let left = text_bounds.origin_x() + horizontal_margin;
421 let right = text_bounds.upper_right().x() - horizontal_margin;
422 if position.x() < left {
423 scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta(
424 left - position.x(),
425 ))
426 }
427 if position.x() > right {
428 scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta(
429 position.x() - right,
430 ))
431 }
432
433 let point_for_position = position_map.point_for_position(text_bounds, position);
434
435 editor.select(
436 SelectPhase::Update {
437 position: point_for_position.previous_valid,
438 goal_column: point_for_position.exact_unclipped.column(),
439 scroll_position: (position_map.snapshot.scroll_position() + scroll_delta)
440 .clamp(Vector2F::zero(), position_map.scroll_max),
441 },
442 cx,
443 );
444 hover_at(editor, point, cx);
445 true
446 } else {
447 hover_at(editor, point, cx);
448 false
449 }
450 }
451
452 fn mouse_moved(
453 editor: &mut Editor,
454 MouseMovedEvent {
455 modifiers: Modifiers { shift, cmd, .. },
456 position,
457 ..
458 }: MouseMovedEvent,
459 position_map: &PositionMap,
460 text_bounds: RectF,
461 cx: &mut ViewContext<Editor>,
462 ) -> bool {
463 // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
464 // Don't trigger hover popover if mouse is hovering over context menu
465 if text_bounds.contains_point(position) {
466 let point_for_position = position_map.point_for_position(text_bounds, position);
467 match point_for_position.as_valid() {
468 Some(point) => {
469 update_go_to_definition_link(
470 editor,
471 GoToDefinitionTrigger::Text(point),
472 cmd,
473 shift,
474 cx,
475 );
476 hover_at(editor, Some(point), cx);
477 }
478 None => {
479 update_inlay_link_and_hover_points(
480 position_map,
481 point_for_position,
482 editor,
483 (cmd, shift),
484 cx,
485 );
486 }
487 }
488 } else {
489 update_go_to_definition_link(editor, GoToDefinitionTrigger::None, cmd, shift, cx);
490 hover_at(editor, None, cx);
491 }
492
493 true
494 }
495
496 fn scroll(
497 editor: &mut Editor,
498 position: Vector2F,
499 mut delta: Vector2F,
500 precise: bool,
501 position_map: &PositionMap,
502 bounds: RectF,
503 cx: &mut ViewContext<Editor>,
504 ) -> bool {
505 if !bounds.contains_point(position) {
506 return false;
507 }
508
509 let line_height = position_map.line_height;
510 let max_glyph_width = position_map.em_width;
511
512 let axis = if precise {
513 //Trackpad
514 position_map.snapshot.ongoing_scroll.filter(&mut delta)
515 } else {
516 //Not trackpad
517 delta *= vec2f(max_glyph_width, line_height);
518 None //Resets ongoing scroll
519 };
520
521 let scroll_position = position_map.snapshot.scroll_position();
522 let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width;
523 let y = (scroll_position.y() * line_height - delta.y()) / line_height;
524 let scroll_position = vec2f(x, y).clamp(Vector2F::zero(), position_map.scroll_max);
525 editor.scroll(scroll_position, axis, cx);
526
527 true
528 }
529
530 fn paint_background(
531 &self,
532 scene: &mut SceneBuilder,
533 gutter_bounds: RectF,
534 text_bounds: RectF,
535 layout: &LayoutState,
536 ) {
537 let bounds = gutter_bounds.union_rect(text_bounds);
538 let scroll_top =
539 layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height;
540 scene.push_quad(Quad {
541 bounds: gutter_bounds,
542 background: Some(self.style.gutter_background),
543 border: Border::new(0., Color::transparent_black()),
544 corner_radii: Default::default(),
545 });
546 scene.push_quad(Quad {
547 bounds: text_bounds,
548 background: Some(self.style.background),
549 border: Border::new(0., Color::transparent_black()),
550 corner_radii: Default::default(),
551 });
552
553 if let EditorMode::Full = layout.mode {
554 let mut active_rows = layout.active_rows.iter().peekable();
555 while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
556 let mut end_row = *start_row;
557 while active_rows.peek().map_or(false, |r| {
558 *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
559 }) {
560 active_rows.next().unwrap();
561 end_row += 1;
562 }
563
564 if !contains_non_empty_selection {
565 let origin = vec2f(
566 bounds.origin_x(),
567 bounds.origin_y() + (layout.position_map.line_height * *start_row as f32)
568 - scroll_top,
569 );
570 let size = vec2f(
571 bounds.width(),
572 layout.position_map.line_height * (end_row - start_row + 1) as f32,
573 );
574 scene.push_quad(Quad {
575 bounds: RectF::new(origin, size),
576 background: Some(self.style.active_line_background),
577 border: Border::default(),
578 corner_radii: Default::default(),
579 });
580 }
581 }
582
583 if let Some(highlighted_rows) = &layout.highlighted_rows {
584 let origin = vec2f(
585 bounds.origin_x(),
586 bounds.origin_y()
587 + (layout.position_map.line_height * highlighted_rows.start as f32)
588 - scroll_top,
589 );
590 let size = vec2f(
591 bounds.width(),
592 layout.position_map.line_height * highlighted_rows.len() as f32,
593 );
594 scene.push_quad(Quad {
595 bounds: RectF::new(origin, size),
596 background: Some(self.style.highlighted_line_background),
597 border: Border::default(),
598 corner_radii: Default::default(),
599 });
600 }
601
602 let scroll_left =
603 layout.position_map.snapshot.scroll_position().x() * layout.position_map.em_width;
604
605 for (wrap_position, active) in layout.wrap_guides.iter() {
606 let x =
607 (text_bounds.origin_x() + wrap_position + layout.position_map.em_width / 2.)
608 - scroll_left;
609
610 if x < text_bounds.origin_x()
611 || (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
612 {
613 continue;
614 }
615
616 let color = if *active {
617 self.style.active_wrap_guide
618 } else {
619 self.style.wrap_guide
620 };
621 scene.push_quad(Quad {
622 bounds: RectF::new(
623 vec2f(x, text_bounds.origin_y()),
624 vec2f(1., text_bounds.height()),
625 ),
626 background: Some(color),
627 border: Border::new(0., Color::transparent_black()),
628 corner_radii: Default::default(),
629 });
630 }
631 }
632 }
633
634 fn paint_gutter(
635 &mut self,
636 scene: &mut SceneBuilder,
637 bounds: RectF,
638 visible_bounds: RectF,
639 layout: &mut LayoutState,
640 editor: &mut Editor,
641 cx: &mut PaintContext<Editor>,
642 ) {
643 let line_height = layout.position_map.line_height;
644
645 let scroll_position = layout.position_map.snapshot.scroll_position();
646 let scroll_top = scroll_position.y() * line_height;
647
648 let show_gutter = matches!(
649 settings::get::<ProjectSettings>(cx).git.git_gutter,
650 Some(GitGutterSetting::TrackedFiles)
651 );
652
653 if show_gutter {
654 Self::paint_diff_hunks(scene, bounds, layout, cx);
655 }
656
657 for (ix, line) in layout.line_number_layouts.iter().enumerate() {
658 if let Some(line) = line {
659 let line_origin = bounds.origin()
660 + vec2f(
661 bounds.width() - line.width() - layout.gutter_padding,
662 ix as f32 * line_height - (scroll_top % line_height),
663 );
664
665 line.paint(scene, line_origin, visible_bounds, line_height, cx);
666 }
667 }
668
669 for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() {
670 if let Some(indicator) = fold_indicator.as_mut() {
671 let position = vec2f(
672 bounds.width() - layout.gutter_padding,
673 ix as f32 * line_height - (scroll_top % line_height),
674 );
675 let centering_offset = vec2f(
676 (layout.gutter_padding + layout.gutter_margin - indicator.size().x()) / 2.,
677 (line_height - indicator.size().y()) / 2.,
678 );
679
680 let indicator_origin = bounds.origin() + position + centering_offset;
681
682 indicator.paint(scene, indicator_origin, visible_bounds, editor, cx);
683 }
684 }
685
686 if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() {
687 let mut x = 0.;
688 let mut y = *row as f32 * line_height - scroll_top;
689 x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.;
690 y += (line_height - indicator.size().y()) / 2.;
691 indicator.paint(
692 scene,
693 bounds.origin() + vec2f(x, y),
694 visible_bounds,
695 editor,
696 cx,
697 );
698 }
699 }
700
701 fn paint_diff_hunks(
702 scene: &mut SceneBuilder,
703 bounds: RectF,
704 layout: &mut LayoutState,
705 cx: &mut ViewContext<Editor>,
706 ) {
707 let diff_style = &theme::current(cx).editor.diff.clone();
708 let line_height = layout.position_map.line_height;
709
710 let scroll_position = layout.position_map.snapshot.scroll_position();
711 let scroll_top = scroll_position.y() * line_height;
712
713 for hunk in &layout.display_hunks {
714 let (display_row_range, status) = match hunk {
715 //TODO: This rendering is entirely a horrible hack
716 &DisplayDiffHunk::Folded { display_row: row } => {
717 let start_y = row as f32 * line_height - scroll_top;
718 let end_y = start_y + line_height;
719
720 let width = diff_style.removed_width_em * line_height;
721 let highlight_origin = bounds.origin() + vec2f(-width, start_y);
722 let highlight_size = vec2f(width * 2., end_y - start_y);
723 let highlight_bounds = RectF::new(highlight_origin, highlight_size);
724
725 scene.push_quad(Quad {
726 bounds: highlight_bounds,
727 background: Some(diff_style.modified),
728 border: Border::new(0., Color::transparent_black()),
729 corner_radii: (1. * line_height).into(),
730 });
731
732 continue;
733 }
734
735 DisplayDiffHunk::Unfolded {
736 display_row_range,
737 status,
738 } => (display_row_range, status),
739 };
740
741 let color = match status {
742 DiffHunkStatus::Added => diff_style.inserted,
743 DiffHunkStatus::Modified => diff_style.modified,
744
745 //TODO: This rendering is entirely a horrible hack
746 DiffHunkStatus::Removed => {
747 let row = display_row_range.start;
748
749 let offset = line_height / 2.;
750 let start_y = row as f32 * line_height - offset - scroll_top;
751 let end_y = start_y + line_height;
752
753 let width = diff_style.removed_width_em * line_height;
754 let highlight_origin = bounds.origin() + vec2f(-width, start_y);
755 let highlight_size = vec2f(width * 2., end_y - start_y);
756 let highlight_bounds = RectF::new(highlight_origin, highlight_size);
757
758 scene.push_quad(Quad {
759 bounds: highlight_bounds,
760 background: Some(diff_style.deleted),
761 border: Border::new(0., Color::transparent_black()),
762 corner_radii: (1. * line_height).into(),
763 });
764
765 continue;
766 }
767 };
768
769 let start_row = display_row_range.start;
770 let end_row = display_row_range.end;
771
772 let start_y = start_row as f32 * line_height - scroll_top;
773 let end_y = end_row as f32 * line_height - scroll_top;
774
775 let width = diff_style.width_em * line_height;
776 let highlight_origin = bounds.origin() + vec2f(-width, start_y);
777 let highlight_size = vec2f(width * 2., end_y - start_y);
778 let highlight_bounds = RectF::new(highlight_origin, highlight_size);
779
780 scene.push_quad(Quad {
781 bounds: highlight_bounds,
782 background: Some(color),
783 border: Border::new(0., Color::transparent_black()),
784 corner_radii: (diff_style.corner_radius * line_height).into(),
785 });
786 }
787 }
788
789 fn paint_text(
790 &mut self,
791 scene: &mut SceneBuilder,
792 bounds: RectF,
793 visible_bounds: RectF,
794 layout: &mut LayoutState,
795 editor: &mut Editor,
796 cx: &mut PaintContext<Editor>,
797 ) {
798 let style = &self.style;
799 let scroll_position = layout.position_map.snapshot.scroll_position();
800 let start_row = layout.visible_display_row_range.start;
801 let scroll_top = scroll_position.y() * layout.position_map.line_height;
802 let max_glyph_width = layout.position_map.em_width;
803 let scroll_left = scroll_position.x() * max_glyph_width;
804 let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.);
805 let line_end_overshoot = 0.15 * layout.position_map.line_height;
806 let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
807
808 scene.push_layer(Some(bounds));
809
810 scene.push_cursor_region(CursorRegion {
811 bounds,
812 style: if !editor.link_go_to_definition_state.definitions.is_empty() {
813 CursorStyle::PointingHand
814 } else {
815 CursorStyle::IBeam
816 },
817 });
818
819 let fold_corner_radius =
820 self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height;
821 for (id, range, color) in layout.fold_ranges.iter() {
822 self.paint_highlighted_range(
823 scene,
824 range.clone(),
825 *color,
826 fold_corner_radius,
827 fold_corner_radius * 2.,
828 layout,
829 content_origin,
830 scroll_top,
831 scroll_left,
832 bounds,
833 );
834
835 for bound in range_to_bounds(
836 &range,
837 content_origin,
838 scroll_left,
839 scroll_top,
840 &layout.visible_display_row_range,
841 line_end_overshoot,
842 &layout.position_map,
843 ) {
844 scene.push_cursor_region(CursorRegion {
845 bounds: bound,
846 style: CursorStyle::PointingHand,
847 });
848
849 let display_row = range.start.row();
850
851 let buffer_row = DisplayPoint::new(display_row, 0)
852 .to_point(&layout.position_map.snapshot.display_snapshot)
853 .row;
854
855 scene.push_mouse_region(
856 MouseRegion::new::<FoldMarkers>(cx.view_id(), *id as usize, bound)
857 .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| {
858 editor.unfold_at(&UnfoldAt { buffer_row }, cx)
859 })
860 .with_notify_on_hover(true)
861 .with_notify_on_click(true),
862 )
863 }
864 }
865
866 for (range, color) in &layout.highlighted_ranges {
867 self.paint_highlighted_range(
868 scene,
869 range.clone(),
870 *color,
871 0.,
872 line_end_overshoot,
873 layout,
874 content_origin,
875 scroll_top,
876 scroll_left,
877 bounds,
878 );
879 }
880
881 let mut cursors = SmallVec::<[Cursor; 32]>::new();
882 let corner_radius = 0.15 * layout.position_map.line_height;
883 let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
884
885 for (replica_id, selections) in &layout.selections {
886 let replica_id = *replica_id;
887 let selection_style = if let Some(replica_id) = replica_id {
888 style.replica_selection_style(replica_id)
889 } else {
890 &style.absent_selection
891 };
892
893 for selection in selections {
894 self.paint_highlighted_range(
895 scene,
896 selection.range.clone(),
897 selection_style.selection,
898 corner_radius,
899 corner_radius * 2.,
900 layout,
901 content_origin,
902 scroll_top,
903 scroll_left,
904 bounds,
905 );
906
907 if selection.is_local && !selection.range.is_empty() {
908 invisible_display_ranges.push(selection.range.clone());
909 }
910 if !selection.is_local || editor.show_local_cursors(cx) {
911 let cursor_position = selection.head;
912 if layout
913 .visible_display_row_range
914 .contains(&cursor_position.row())
915 {
916 let cursor_row_layout = &layout.position_map.line_layouts
917 [(cursor_position.row() - start_row) as usize]
918 .line;
919 let cursor_column = cursor_position.column() as usize;
920
921 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
922 let mut block_width =
923 cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x;
924 if block_width == 0.0 {
925 block_width = layout.position_map.em_width;
926 }
927 let block_text = if let CursorShape::Block = selection.cursor_shape {
928 layout
929 .position_map
930 .snapshot
931 .chars_at(cursor_position)
932 .next()
933 .and_then(|(character, _)| {
934 let font_id =
935 cursor_row_layout.font_for_index(cursor_column)?;
936 let text = character.to_string();
937
938 Some(cx.text_layout_cache().layout_str(
939 &text,
940 cursor_row_layout.font_size(),
941 &[(
942 text.chars().count(),
943 RunStyle {
944 font_id,
945 color: style.background,
946 underline: Default::default(),
947 },
948 )],
949 ))
950 })
951 } else {
952 None
953 };
954
955 let x = cursor_character_x - scroll_left;
956 let y = cursor_position.row() as f32 * layout.position_map.line_height
957 - scroll_top;
958 if selection.is_newest {
959 editor.pixel_position_of_newest_cursor = Some(vec2f(
960 bounds.origin_x() + x + block_width / 2.,
961 bounds.origin_y() + y + layout.position_map.line_height / 2.,
962 ));
963 }
964 cursors.push(Cursor {
965 color: selection_style.cursor,
966 block_width,
967 origin: vec2f(x, y),
968 line_height: layout.position_map.line_height,
969 shape: selection.cursor_shape,
970 block_text,
971 });
972 }
973 }
974 }
975 }
976
977 if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) {
978 for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
979 let row = start_row + ix as u32;
980 line_with_invisibles.draw(
981 layout,
982 row,
983 scroll_top,
984 scene,
985 content_origin,
986 scroll_left,
987 visible_text_bounds,
988 whitespace_setting,
989 &invisible_display_ranges,
990 visible_bounds,
991 cx,
992 )
993 }
994 }
995
996 scene.paint_layer(Some(bounds), |scene| {
997 for cursor in cursors {
998 cursor.paint(scene, content_origin, cx);
999 }
1000 });
1001
1002 if let Some((position, context_menu)) = layout.context_menu.as_mut() {
1003 scene.push_stacking_context(None, None);
1004 let cursor_row_layout =
1005 &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
1006 let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
1007 let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top;
1008 let mut list_origin = content_origin + vec2f(x, y);
1009 let list_width = context_menu.size().x();
1010 let list_height = context_menu.size().y();
1011
1012 // Snap the right edge of the list to the right edge of the window if
1013 // its horizontal bounds overflow.
1014 if list_origin.x() + list_width > cx.window_size().x() {
1015 list_origin.set_x((cx.window_size().x() - list_width).max(0.));
1016 }
1017
1018 if list_origin.y() + list_height > bounds.max_y() {
1019 list_origin.set_y(list_origin.y() - layout.position_map.line_height - list_height);
1020 }
1021
1022 context_menu.paint(
1023 scene,
1024 list_origin,
1025 RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
1026 editor,
1027 cx,
1028 );
1029
1030 scene.pop_stacking_context();
1031 }
1032
1033 if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
1034 scene.push_stacking_context(None, None);
1035
1036 // This is safe because we check on layout whether the required row is available
1037 let hovered_row_layout =
1038 &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
1039
1040 // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
1041 // height. This is the size we will use to decide whether to render popovers above or below
1042 // the hovered line.
1043 let first_size = hover_popovers[0].size();
1044 let height_to_reserve = first_size.y()
1045 + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height;
1046
1047 // Compute Hovered Point
1048 let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left;
1049 let y = position.row() as f32 * layout.position_map.line_height - scroll_top;
1050 let hovered_point = content_origin + vec2f(x, y);
1051
1052 if hovered_point.y() - height_to_reserve > 0.0 {
1053 // There is enough space above. Render popovers above the hovered point
1054 let mut current_y = hovered_point.y();
1055 for hover_popover in hover_popovers {
1056 let size = hover_popover.size();
1057 let mut popover_origin = vec2f(hovered_point.x(), current_y - size.y());
1058
1059 let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x());
1060 if x_out_of_bounds < 0.0 {
1061 popover_origin.set_x(popover_origin.x() + x_out_of_bounds);
1062 }
1063
1064 hover_popover.paint(
1065 scene,
1066 popover_origin,
1067 RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
1068 editor,
1069 cx,
1070 );
1071
1072 current_y = popover_origin.y() - HOVER_POPOVER_GAP;
1073 }
1074 } else {
1075 // There is not enough space above. Render popovers below the hovered point
1076 let mut current_y = hovered_point.y() + layout.position_map.line_height;
1077 for hover_popover in hover_popovers {
1078 let size = hover_popover.size();
1079 let mut popover_origin = vec2f(hovered_point.x(), current_y);
1080
1081 let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x());
1082 if x_out_of_bounds < 0.0 {
1083 popover_origin.set_x(popover_origin.x() + x_out_of_bounds);
1084 }
1085
1086 hover_popover.paint(
1087 scene,
1088 popover_origin,
1089 RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
1090 editor,
1091 cx,
1092 );
1093
1094 current_y = popover_origin.y() + size.y() + HOVER_POPOVER_GAP;
1095 }
1096 }
1097
1098 scene.pop_stacking_context();
1099 }
1100
1101 scene.pop_layer();
1102 }
1103
1104 fn scrollbar_left(&self, bounds: &RectF) -> f32 {
1105 bounds.max_x() - self.style.theme.scrollbar.width
1106 }
1107
1108 fn paint_scrollbar(
1109 &mut self,
1110 scene: &mut SceneBuilder,
1111 bounds: RectF,
1112 layout: &mut LayoutState,
1113 cx: &mut ViewContext<Editor>,
1114 editor: &Editor,
1115 ) {
1116 enum ScrollbarMouseHandlers {}
1117 if layout.mode != EditorMode::Full {
1118 return;
1119 }
1120
1121 let style = &self.style.theme.scrollbar;
1122
1123 let top = bounds.min_y();
1124 let bottom = bounds.max_y();
1125 let right = bounds.max_x();
1126 let left = self.scrollbar_left(&bounds);
1127 let row_range = &layout.scrollbar_row_range;
1128 let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
1129
1130 let mut height = bounds.height();
1131 let mut first_row_y_offset = 0.0;
1132
1133 // Impose a minimum height on the scrollbar thumb
1134 let row_height = height / max_row;
1135 let min_thumb_height =
1136 style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size);
1137 let thumb_height = (row_range.end - row_range.start) * row_height;
1138 if thumb_height < min_thumb_height {
1139 first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
1140 height -= min_thumb_height - thumb_height;
1141 }
1142
1143 let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * row_height };
1144
1145 let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
1146 let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
1147 let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom));
1148 let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom));
1149
1150 if layout.show_scrollbars {
1151 scene.push_quad(Quad {
1152 bounds: track_bounds,
1153 border: style.track.border,
1154 background: style.track.background_color,
1155 ..Default::default()
1156 });
1157 let scrollbar_settings = settings::get::<EditorSettings>(cx).scrollbar;
1158 let theme = theme::current(cx);
1159 let scrollbar_theme = &theme.editor.scrollbar;
1160 if layout.is_singleton && scrollbar_settings.selections {
1161 let start_anchor = Anchor::min();
1162 let end_anchor = Anchor::max();
1163 let color = scrollbar_theme.selections;
1164 let border = Border {
1165 width: 1.,
1166 color: style.thumb.border.color,
1167 overlay: false,
1168 top: false,
1169 right: true,
1170 bottom: false,
1171 left: true,
1172 };
1173 let mut push_region = |start: DisplayPoint, end: DisplayPoint| {
1174 let start_y = y_for_row(start.row() as f32);
1175 let mut end_y = y_for_row(end.row() as f32);
1176 if end_y - start_y < 1. {
1177 end_y = start_y + 1.;
1178 }
1179 let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
1180
1181 scene.push_quad(Quad {
1182 bounds,
1183 background: Some(color),
1184 border,
1185 corner_radii: style.thumb.corner_radii.into(),
1186 })
1187 };
1188 let background_ranges = editor
1189 .background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
1190 start_anchor..end_anchor,
1191 &layout.position_map.snapshot,
1192 50000,
1193 );
1194 for row in background_ranges {
1195 let start = row.start();
1196 let end = row.end();
1197 push_region(*start, *end);
1198 }
1199 }
1200
1201 if layout.is_singleton && scrollbar_settings.git_diff {
1202 let diff_style = scrollbar_theme.git.clone();
1203 for hunk in layout
1204 .position_map
1205 .snapshot
1206 .buffer_snapshot
1207 .git_diff_hunks_in_range(0..(max_row.floor() as u32))
1208 {
1209 let start_display = Point::new(hunk.buffer_range.start, 0)
1210 .to_display_point(&layout.position_map.snapshot.display_snapshot);
1211 let end_display = Point::new(hunk.buffer_range.end, 0)
1212 .to_display_point(&layout.position_map.snapshot.display_snapshot);
1213 let start_y = y_for_row(start_display.row() as f32);
1214 let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
1215 y_for_row((end_display.row() + 1) as f32)
1216 } else {
1217 y_for_row((end_display.row()) as f32)
1218 };
1219
1220 if end_y - start_y < 1. {
1221 end_y = start_y + 1.;
1222 }
1223 let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
1224
1225 let color = match hunk.status() {
1226 DiffHunkStatus::Added => diff_style.inserted,
1227 DiffHunkStatus::Modified => diff_style.modified,
1228 DiffHunkStatus::Removed => diff_style.deleted,
1229 };
1230
1231 let border = Border {
1232 width: 1.,
1233 color: style.thumb.border.color,
1234 overlay: false,
1235 top: false,
1236 right: true,
1237 bottom: false,
1238 left: true,
1239 };
1240
1241 scene.push_quad(Quad {
1242 bounds,
1243 background: Some(color),
1244 border,
1245 corner_radii: style.thumb.corner_radii.into(),
1246 })
1247 }
1248 }
1249
1250 scene.push_quad(Quad {
1251 bounds: thumb_bounds,
1252 border: style.thumb.border,
1253 background: style.thumb.background_color,
1254 corner_radii: style.thumb.corner_radii.into(),
1255 });
1256 }
1257
1258 scene.push_cursor_region(CursorRegion {
1259 bounds: track_bounds,
1260 style: CursorStyle::Arrow,
1261 });
1262 scene.push_mouse_region(
1263 MouseRegion::new::<ScrollbarMouseHandlers>(cx.view_id(), cx.view_id(), track_bounds)
1264 .on_move(move |event, editor: &mut Editor, cx| {
1265 if event.pressed_button.is_none() {
1266 editor.scroll_manager.show_scrollbar(cx);
1267 }
1268 })
1269 .on_down(MouseButton::Left, {
1270 let row_range = row_range.clone();
1271 move |event, editor: &mut Editor, cx| {
1272 let y = event.position.y();
1273 if y < thumb_top || thumb_bottom < y {
1274 let center_row = ((y - top) * max_row as f32 / height).round() as u32;
1275 let top_row = center_row
1276 .saturating_sub((row_range.end - row_range.start) as u32 / 2);
1277 let mut position = editor.scroll_position(cx);
1278 position.set_y(top_row as f32);
1279 editor.set_scroll_position(position, cx);
1280 } else {
1281 editor.scroll_manager.show_scrollbar(cx);
1282 }
1283 }
1284 })
1285 .on_drag(MouseButton::Left, {
1286 move |event, editor: &mut Editor, cx| {
1287 if event.end {
1288 return;
1289 }
1290
1291 let y = event.prev_mouse_position.y();
1292 let new_y = event.position.y();
1293 if thumb_top < y && y < thumb_bottom {
1294 let mut position = editor.scroll_position(cx);
1295 position.set_y(position.y() + (new_y - y) * (max_row as f32) / height);
1296 if position.y() < 0.0 {
1297 position.set_y(0.);
1298 }
1299 editor.set_scroll_position(position, cx);
1300 }
1301 }
1302 }),
1303 );
1304 }
1305
1306 #[allow(clippy::too_many_arguments)]
1307 fn paint_highlighted_range(
1308 &self,
1309 scene: &mut SceneBuilder,
1310 range: Range<DisplayPoint>,
1311 color: Color,
1312 corner_radius: f32,
1313 line_end_overshoot: f32,
1314 layout: &LayoutState,
1315 content_origin: Vector2F,
1316 scroll_top: f32,
1317 scroll_left: f32,
1318 bounds: RectF,
1319 ) {
1320 let start_row = layout.visible_display_row_range.start;
1321 let end_row = layout.visible_display_row_range.end;
1322 if range.start != range.end {
1323 let row_range = if range.end.column() == 0 {
1324 cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
1325 } else {
1326 cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
1327 };
1328
1329 let highlighted_range = HighlightedRange {
1330 color,
1331 line_height: layout.position_map.line_height,
1332 corner_radius,
1333 start_y: content_origin.y()
1334 + row_range.start as f32 * layout.position_map.line_height
1335 - scroll_top,
1336 lines: row_range
1337 .into_iter()
1338 .map(|row| {
1339 let line_layout =
1340 &layout.position_map.line_layouts[(row - start_row) as usize].line;
1341 HighlightedRangeLine {
1342 start_x: if row == range.start.row() {
1343 content_origin.x()
1344 + line_layout.x_for_index(range.start.column() as usize)
1345 - scroll_left
1346 } else {
1347 content_origin.x() - scroll_left
1348 },
1349 end_x: if row == range.end.row() {
1350 content_origin.x()
1351 + line_layout.x_for_index(range.end.column() as usize)
1352 - scroll_left
1353 } else {
1354 content_origin.x() + line_layout.width() + line_end_overshoot
1355 - scroll_left
1356 },
1357 }
1358 })
1359 .collect(),
1360 };
1361
1362 highlighted_range.paint(bounds, scene);
1363 }
1364 }
1365
1366 fn paint_blocks(
1367 &mut self,
1368 scene: &mut SceneBuilder,
1369 bounds: RectF,
1370 visible_bounds: RectF,
1371 layout: &mut LayoutState,
1372 editor: &mut Editor,
1373 cx: &mut PaintContext<Editor>,
1374 ) {
1375 let scroll_position = layout.position_map.snapshot.scroll_position();
1376 let scroll_left = scroll_position.x() * layout.position_map.em_width;
1377 let scroll_top = scroll_position.y() * layout.position_map.line_height;
1378
1379 for block in &mut layout.blocks {
1380 let mut origin = bounds.origin()
1381 + vec2f(
1382 0.,
1383 block.row as f32 * layout.position_map.line_height - scroll_top,
1384 );
1385 if !matches!(block.style, BlockStyle::Sticky) {
1386 origin += vec2f(-scroll_left, 0.);
1387 }
1388 block
1389 .element
1390 .paint(scene, origin, visible_bounds, editor, cx);
1391 }
1392 }
1393
1394 fn column_pixels(&self, column: usize, cx: &ViewContext<Editor>) -> f32 {
1395 let style = &self.style;
1396
1397 cx.text_layout_cache()
1398 .layout_str(
1399 " ".repeat(column).as_str(),
1400 style.text.font_size,
1401 &[(
1402 column,
1403 RunStyle {
1404 font_id: style.text.font_id,
1405 color: Color::black(),
1406 underline: Default::default(),
1407 },
1408 )],
1409 )
1410 .width()
1411 }
1412
1413 fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
1414 let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
1415 self.column_pixels(digit_count, cx)
1416 }
1417
1418 //Folds contained in a hunk are ignored apart from shrinking visual size
1419 //If a fold contains any hunks then that fold line is marked as modified
1420 fn layout_git_gutters(
1421 &self,
1422 display_rows: Range<u32>,
1423 snapshot: &EditorSnapshot,
1424 ) -> Vec<DisplayDiffHunk> {
1425 let buffer_snapshot = &snapshot.buffer_snapshot;
1426
1427 let buffer_start_row = DisplayPoint::new(display_rows.start, 0)
1428 .to_point(snapshot)
1429 .row;
1430 let buffer_end_row = DisplayPoint::new(display_rows.end, 0)
1431 .to_point(snapshot)
1432 .row;
1433
1434 buffer_snapshot
1435 .git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
1436 .map(|hunk| diff_hunk_to_display(hunk, snapshot))
1437 .dedup()
1438 .collect()
1439 }
1440
1441 fn layout_line_numbers(
1442 &self,
1443 rows: Range<u32>,
1444 active_rows: &BTreeMap<u32, bool>,
1445 is_singleton: bool,
1446 snapshot: &EditorSnapshot,
1447 cx: &ViewContext<Editor>,
1448 ) -> (
1449 Vec<Option<text_layout::Line>>,
1450 Vec<Option<(FoldStatus, BufferRow, bool)>>,
1451 ) {
1452 let style = &self.style;
1453 let include_line_numbers = snapshot.mode == EditorMode::Full;
1454 let mut line_number_layouts = Vec::with_capacity(rows.len());
1455 let mut fold_statuses = Vec::with_capacity(rows.len());
1456 let mut line_number = String::new();
1457 for (ix, row) in snapshot
1458 .buffer_rows(rows.start)
1459 .take((rows.end - rows.start) as usize)
1460 .enumerate()
1461 {
1462 let display_row = rows.start + ix as u32;
1463 let (active, color) = if active_rows.contains_key(&display_row) {
1464 (true, style.line_number_active)
1465 } else {
1466 (false, style.line_number)
1467 };
1468 if let Some(buffer_row) = row {
1469 if include_line_numbers {
1470 line_number.clear();
1471 write!(&mut line_number, "{}", buffer_row + 1).unwrap();
1472 line_number_layouts.push(Some(cx.text_layout_cache().layout_str(
1473 &line_number,
1474 style.text.font_size,
1475 &[(
1476 line_number.len(),
1477 RunStyle {
1478 font_id: style.text.font_id,
1479 color,
1480 underline: Default::default(),
1481 },
1482 )],
1483 )));
1484 fold_statuses.push(
1485 is_singleton
1486 .then(|| {
1487 snapshot
1488 .fold_for_line(buffer_row)
1489 .map(|fold_status| (fold_status, buffer_row, active))
1490 })
1491 .flatten(),
1492 )
1493 }
1494 } else {
1495 fold_statuses.push(None);
1496 line_number_layouts.push(None);
1497 }
1498 }
1499
1500 (line_number_layouts, fold_statuses)
1501 }
1502
1503 fn layout_lines(
1504 &mut self,
1505 rows: Range<u32>,
1506 line_number_layouts: &[Option<Line>],
1507 snapshot: &EditorSnapshot,
1508 cx: &ViewContext<Editor>,
1509 ) -> Vec<LineWithInvisibles> {
1510 if rows.start >= rows.end {
1511 return Vec::new();
1512 }
1513
1514 // When the editor is empty and unfocused, then show the placeholder.
1515 if snapshot.is_empty() {
1516 let placeholder_style = self
1517 .style
1518 .placeholder_text
1519 .as_ref()
1520 .unwrap_or(&self.style.text);
1521 let placeholder_text = snapshot.placeholder_text();
1522 let placeholder_lines = placeholder_text
1523 .as_ref()
1524 .map_or("", AsRef::as_ref)
1525 .split('\n')
1526 .skip(rows.start as usize)
1527 .chain(iter::repeat(""))
1528 .take(rows.len());
1529 placeholder_lines
1530 .map(|line| {
1531 cx.text_layout_cache().layout_str(
1532 line,
1533 placeholder_style.font_size,
1534 &[(
1535 line.len(),
1536 RunStyle {
1537 font_id: placeholder_style.font_id,
1538 color: placeholder_style.color,
1539 underline: Default::default(),
1540 },
1541 )],
1542 )
1543 })
1544 .map(|line| LineWithInvisibles {
1545 line,
1546 invisibles: Vec::new(),
1547 })
1548 .collect()
1549 } else {
1550 let style = &self.style;
1551 let chunks = snapshot
1552 .chunks(
1553 rows.clone(),
1554 true,
1555 Some(style.theme.hint),
1556 Some(style.theme.suggestion),
1557 )
1558 .map(|chunk| {
1559 let mut highlight_style = chunk
1560 .syntax_highlight_id
1561 .and_then(|id| id.style(&style.syntax));
1562
1563 if let Some(chunk_highlight) = chunk.highlight_style {
1564 if let Some(highlight_style) = highlight_style.as_mut() {
1565 highlight_style.highlight(chunk_highlight);
1566 } else {
1567 highlight_style = Some(chunk_highlight);
1568 }
1569 }
1570
1571 let mut diagnostic_highlight = HighlightStyle::default();
1572
1573 if chunk.is_unnecessary {
1574 diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
1575 }
1576
1577 if let Some(severity) = chunk.diagnostic_severity {
1578 // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
1579 if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
1580 let diagnostic_style = super::diagnostic_style(severity, true, style);
1581 diagnostic_highlight.underline = Some(Underline {
1582 color: Some(diagnostic_style.message.text.color),
1583 thickness: 1.0.into(),
1584 squiggly: true,
1585 });
1586 }
1587 }
1588
1589 if let Some(highlight_style) = highlight_style.as_mut() {
1590 highlight_style.highlight(diagnostic_highlight);
1591 } else {
1592 highlight_style = Some(diagnostic_highlight);
1593 }
1594
1595 HighlightedChunk {
1596 chunk: chunk.text,
1597 style: highlight_style,
1598 is_tab: chunk.is_tab,
1599 }
1600 });
1601
1602 LineWithInvisibles::from_chunks(
1603 chunks,
1604 &style.text,
1605 cx.text_layout_cache(),
1606 cx.font_cache(),
1607 MAX_LINE_LEN,
1608 rows.len() as usize,
1609 line_number_layouts,
1610 snapshot.mode,
1611 )
1612 }
1613 }
1614
1615 #[allow(clippy::too_many_arguments)]
1616 fn layout_blocks(
1617 &mut self,
1618 rows: Range<u32>,
1619 snapshot: &EditorSnapshot,
1620 editor_width: f32,
1621 scroll_width: f32,
1622 gutter_padding: f32,
1623 gutter_width: f32,
1624 em_width: f32,
1625 text_x: f32,
1626 line_height: f32,
1627 style: &EditorStyle,
1628 line_layouts: &[LineWithInvisibles],
1629 editor: &mut Editor,
1630 cx: &mut LayoutContext<Editor>,
1631 ) -> (f32, Vec<BlockLayout>) {
1632 let mut block_id = 0;
1633 let scroll_x = snapshot.scroll_anchor.offset.x();
1634 let (fixed_blocks, non_fixed_blocks) = snapshot
1635 .blocks_in_range(rows.clone())
1636 .partition::<Vec<_>, _>(|(_, block)| match block {
1637 TransformBlock::ExcerptHeader { .. } => false,
1638 TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
1639 });
1640 let mut render_block = |block: &TransformBlock, width: f32, block_id: usize| {
1641 let mut element = match block {
1642 TransformBlock::Custom(block) => {
1643 let align_to = block
1644 .position()
1645 .to_point(&snapshot.buffer_snapshot)
1646 .to_display_point(snapshot);
1647 let anchor_x = text_x
1648 + if rows.contains(&align_to.row()) {
1649 line_layouts[(align_to.row() - rows.start) as usize]
1650 .line
1651 .x_for_index(align_to.column() as usize)
1652 } else {
1653 layout_line(align_to.row(), snapshot, style, cx.text_layout_cache())
1654 .x_for_index(align_to.column() as usize)
1655 };
1656
1657 block.render(&mut BlockContext {
1658 view_context: cx,
1659 anchor_x,
1660 gutter_padding,
1661 line_height,
1662 scroll_x,
1663 gutter_width,
1664 em_width,
1665 block_id,
1666 })
1667 }
1668 TransformBlock::ExcerptHeader {
1669 id,
1670 buffer,
1671 range,
1672 starts_new_buffer,
1673 ..
1674 } => {
1675 let tooltip_style = theme::current(cx).tooltip.clone();
1676 let include_root = editor
1677 .project
1678 .as_ref()
1679 .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
1680 .unwrap_or_default();
1681 let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
1682 let jump_path = ProjectPath {
1683 worktree_id: file.worktree_id(cx),
1684 path: file.path.clone(),
1685 };
1686 let jump_anchor = range
1687 .primary
1688 .as_ref()
1689 .map_or(range.context.start, |primary| primary.start);
1690 let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
1691
1692 enum JumpIcon {}
1693 MouseEventHandler::new::<JumpIcon, _>((*id).into(), cx, |state, _| {
1694 let style = style.jump_icon.style_for(state);
1695 Svg::new("icons/arrow_up_right_8.svg")
1696 .with_color(style.color)
1697 .constrained()
1698 .with_width(style.icon_width)
1699 .aligned()
1700 .contained()
1701 .with_style(style.container)
1702 .constrained()
1703 .with_width(style.button_width)
1704 .with_height(style.button_width)
1705 })
1706 .with_cursor_style(CursorStyle::PointingHand)
1707 .on_click(MouseButton::Left, move |_, editor, cx| {
1708 if let Some(workspace) = editor
1709 .workspace
1710 .as_ref()
1711 .and_then(|(workspace, _)| workspace.upgrade(cx))
1712 {
1713 workspace.update(cx, |workspace, cx| {
1714 Editor::jump(
1715 workspace,
1716 jump_path.clone(),
1717 jump_position,
1718 jump_anchor,
1719 cx,
1720 );
1721 });
1722 }
1723 })
1724 .with_tooltip::<JumpIcon>(
1725 (*id).into(),
1726 "Jump to Buffer".to_string(),
1727 Some(Box::new(crate::OpenExcerpts)),
1728 tooltip_style.clone(),
1729 cx,
1730 )
1731 .aligned()
1732 .flex_float()
1733 });
1734
1735 if *starts_new_buffer {
1736 let editor_font_size = style.text.font_size;
1737 let style = &style.diagnostic_path_header;
1738 let font_size = (style.text_scale_factor * editor_font_size).round();
1739
1740 let path = buffer.resolve_file_path(cx, include_root);
1741 let mut filename = None;
1742 let mut parent_path = None;
1743 // Can't use .and_then() because `.file_name()` and `.parent()` return references :(
1744 if let Some(path) = path {
1745 filename = path.file_name().map(|f| f.to_string_lossy().to_string());
1746 parent_path =
1747 path.parent().map(|p| p.to_string_lossy().to_string() + "/");
1748 }
1749
1750 Flex::row()
1751 .with_child(
1752 Label::new(
1753 filename.unwrap_or_else(|| "untitled".to_string()),
1754 style.filename.text.clone().with_font_size(font_size),
1755 )
1756 .contained()
1757 .with_style(style.filename.container)
1758 .aligned(),
1759 )
1760 .with_children(parent_path.map(|path| {
1761 Label::new(path, style.path.text.clone().with_font_size(font_size))
1762 .contained()
1763 .with_style(style.path.container)
1764 .aligned()
1765 }))
1766 .with_children(jump_icon)
1767 .contained()
1768 .with_style(style.container)
1769 .with_padding_left(gutter_padding)
1770 .with_padding_right(gutter_padding)
1771 .expanded()
1772 .into_any_named("path header block")
1773 } else {
1774 let text_style = style.text.clone();
1775 Flex::row()
1776 .with_child(Label::new("⋯", text_style))
1777 .with_children(jump_icon)
1778 .contained()
1779 .with_padding_left(gutter_padding)
1780 .with_padding_right(gutter_padding)
1781 .expanded()
1782 .into_any_named("collapsed context")
1783 }
1784 }
1785 };
1786
1787 element.layout(
1788 SizeConstraint {
1789 min: Vector2F::zero(),
1790 max: vec2f(width, block.height() as f32 * line_height),
1791 },
1792 editor,
1793 cx,
1794 );
1795 element
1796 };
1797
1798 let mut fixed_block_max_width = 0f32;
1799 let mut blocks = Vec::new();
1800 for (row, block) in fixed_blocks {
1801 let element = render_block(block, f32::INFINITY, block_id);
1802 block_id += 1;
1803 fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width);
1804 blocks.push(BlockLayout {
1805 row,
1806 element,
1807 style: BlockStyle::Fixed,
1808 });
1809 }
1810 for (row, block) in non_fixed_blocks {
1811 let style = match block {
1812 TransformBlock::Custom(block) => block.style(),
1813 TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
1814 };
1815 let width = match style {
1816 BlockStyle::Sticky => editor_width,
1817 BlockStyle::Flex => editor_width
1818 .max(fixed_block_max_width)
1819 .max(gutter_width + scroll_width),
1820 BlockStyle::Fixed => unreachable!(),
1821 };
1822 let element = render_block(block, width, block_id);
1823 block_id += 1;
1824 blocks.push(BlockLayout {
1825 row,
1826 element,
1827 style,
1828 });
1829 }
1830 (
1831 scroll_width.max(fixed_block_max_width - gutter_width),
1832 blocks,
1833 )
1834 }
1835}
1836
1837fn update_inlay_link_and_hover_points(
1838 position_map: &PositionMap,
1839 point_for_position: PointForPosition,
1840 editor: &mut Editor,
1841 (cmd_held, shift_held): (bool, bool),
1842 cx: &mut ViewContext<'_, '_, Editor>,
1843) {
1844 let hint_start_offset = position_map
1845 .snapshot
1846 .display_point_to_inlay_offset(point_for_position.previous_valid, Bias::Left);
1847 let hint_end_offset = position_map
1848 .snapshot
1849 .display_point_to_inlay_offset(point_for_position.next_valid, Bias::Right);
1850 let offset_overshoot = point_for_position.column_overshoot_after_line_end as usize;
1851 let hovered_offset = if offset_overshoot == 0 {
1852 Some(
1853 position_map
1854 .snapshot
1855 .display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left),
1856 )
1857 } else if (hint_end_offset - hint_start_offset).0 >= offset_overshoot {
1858 Some(InlayOffset(hint_start_offset.0 + offset_overshoot))
1859 } else {
1860 None
1861 };
1862 if let Some(hovered_offset) = hovered_offset {
1863 let buffer = editor.buffer().read(cx);
1864 let snapshot = buffer.snapshot(cx);
1865 let previous_valid_anchor = snapshot.anchor_at(
1866 point_for_position
1867 .previous_valid
1868 .to_point(&position_map.snapshot.display_snapshot),
1869 Bias::Left,
1870 );
1871 let next_valid_anchor = snapshot.anchor_at(
1872 point_for_position
1873 .next_valid
1874 .to_point(&position_map.snapshot.display_snapshot),
1875 Bias::Right,
1876 );
1877
1878 let mut go_to_definition_updated = false;
1879 let mut hover_updated = false;
1880 if let Some(hovered_hint) = editor
1881 .visible_inlay_hints(cx)
1882 .into_iter()
1883 .skip_while(|hint| hint.position.cmp(&previous_valid_anchor, &snapshot).is_lt())
1884 .take_while(|hint| hint.position.cmp(&next_valid_anchor, &snapshot).is_le())
1885 .max_by_key(|hint| hint.id)
1886 {
1887 let inlay_hint_cache = editor.inlay_hint_cache();
1888 if let Some(cached_hint) =
1889 inlay_hint_cache.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id)
1890 {
1891 match cached_hint.resolve_state {
1892 ResolveState::CanResolve(_, _) => {
1893 if let Some(buffer_id) = previous_valid_anchor.buffer_id {
1894 inlay_hint_cache.spawn_hint_resolve(
1895 buffer_id,
1896 previous_valid_anchor.excerpt_id,
1897 hovered_hint.id,
1898 cx,
1899 );
1900 }
1901 }
1902 ResolveState::Resolved => {
1903 match cached_hint.label {
1904 project::InlayHintLabel::String(_) => {
1905 if cached_hint.tooltip.is_some() {
1906 dbg!(&cached_hint.tooltip); // TODO kb
1907 // hover_at_point = Some(hovered_offset);
1908 }
1909 }
1910 project::InlayHintLabel::LabelParts(label_parts) => {
1911 if let Some(hovered_hint_part) = find_hovered_hint_part(
1912 label_parts,
1913 hint_start_offset..hint_end_offset,
1914 hovered_offset,
1915 ) {
1916 if hovered_hint_part.tooltip.is_some() {
1917 dbg!(&hovered_hint_part.tooltip); // TODO kb
1918 // hover_at_point = Some(hovered_offset);
1919 }
1920 if let Some(location) = hovered_hint_part.location {
1921 if let Some(buffer) = cached_hint
1922 .position
1923 .buffer_id
1924 .and_then(|buffer_id| buffer.buffer(buffer_id))
1925 {
1926 go_to_definition_updated = true;
1927 update_go_to_definition_link(
1928 editor,
1929 GoToDefinitionTrigger::InlayHint(
1930 hovered_hint.position,
1931 LocationLink {
1932 origin: Some(Location {
1933 buffer,
1934 range: cached_hint.position
1935 ..cached_hint.position,
1936 }),
1937 target: location,
1938 },
1939 ),
1940 cmd_held,
1941 shift_held,
1942 cx,
1943 );
1944 }
1945 }
1946 }
1947 }
1948 };
1949 }
1950 ResolveState::Resolving => {}
1951 }
1952 }
1953 }
1954
1955 if !go_to_definition_updated {
1956 update_go_to_definition_link(
1957 editor,
1958 GoToDefinitionTrigger::None,
1959 cmd_held,
1960 shift_held,
1961 cx,
1962 );
1963 }
1964 if !hover_updated {
1965 hover_at(editor, None, cx);
1966 }
1967 }
1968}
1969
1970fn find_hovered_hint_part(
1971 label_parts: Vec<InlayHintLabelPart>,
1972 hint_range: Range<InlayOffset>,
1973 hovered_offset: InlayOffset,
1974) -> Option<InlayHintLabelPart> {
1975 if hovered_offset >= hint_range.start && hovered_offset <= hint_range.end {
1976 let mut hovered_character = (hovered_offset - hint_range.start).0;
1977 for part in label_parts {
1978 let part_len = part.value.chars().count();
1979 if hovered_character >= part_len {
1980 hovered_character -= part_len;
1981 } else {
1982 return Some(part);
1983 }
1984 }
1985 }
1986 None
1987}
1988
1989struct HighlightedChunk<'a> {
1990 chunk: &'a str,
1991 style: Option<HighlightStyle>,
1992 is_tab: bool,
1993}
1994
1995#[derive(Debug)]
1996pub struct LineWithInvisibles {
1997 pub line: Line,
1998 invisibles: Vec<Invisible>,
1999}
2000
2001impl LineWithInvisibles {
2002 fn from_chunks<'a>(
2003 chunks: impl Iterator<Item = HighlightedChunk<'a>>,
2004 text_style: &TextStyle,
2005 text_layout_cache: &TextLayoutCache,
2006 font_cache: &Arc<FontCache>,
2007 max_line_len: usize,
2008 max_line_count: usize,
2009 line_number_layouts: &[Option<Line>],
2010 editor_mode: EditorMode,
2011 ) -> Vec<Self> {
2012 let mut layouts = Vec::with_capacity(max_line_count);
2013 let mut line = String::new();
2014 let mut invisibles = Vec::new();
2015 let mut styles = Vec::new();
2016 let mut non_whitespace_added = false;
2017 let mut row = 0;
2018 let mut line_exceeded_max_len = false;
2019 for highlighted_chunk in chunks.chain([HighlightedChunk {
2020 chunk: "\n",
2021 style: None,
2022 is_tab: false,
2023 }]) {
2024 for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
2025 if ix > 0 {
2026 layouts.push(Self {
2027 line: text_layout_cache.layout_str(&line, text_style.font_size, &styles),
2028 invisibles: invisibles.drain(..).collect(),
2029 });
2030
2031 line.clear();
2032 styles.clear();
2033 row += 1;
2034 line_exceeded_max_len = false;
2035 non_whitespace_added = false;
2036 if row == max_line_count {
2037 return layouts;
2038 }
2039 }
2040
2041 if !line_chunk.is_empty() && !line_exceeded_max_len {
2042 let text_style = if let Some(style) = highlighted_chunk.style {
2043 text_style
2044 .clone()
2045 .highlight(style, font_cache)
2046 .map(Cow::Owned)
2047 .unwrap_or_else(|_| Cow::Borrowed(text_style))
2048 } else {
2049 Cow::Borrowed(text_style)
2050 };
2051
2052 if line.len() + line_chunk.len() > max_line_len {
2053 let mut chunk_len = max_line_len - line.len();
2054 while !line_chunk.is_char_boundary(chunk_len) {
2055 chunk_len -= 1;
2056 }
2057 line_chunk = &line_chunk[..chunk_len];
2058 line_exceeded_max_len = true;
2059 }
2060
2061 styles.push((
2062 line_chunk.len(),
2063 RunStyle {
2064 font_id: text_style.font_id,
2065 color: text_style.color,
2066 underline: text_style.underline,
2067 },
2068 ));
2069
2070 if editor_mode == EditorMode::Full {
2071 // Line wrap pads its contents with fake whitespaces,
2072 // avoid printing them
2073 let inside_wrapped_string = line_number_layouts
2074 .get(row)
2075 .and_then(|layout| layout.as_ref())
2076 .is_none();
2077 if highlighted_chunk.is_tab {
2078 if non_whitespace_added || !inside_wrapped_string {
2079 invisibles.push(Invisible::Tab {
2080 line_start_offset: line.len(),
2081 });
2082 }
2083 } else {
2084 invisibles.extend(
2085 line_chunk
2086 .chars()
2087 .enumerate()
2088 .filter(|(_, line_char)| {
2089 let is_whitespace = line_char.is_whitespace();
2090 non_whitespace_added |= !is_whitespace;
2091 is_whitespace
2092 && (non_whitespace_added || !inside_wrapped_string)
2093 })
2094 .map(|(whitespace_index, _)| Invisible::Whitespace {
2095 line_offset: line.len() + whitespace_index,
2096 }),
2097 )
2098 }
2099 }
2100
2101 line.push_str(line_chunk);
2102 }
2103 }
2104 }
2105
2106 layouts
2107 }
2108
2109 fn draw(
2110 &self,
2111 layout: &LayoutState,
2112 row: u32,
2113 scroll_top: f32,
2114 scene: &mut SceneBuilder,
2115 content_origin: Vector2F,
2116 scroll_left: f32,
2117 visible_text_bounds: RectF,
2118 whitespace_setting: ShowWhitespaceSetting,
2119 selection_ranges: &[Range<DisplayPoint>],
2120 visible_bounds: RectF,
2121 cx: &mut ViewContext<Editor>,
2122 ) {
2123 let line_height = layout.position_map.line_height;
2124 let line_y = row as f32 * line_height - scroll_top;
2125
2126 self.line.paint(
2127 scene,
2128 content_origin + vec2f(-scroll_left, line_y),
2129 visible_text_bounds,
2130 line_height,
2131 cx,
2132 );
2133
2134 self.draw_invisibles(
2135 &selection_ranges,
2136 layout,
2137 content_origin,
2138 scroll_left,
2139 line_y,
2140 row,
2141 scene,
2142 visible_bounds,
2143 line_height,
2144 whitespace_setting,
2145 cx,
2146 );
2147 }
2148
2149 fn draw_invisibles(
2150 &self,
2151 selection_ranges: &[Range<DisplayPoint>],
2152 layout: &LayoutState,
2153 content_origin: Vector2F,
2154 scroll_left: f32,
2155 line_y: f32,
2156 row: u32,
2157 scene: &mut SceneBuilder,
2158 visible_bounds: RectF,
2159 line_height: f32,
2160 whitespace_setting: ShowWhitespaceSetting,
2161 cx: &mut ViewContext<Editor>,
2162 ) {
2163 let allowed_invisibles_regions = match whitespace_setting {
2164 ShowWhitespaceSetting::None => return,
2165 ShowWhitespaceSetting::Selection => Some(selection_ranges),
2166 ShowWhitespaceSetting::All => None,
2167 };
2168
2169 for invisible in &self.invisibles {
2170 let (&token_offset, invisible_symbol) = match invisible {
2171 Invisible::Tab { line_start_offset } => (line_start_offset, &layout.tab_invisible),
2172 Invisible::Whitespace { line_offset } => (line_offset, &layout.space_invisible),
2173 };
2174
2175 let x_offset = self.line.x_for_index(token_offset);
2176 let invisible_offset =
2177 (layout.position_map.em_width - invisible_symbol.width()).max(0.0) / 2.0;
2178 let origin = content_origin + vec2f(-scroll_left + x_offset + invisible_offset, line_y);
2179
2180 if let Some(allowed_regions) = allowed_invisibles_regions {
2181 let invisible_point = DisplayPoint::new(row, token_offset as u32);
2182 if !allowed_regions
2183 .iter()
2184 .any(|region| region.start <= invisible_point && invisible_point < region.end)
2185 {
2186 continue;
2187 }
2188 }
2189 invisible_symbol.paint(scene, origin, visible_bounds, line_height, cx);
2190 }
2191 }
2192}
2193
2194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2195enum Invisible {
2196 Tab { line_start_offset: usize },
2197 Whitespace { line_offset: usize },
2198}
2199
2200impl Element<Editor> for EditorElement {
2201 type LayoutState = LayoutState;
2202 type PaintState = ();
2203
2204 fn layout(
2205 &mut self,
2206 constraint: SizeConstraint,
2207 editor: &mut Editor,
2208 cx: &mut LayoutContext<Editor>,
2209 ) -> (Vector2F, Self::LayoutState) {
2210 let mut size = constraint.max;
2211 if size.x().is_infinite() {
2212 unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
2213 }
2214
2215 let snapshot = editor.snapshot(cx);
2216 let style = self.style.clone();
2217
2218 let line_height = (style.text.font_size * style.line_height_scalar).round();
2219
2220 let gutter_padding;
2221 let gutter_width;
2222 let gutter_margin;
2223 if snapshot.show_gutter {
2224 let em_width = style.text.em_width(cx.font_cache());
2225 gutter_padding = (em_width * style.gutter_padding_factor).round();
2226 gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
2227 gutter_margin = -style.text.descent(cx.font_cache());
2228 } else {
2229 gutter_padding = 0.0;
2230 gutter_width = 0.0;
2231 gutter_margin = 0.0;
2232 };
2233
2234 let text_width = size.x() - gutter_width;
2235 let em_width = style.text.em_width(cx.font_cache());
2236 let em_advance = style.text.em_advance(cx.font_cache());
2237 let overscroll = vec2f(em_width, 0.);
2238 let snapshot = {
2239 editor.set_visible_line_count(size.y() / line_height, cx);
2240
2241 let editor_width = text_width - gutter_margin - overscroll.x() - em_width;
2242 let wrap_width = match editor.soft_wrap_mode(cx) {
2243 SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
2244 SoftWrap::EditorWidth => editor_width,
2245 SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
2246 };
2247
2248 if editor.set_wrap_width(Some(wrap_width), cx) {
2249 editor.snapshot(cx)
2250 } else {
2251 snapshot
2252 }
2253 };
2254
2255 let wrap_guides = editor
2256 .wrap_guides(cx)
2257 .iter()
2258 .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
2259 .collect();
2260
2261 let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
2262 if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
2263 size.set_y(
2264 scroll_height
2265 .min(constraint.max_along(Axis::Vertical))
2266 .max(constraint.min_along(Axis::Vertical))
2267 .min(line_height * max_lines as f32),
2268 )
2269 } else if let EditorMode::SingleLine = snapshot.mode {
2270 size.set_y(
2271 line_height
2272 .min(constraint.max_along(Axis::Vertical))
2273 .max(constraint.min_along(Axis::Vertical)),
2274 )
2275 } else if size.y().is_infinite() {
2276 size.set_y(scroll_height);
2277 }
2278 let gutter_size = vec2f(gutter_width, size.y());
2279 let text_size = vec2f(text_width, size.y());
2280
2281 let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), line_height, cx);
2282 let mut snapshot = editor.snapshot(cx);
2283
2284 let scroll_position = snapshot.scroll_position();
2285 // The scroll position is a fractional point, the whole number of which represents
2286 // the top of the window in terms of display rows.
2287 let start_row = scroll_position.y() as u32;
2288 let height_in_lines = size.y() / line_height;
2289 let max_row = snapshot.max_point().row();
2290
2291 // Add 1 to ensure selections bleed off screen
2292 let end_row = 1 + cmp::min(
2293 (scroll_position.y() + height_in_lines).ceil() as u32,
2294 max_row,
2295 );
2296
2297 let start_anchor = if start_row == 0 {
2298 Anchor::min()
2299 } else {
2300 snapshot
2301 .buffer_snapshot
2302 .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
2303 };
2304 let end_anchor = if end_row > max_row {
2305 Anchor::max()
2306 } else {
2307 snapshot
2308 .buffer_snapshot
2309 .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
2310 };
2311
2312 let mut selections: Vec<(Option<ReplicaId>, Vec<SelectionLayout>)> = Vec::new();
2313 let mut active_rows = BTreeMap::new();
2314 let mut fold_ranges = Vec::new();
2315 let is_singleton = editor.is_singleton(cx);
2316
2317 let highlighted_rows = editor.highlighted_rows();
2318 let theme = theme::current(cx);
2319 let highlighted_ranges = editor.background_highlights_in_range(
2320 start_anchor..end_anchor,
2321 &snapshot.display_snapshot,
2322 theme.as_ref(),
2323 );
2324
2325 fold_ranges.extend(
2326 snapshot
2327 .folds_in_range(start_anchor..end_anchor)
2328 .map(|anchor| {
2329 let start = anchor.start.to_point(&snapshot.buffer_snapshot);
2330 (
2331 start.row,
2332 start.to_display_point(&snapshot.display_snapshot)
2333 ..anchor.end.to_display_point(&snapshot),
2334 )
2335 }),
2336 );
2337
2338 let mut remote_selections = HashMap::default();
2339 for (replica_id, line_mode, cursor_shape, selection) in snapshot
2340 .buffer_snapshot
2341 .remote_selections_in_range(&(start_anchor..end_anchor))
2342 {
2343 let replica_id = if let Some(mapping) = &editor.replica_id_mapping {
2344 mapping.get(&replica_id).copied()
2345 } else {
2346 None
2347 };
2348
2349 // The local selections match the leader's selections.
2350 if replica_id.is_some() && replica_id == editor.leader_replica_id {
2351 continue;
2352 }
2353 remote_selections
2354 .entry(replica_id)
2355 .or_insert(Vec::new())
2356 .push(SelectionLayout::new(
2357 selection,
2358 line_mode,
2359 cursor_shape,
2360 &snapshot.display_snapshot,
2361 false,
2362 false,
2363 ));
2364 }
2365 selections.extend(remote_selections);
2366
2367 let mut newest_selection_head = None;
2368
2369 if editor.show_local_selections {
2370 let mut local_selections: Vec<Selection<Point>> = editor
2371 .selections
2372 .disjoint_in_range(start_anchor..end_anchor, cx);
2373 local_selections.extend(editor.selections.pending(cx));
2374 let mut layouts = Vec::new();
2375 let newest = editor.selections.newest(cx);
2376 for selection in local_selections.drain(..) {
2377 let is_empty = selection.start == selection.end;
2378 let is_newest = selection == newest;
2379
2380 let layout = SelectionLayout::new(
2381 selection,
2382 editor.selections.line_mode,
2383 editor.cursor_shape,
2384 &snapshot.display_snapshot,
2385 is_newest,
2386 true,
2387 );
2388 if is_newest {
2389 newest_selection_head = Some(layout.head);
2390 }
2391
2392 for row in cmp::max(layout.active_rows.start, start_row)
2393 ..=cmp::min(layout.active_rows.end, end_row)
2394 {
2395 let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
2396 *contains_non_empty_selection |= !is_empty;
2397 }
2398 layouts.push(layout);
2399 }
2400
2401 // Render the local selections in the leader's color when following.
2402 let local_replica_id = if let Some(leader_replica_id) = editor.leader_replica_id {
2403 leader_replica_id
2404 } else {
2405 let replica_id = editor.replica_id(cx);
2406 if let Some(mapping) = &editor.replica_id_mapping {
2407 mapping.get(&replica_id).copied().unwrap_or(replica_id)
2408 } else {
2409 replica_id
2410 }
2411 };
2412
2413 selections.push((Some(local_replica_id), layouts));
2414 }
2415
2416 let scrollbar_settings = &settings::get::<EditorSettings>(cx).scrollbar;
2417 let show_scrollbars = match scrollbar_settings.show {
2418 ShowScrollbar::Auto => {
2419 // Git
2420 (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
2421 ||
2422 // Selections
2423 (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
2424 // Scrollmanager
2425 || editor.scroll_manager.scrollbars_visible()
2426 }
2427 ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
2428 ShowScrollbar::Always => true,
2429 ShowScrollbar::Never => false,
2430 };
2431
2432 let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)> = fold_ranges
2433 .into_iter()
2434 .map(|(id, fold)| {
2435 let color = self
2436 .style
2437 .folds
2438 .ellipses
2439 .background
2440 .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
2441 .color;
2442
2443 (id, fold, color)
2444 })
2445 .collect();
2446
2447 let (line_number_layouts, fold_statuses) = self.layout_line_numbers(
2448 start_row..end_row,
2449 &active_rows,
2450 is_singleton,
2451 &snapshot,
2452 cx,
2453 );
2454
2455 let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
2456
2457 let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines);
2458
2459 let mut max_visible_line_width = 0.0;
2460 let line_layouts =
2461 self.layout_lines(start_row..end_row, &line_number_layouts, &snapshot, cx);
2462 for line_with_invisibles in &line_layouts {
2463 if line_with_invisibles.line.width() > max_visible_line_width {
2464 max_visible_line_width = line_with_invisibles.line.width();
2465 }
2466 }
2467
2468 let style = self.style.clone();
2469 let longest_line_width = layout_line(
2470 snapshot.longest_row(),
2471 &snapshot,
2472 &style,
2473 cx.text_layout_cache(),
2474 )
2475 .width();
2476 let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x();
2477 let em_width = style.text.em_width(cx.font_cache());
2478 let (scroll_width, blocks) = self.layout_blocks(
2479 start_row..end_row,
2480 &snapshot,
2481 size.x(),
2482 scroll_width,
2483 gutter_padding,
2484 gutter_width,
2485 em_width,
2486 gutter_width + gutter_margin,
2487 line_height,
2488 &style,
2489 &line_layouts,
2490 editor,
2491 cx,
2492 );
2493
2494 let scroll_max = vec2f(
2495 ((scroll_width - text_size.x()) / em_width).max(0.0),
2496 max_row as f32,
2497 );
2498
2499 let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x());
2500
2501 let autoscrolled = if autoscroll_horizontally {
2502 editor.autoscroll_horizontally(
2503 start_row,
2504 text_size.x(),
2505 scroll_width,
2506 em_width,
2507 &line_layouts,
2508 cx,
2509 )
2510 } else {
2511 false
2512 };
2513
2514 if clamped || autoscrolled {
2515 snapshot = editor.snapshot(cx);
2516 }
2517
2518 let style = editor.style(cx);
2519
2520 let mut context_menu = None;
2521 let mut code_actions_indicator = None;
2522 if let Some(newest_selection_head) = newest_selection_head {
2523 if (start_row..end_row).contains(&newest_selection_head.row()) {
2524 if editor.context_menu_visible() {
2525 context_menu =
2526 editor.render_context_menu(newest_selection_head, style.clone(), cx);
2527 }
2528
2529 let active = matches!(
2530 editor.context_menu,
2531 Some(crate::ContextMenu::CodeActions(_))
2532 );
2533
2534 code_actions_indicator = editor
2535 .render_code_actions_indicator(&style, active, cx)
2536 .map(|indicator| (newest_selection_head.row(), indicator));
2537 }
2538 }
2539
2540 let visible_rows = start_row..start_row + line_layouts.len() as u32;
2541 let mut hover = editor
2542 .hover_state
2543 .render(&snapshot, &style, visible_rows, cx);
2544 let mode = editor.mode;
2545
2546 let mut fold_indicators = editor.render_fold_indicators(
2547 fold_statuses,
2548 &style,
2549 editor.gutter_hovered,
2550 line_height,
2551 gutter_margin,
2552 cx,
2553 );
2554
2555 if let Some((_, context_menu)) = context_menu.as_mut() {
2556 context_menu.layout(
2557 SizeConstraint {
2558 min: Vector2F::zero(),
2559 max: vec2f(
2560 cx.window_size().x() * 0.7,
2561 (12. * line_height).min((size.y() - line_height) / 2.),
2562 ),
2563 },
2564 editor,
2565 cx,
2566 );
2567 }
2568
2569 if let Some((_, indicator)) = code_actions_indicator.as_mut() {
2570 indicator.layout(
2571 SizeConstraint::strict_along(
2572 Axis::Vertical,
2573 line_height * style.code_actions.vertical_scale,
2574 ),
2575 editor,
2576 cx,
2577 );
2578 }
2579
2580 for fold_indicator in fold_indicators.iter_mut() {
2581 if let Some(indicator) = fold_indicator.as_mut() {
2582 indicator.layout(
2583 SizeConstraint::strict_along(
2584 Axis::Vertical,
2585 line_height * style.code_actions.vertical_scale,
2586 ),
2587 editor,
2588 cx,
2589 );
2590 }
2591 }
2592
2593 if let Some((_, hover_popovers)) = hover.as_mut() {
2594 for hover_popover in hover_popovers.iter_mut() {
2595 hover_popover.layout(
2596 SizeConstraint {
2597 min: Vector2F::zero(),
2598 max: vec2f(
2599 (120. * em_width) // Default size
2600 .min(size.x() / 2.) // Shrink to half of the editor width
2601 .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
2602 (16. * line_height) // Default size
2603 .min(size.y() / 2.) // Shrink to half of the editor height
2604 .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
2605 ),
2606 },
2607 editor,
2608 cx,
2609 );
2610 }
2611 }
2612
2613 let invisible_symbol_font_size = self.style.text.font_size / 2.0;
2614 let invisible_symbol_style = RunStyle {
2615 color: self.style.whitespace,
2616 font_id: self.style.text.font_id,
2617 underline: Default::default(),
2618 };
2619
2620 (
2621 size,
2622 LayoutState {
2623 mode,
2624 position_map: Arc::new(PositionMap {
2625 size,
2626 scroll_max,
2627 line_layouts,
2628 line_height,
2629 em_width,
2630 em_advance,
2631 snapshot,
2632 }),
2633 visible_display_row_range: start_row..end_row,
2634 wrap_guides,
2635 gutter_size,
2636 gutter_padding,
2637 text_size,
2638 scrollbar_row_range,
2639 show_scrollbars,
2640 is_singleton,
2641 max_row,
2642 gutter_margin,
2643 active_rows,
2644 highlighted_rows,
2645 highlighted_ranges,
2646 fold_ranges,
2647 line_number_layouts,
2648 display_hunks,
2649 blocks,
2650 selections,
2651 context_menu,
2652 code_actions_indicator,
2653 fold_indicators,
2654 tab_invisible: cx.text_layout_cache().layout_str(
2655 "→",
2656 invisible_symbol_font_size,
2657 &[("→".len(), invisible_symbol_style)],
2658 ),
2659 space_invisible: cx.text_layout_cache().layout_str(
2660 "•",
2661 invisible_symbol_font_size,
2662 &[("•".len(), invisible_symbol_style)],
2663 ),
2664 hover_popovers: hover,
2665 },
2666 )
2667 }
2668
2669 fn paint(
2670 &mut self,
2671 scene: &mut SceneBuilder,
2672 bounds: RectF,
2673 visible_bounds: RectF,
2674 layout: &mut Self::LayoutState,
2675 editor: &mut Editor,
2676 cx: &mut PaintContext<Editor>,
2677 ) -> Self::PaintState {
2678 let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
2679 scene.push_layer(Some(visible_bounds));
2680
2681 let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
2682 let text_bounds = RectF::new(
2683 bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2684 layout.text_size,
2685 );
2686
2687 Self::attach_mouse_handlers(
2688 scene,
2689 &layout.position_map,
2690 layout.hover_popovers.is_some(),
2691 visible_bounds,
2692 text_bounds,
2693 gutter_bounds,
2694 bounds,
2695 cx,
2696 );
2697
2698 self.paint_background(scene, gutter_bounds, text_bounds, layout);
2699 if layout.gutter_size.x() > 0. {
2700 self.paint_gutter(scene, gutter_bounds, visible_bounds, layout, editor, cx);
2701 }
2702 self.paint_text(scene, text_bounds, visible_bounds, layout, editor, cx);
2703
2704 scene.push_layer(Some(bounds));
2705 if !layout.blocks.is_empty() {
2706 self.paint_blocks(scene, bounds, visible_bounds, layout, editor, cx);
2707 }
2708 self.paint_scrollbar(scene, bounds, layout, cx, &editor);
2709 scene.pop_layer();
2710
2711 scene.pop_layer();
2712 }
2713
2714 fn rect_for_text_range(
2715 &self,
2716 range_utf16: Range<usize>,
2717 bounds: RectF,
2718 _: RectF,
2719 layout: &Self::LayoutState,
2720 _: &Self::PaintState,
2721 _: &Editor,
2722 _: &ViewContext<Editor>,
2723 ) -> Option<RectF> {
2724 let text_bounds = RectF::new(
2725 bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2726 layout.text_size,
2727 );
2728 let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.);
2729 let scroll_position = layout.position_map.snapshot.scroll_position();
2730 let start_row = scroll_position.y() as u32;
2731 let scroll_top = scroll_position.y() * layout.position_map.line_height;
2732 let scroll_left = scroll_position.x() * layout.position_map.em_width;
2733
2734 let range_start = OffsetUtf16(range_utf16.start)
2735 .to_display_point(&layout.position_map.snapshot.display_snapshot);
2736 if range_start.row() < start_row {
2737 return None;
2738 }
2739
2740 let line = &layout
2741 .position_map
2742 .line_layouts
2743 .get((range_start.row() - start_row) as usize)?
2744 .line;
2745 let range_start_x = line.x_for_index(range_start.column() as usize);
2746 let range_start_y = range_start.row() as f32 * layout.position_map.line_height;
2747 Some(RectF::new(
2748 content_origin
2749 + vec2f(
2750 range_start_x,
2751 range_start_y + layout.position_map.line_height,
2752 )
2753 - vec2f(scroll_left, scroll_top),
2754 vec2f(
2755 layout.position_map.em_width,
2756 layout.position_map.line_height,
2757 ),
2758 ))
2759 }
2760
2761 fn debug(
2762 &self,
2763 bounds: RectF,
2764 _: &Self::LayoutState,
2765 _: &Self::PaintState,
2766 _: &Editor,
2767 _: &ViewContext<Editor>,
2768 ) -> json::Value {
2769 json!({
2770 "type": "BufferElement",
2771 "bounds": bounds.to_json()
2772 })
2773 }
2774}
2775
2776type BufferRow = u32;
2777
2778pub struct LayoutState {
2779 position_map: Arc<PositionMap>,
2780 gutter_size: Vector2F,
2781 gutter_padding: f32,
2782 gutter_margin: f32,
2783 text_size: Vector2F,
2784 mode: EditorMode,
2785 wrap_guides: SmallVec<[(f32, bool); 2]>,
2786 visible_display_row_range: Range<u32>,
2787 active_rows: BTreeMap<u32, bool>,
2788 highlighted_rows: Option<Range<u32>>,
2789 line_number_layouts: Vec<Option<text_layout::Line>>,
2790 display_hunks: Vec<DisplayDiffHunk>,
2791 blocks: Vec<BlockLayout>,
2792 highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
2793 fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)>,
2794 selections: Vec<(Option<ReplicaId>, Vec<SelectionLayout>)>,
2795 scrollbar_row_range: Range<f32>,
2796 show_scrollbars: bool,
2797 is_singleton: bool,
2798 max_row: u32,
2799 context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
2800 code_actions_indicator: Option<(u32, AnyElement<Editor>)>,
2801 hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
2802 fold_indicators: Vec<Option<AnyElement<Editor>>>,
2803 tab_invisible: Line,
2804 space_invisible: Line,
2805}
2806
2807struct PositionMap {
2808 size: Vector2F,
2809 line_height: f32,
2810 scroll_max: Vector2F,
2811 em_width: f32,
2812 em_advance: f32,
2813 line_layouts: Vec<LineWithInvisibles>,
2814 snapshot: EditorSnapshot,
2815}
2816
2817#[derive(Debug)]
2818pub struct PointForPosition {
2819 previous_valid: DisplayPoint,
2820 pub next_valid: DisplayPoint,
2821 exact_unclipped: DisplayPoint,
2822 column_overshoot_after_line_end: u32,
2823}
2824
2825impl PointForPosition {
2826 #[cfg(test)]
2827 pub fn valid(valid: DisplayPoint) -> Self {
2828 Self {
2829 previous_valid: valid,
2830 next_valid: valid,
2831 exact_unclipped: valid,
2832 column_overshoot_after_line_end: 0,
2833 }
2834 }
2835
2836 fn as_valid(&self) -> Option<DisplayPoint> {
2837 if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
2838 Some(self.previous_valid)
2839 } else {
2840 None
2841 }
2842 }
2843}
2844
2845impl PositionMap {
2846 fn point_for_position(&self, text_bounds: RectF, position: Vector2F) -> PointForPosition {
2847 let scroll_position = self.snapshot.scroll_position();
2848 let position = position - text_bounds.origin();
2849 let y = position.y().max(0.0).min(self.size.y());
2850 let x = position.x() + (scroll_position.x() * self.em_width);
2851 let row = (y / self.line_height + scroll_position.y()) as u32;
2852 let (column, x_overshoot_after_line_end) = if let Some(line) = self
2853 .line_layouts
2854 .get(row as usize - scroll_position.y() as usize)
2855 .map(|line_with_spaces| &line_with_spaces.line)
2856 {
2857 if let Some(ix) = line.index_for_x(x) {
2858 (ix as u32, 0.0)
2859 } else {
2860 (line.len() as u32, 0f32.max(x - line.width()))
2861 }
2862 } else {
2863 (0, x)
2864 };
2865
2866 let mut exact_unclipped = DisplayPoint::new(row, column);
2867 let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
2868 let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
2869
2870 let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
2871 *exact_unclipped.column_mut() += column_overshoot_after_line_end;
2872 PointForPosition {
2873 previous_valid,
2874 next_valid,
2875 exact_unclipped,
2876 column_overshoot_after_line_end,
2877 }
2878 }
2879}
2880
2881struct BlockLayout {
2882 row: u32,
2883 element: AnyElement<Editor>,
2884 style: BlockStyle,
2885}
2886
2887fn layout_line(
2888 row: u32,
2889 snapshot: &EditorSnapshot,
2890 style: &EditorStyle,
2891 layout_cache: &TextLayoutCache,
2892) -> text_layout::Line {
2893 let mut line = snapshot.line(row);
2894
2895 if line.len() > MAX_LINE_LEN {
2896 let mut len = MAX_LINE_LEN;
2897 while !line.is_char_boundary(len) {
2898 len -= 1;
2899 }
2900
2901 line.truncate(len);
2902 }
2903
2904 layout_cache.layout_str(
2905 &line,
2906 style.text.font_size,
2907 &[(
2908 snapshot.line_len(row) as usize,
2909 RunStyle {
2910 font_id: style.text.font_id,
2911 color: Color::black(),
2912 underline: Default::default(),
2913 },
2914 )],
2915 )
2916}
2917
2918#[derive(Debug)]
2919pub struct Cursor {
2920 origin: Vector2F,
2921 block_width: f32,
2922 line_height: f32,
2923 color: Color,
2924 shape: CursorShape,
2925 block_text: Option<Line>,
2926}
2927
2928impl Cursor {
2929 pub fn new(
2930 origin: Vector2F,
2931 block_width: f32,
2932 line_height: f32,
2933 color: Color,
2934 shape: CursorShape,
2935 block_text: Option<Line>,
2936 ) -> Cursor {
2937 Cursor {
2938 origin,
2939 block_width,
2940 line_height,
2941 color,
2942 shape,
2943 block_text,
2944 }
2945 }
2946
2947 pub fn bounding_rect(&self, origin: Vector2F) -> RectF {
2948 RectF::new(
2949 self.origin + origin,
2950 vec2f(self.block_width, self.line_height),
2951 )
2952 }
2953
2954 pub fn paint(&self, scene: &mut SceneBuilder, origin: Vector2F, cx: &mut WindowContext) {
2955 let bounds = match self.shape {
2956 CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
2957 CursorShape::Block | CursorShape::Hollow => RectF::new(
2958 self.origin + origin,
2959 vec2f(self.block_width, self.line_height),
2960 ),
2961 CursorShape::Underscore => RectF::new(
2962 self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
2963 vec2f(self.block_width, 2.0),
2964 ),
2965 };
2966
2967 //Draw background or border quad
2968 if matches!(self.shape, CursorShape::Hollow) {
2969 scene.push_quad(Quad {
2970 bounds,
2971 background: None,
2972 border: Border::all(1., self.color),
2973 corner_radii: Default::default(),
2974 });
2975 } else {
2976 scene.push_quad(Quad {
2977 bounds,
2978 background: Some(self.color),
2979 border: Default::default(),
2980 corner_radii: Default::default(),
2981 });
2982 }
2983
2984 if let Some(block_text) = &self.block_text {
2985 block_text.paint(scene, self.origin + origin, bounds, self.line_height, cx);
2986 }
2987 }
2988
2989 pub fn shape(&self) -> CursorShape {
2990 self.shape
2991 }
2992}
2993
2994#[derive(Debug)]
2995pub struct HighlightedRange {
2996 pub start_y: f32,
2997 pub line_height: f32,
2998 pub lines: Vec<HighlightedRangeLine>,
2999 pub color: Color,
3000 pub corner_radius: f32,
3001}
3002
3003#[derive(Debug)]
3004pub struct HighlightedRangeLine {
3005 pub start_x: f32,
3006 pub end_x: f32,
3007}
3008
3009impl HighlightedRange {
3010 pub fn paint(&self, bounds: RectF, scene: &mut SceneBuilder) {
3011 if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
3012 self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
3013 self.paint_lines(
3014 self.start_y + self.line_height,
3015 &self.lines[1..],
3016 bounds,
3017 scene,
3018 );
3019 } else {
3020 self.paint_lines(self.start_y, &self.lines, bounds, scene);
3021 }
3022 }
3023
3024 fn paint_lines(
3025 &self,
3026 start_y: f32,
3027 lines: &[HighlightedRangeLine],
3028 bounds: RectF,
3029 scene: &mut SceneBuilder,
3030 ) {
3031 if lines.is_empty() {
3032 return;
3033 }
3034
3035 let mut path = PathBuilder::new();
3036 let first_line = lines.first().unwrap();
3037 let last_line = lines.last().unwrap();
3038
3039 let first_top_left = vec2f(first_line.start_x, start_y);
3040 let first_top_right = vec2f(first_line.end_x, start_y);
3041
3042 let curve_height = vec2f(0., self.corner_radius);
3043 let curve_width = |start_x: f32, end_x: f32| {
3044 let max = (end_x - start_x) / 2.;
3045 let width = if max < self.corner_radius {
3046 max
3047 } else {
3048 self.corner_radius
3049 };
3050
3051 vec2f(width, 0.)
3052 };
3053
3054 let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
3055 path.reset(first_top_right - top_curve_width);
3056 path.curve_to(first_top_right + curve_height, first_top_right);
3057
3058 let mut iter = lines.iter().enumerate().peekable();
3059 while let Some((ix, line)) = iter.next() {
3060 let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
3061
3062 if let Some((_, next_line)) = iter.peek() {
3063 let next_top_right = vec2f(next_line.end_x, bottom_right.y());
3064
3065 match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
3066 Ordering::Equal => {
3067 path.line_to(bottom_right);
3068 }
3069 Ordering::Less => {
3070 let curve_width = curve_width(next_top_right.x(), bottom_right.x());
3071 path.line_to(bottom_right - curve_height);
3072 if self.corner_radius > 0. {
3073 path.curve_to(bottom_right - curve_width, bottom_right);
3074 }
3075 path.line_to(next_top_right + curve_width);
3076 if self.corner_radius > 0. {
3077 path.curve_to(next_top_right + curve_height, next_top_right);
3078 }
3079 }
3080 Ordering::Greater => {
3081 let curve_width = curve_width(bottom_right.x(), next_top_right.x());
3082 path.line_to(bottom_right - curve_height);
3083 if self.corner_radius > 0. {
3084 path.curve_to(bottom_right + curve_width, bottom_right);
3085 }
3086 path.line_to(next_top_right - curve_width);
3087 if self.corner_radius > 0. {
3088 path.curve_to(next_top_right + curve_height, next_top_right);
3089 }
3090 }
3091 }
3092 } else {
3093 let curve_width = curve_width(line.start_x, line.end_x);
3094 path.line_to(bottom_right - curve_height);
3095 if self.corner_radius > 0. {
3096 path.curve_to(bottom_right - curve_width, bottom_right);
3097 }
3098
3099 let bottom_left = vec2f(line.start_x, bottom_right.y());
3100 path.line_to(bottom_left + curve_width);
3101 if self.corner_radius > 0. {
3102 path.curve_to(bottom_left - curve_height, bottom_left);
3103 }
3104 }
3105 }
3106
3107 if first_line.start_x > last_line.start_x {
3108 let curve_width = curve_width(last_line.start_x, first_line.start_x);
3109 let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
3110 path.line_to(second_top_left + curve_height);
3111 if self.corner_radius > 0. {
3112 path.curve_to(second_top_left + curve_width, second_top_left);
3113 }
3114 let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
3115 path.line_to(first_bottom_left - curve_width);
3116 if self.corner_radius > 0. {
3117 path.curve_to(first_bottom_left - curve_height, first_bottom_left);
3118 }
3119 }
3120
3121 path.line_to(first_top_left + curve_height);
3122 if self.corner_radius > 0. {
3123 path.curve_to(first_top_left + top_curve_width, first_top_left);
3124 }
3125 path.line_to(first_top_right - top_curve_width);
3126
3127 scene.push_path(path.build(self.color, Some(bounds)));
3128 }
3129}
3130
3131fn range_to_bounds(
3132 range: &Range<DisplayPoint>,
3133 content_origin: Vector2F,
3134 scroll_left: f32,
3135 scroll_top: f32,
3136 visible_row_range: &Range<u32>,
3137 line_end_overshoot: f32,
3138 position_map: &PositionMap,
3139) -> impl Iterator<Item = RectF> {
3140 let mut bounds: SmallVec<[RectF; 1]> = SmallVec::new();
3141
3142 if range.start == range.end {
3143 return bounds.into_iter();
3144 }
3145
3146 let start_row = visible_row_range.start;
3147 let end_row = visible_row_range.end;
3148
3149 let row_range = if range.end.column() == 0 {
3150 cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
3151 } else {
3152 cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
3153 };
3154
3155 let first_y =
3156 content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top;
3157
3158 for (idx, row) in row_range.enumerate() {
3159 let line_layout = &position_map.line_layouts[(row - start_row) as usize].line;
3160
3161 let start_x = if row == range.start.row() {
3162 content_origin.x() + line_layout.x_for_index(range.start.column() as usize)
3163 - scroll_left
3164 } else {
3165 content_origin.x() - scroll_left
3166 };
3167
3168 let end_x = if row == range.end.row() {
3169 content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left
3170 } else {
3171 content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left
3172 };
3173
3174 bounds.push(RectF::from_points(
3175 vec2f(start_x, first_y + position_map.line_height * idx as f32),
3176 vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32),
3177 ))
3178 }
3179
3180 bounds.into_iter()
3181}
3182
3183pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
3184 delta.powf(1.5) / 100.0
3185}
3186
3187fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
3188 delta.powf(1.2) / 300.0
3189}
3190
3191#[cfg(test)]
3192mod tests {
3193 use super::*;
3194 use crate::{
3195 display_map::{BlockDisposition, BlockProperties},
3196 editor_tests::{init_test, update_test_language_settings},
3197 Editor, MultiBuffer,
3198 };
3199 use gpui::TestAppContext;
3200 use language::language_settings;
3201 use log::info;
3202 use std::{num::NonZeroU32, sync::Arc};
3203 use util::test::sample_text;
3204
3205 #[gpui::test]
3206 fn test_layout_line_numbers(cx: &mut TestAppContext) {
3207 init_test(cx, |_| {});
3208
3209 let editor = cx
3210 .add_window(|cx| {
3211 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
3212 Editor::new(EditorMode::Full, buffer, None, None, cx)
3213 })
3214 .root(cx);
3215 let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3216
3217 let layouts = editor.update(cx, |editor, cx| {
3218 let snapshot = editor.snapshot(cx);
3219 element
3220 .layout_line_numbers(0..6, &Default::default(), false, &snapshot, cx)
3221 .0
3222 });
3223 assert_eq!(layouts.len(), 6);
3224 }
3225
3226 #[gpui::test]
3227 async fn test_vim_visual_selections(cx: &mut TestAppContext) {
3228 init_test(cx, |_| {});
3229
3230 let editor = cx
3231 .add_window(|cx| {
3232 let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
3233 Editor::new(EditorMode::Full, buffer, None, None, cx)
3234 })
3235 .root(cx);
3236 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3237 let (_, state) = editor.update(cx, |editor, cx| {
3238 editor.cursor_shape = CursorShape::Block;
3239 editor.change_selections(None, cx, |s| {
3240 s.select_ranges([
3241 Point::new(0, 0)..Point::new(1, 0),
3242 Point::new(3, 2)..Point::new(3, 3),
3243 Point::new(5, 6)..Point::new(6, 0),
3244 ]);
3245 });
3246 let mut new_parents = Default::default();
3247 let mut notify_views_if_parents_change = Default::default();
3248 let mut layout_cx = LayoutContext::new(
3249 cx,
3250 &mut new_parents,
3251 &mut notify_views_if_parents_change,
3252 false,
3253 );
3254 element.layout(
3255 SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3256 editor,
3257 &mut layout_cx,
3258 )
3259 });
3260 assert_eq!(state.selections.len(), 1);
3261 let local_selections = &state.selections[0].1;
3262 assert_eq!(local_selections.len(), 3);
3263 // moves cursor back one line
3264 assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6));
3265 assert_eq!(
3266 local_selections[0].range,
3267 DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0)
3268 );
3269
3270 // moves cursor back one column
3271 assert_eq!(
3272 local_selections[1].range,
3273 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3)
3274 );
3275 assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2));
3276
3277 // leaves cursor on the max point
3278 assert_eq!(
3279 local_selections[2].range,
3280 DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0)
3281 );
3282 assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0));
3283
3284 // active lines does not include 1 (even though the range of the selection does)
3285 assert_eq!(
3286 state.active_rows.keys().cloned().collect::<Vec<u32>>(),
3287 vec![0, 3, 5, 6]
3288 );
3289
3290 // multi-buffer support
3291 // in DisplayPoint co-ordinates, this is what we're dealing with:
3292 // 0: [[file
3293 // 1: header]]
3294 // 2: aaaaaa
3295 // 3: bbbbbb
3296 // 4: cccccc
3297 // 5:
3298 // 6: ...
3299 // 7: ffffff
3300 // 8: gggggg
3301 // 9: hhhhhh
3302 // 10:
3303 // 11: [[file
3304 // 12: header]]
3305 // 13: bbbbbb
3306 // 14: cccccc
3307 // 15: dddddd
3308 let editor = cx
3309 .add_window(|cx| {
3310 let buffer = MultiBuffer::build_multi(
3311 [
3312 (
3313 &(sample_text(8, 6, 'a') + "\n"),
3314 vec![
3315 Point::new(0, 0)..Point::new(3, 0),
3316 Point::new(4, 0)..Point::new(7, 0),
3317 ],
3318 ),
3319 (
3320 &(sample_text(8, 6, 'a') + "\n"),
3321 vec![Point::new(1, 0)..Point::new(3, 0)],
3322 ),
3323 ],
3324 cx,
3325 );
3326 Editor::new(EditorMode::Full, buffer, None, None, cx)
3327 })
3328 .root(cx);
3329 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3330 let (_, state) = editor.update(cx, |editor, cx| {
3331 editor.cursor_shape = CursorShape::Block;
3332 editor.change_selections(None, cx, |s| {
3333 s.select_display_ranges([
3334 DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0),
3335 DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
3336 ]);
3337 });
3338 let mut new_parents = Default::default();
3339 let mut notify_views_if_parents_change = Default::default();
3340 let mut layout_cx = LayoutContext::new(
3341 cx,
3342 &mut new_parents,
3343 &mut notify_views_if_parents_change,
3344 false,
3345 );
3346 element.layout(
3347 SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3348 editor,
3349 &mut layout_cx,
3350 )
3351 });
3352
3353 assert_eq!(state.selections.len(), 1);
3354 let local_selections = &state.selections[0].1;
3355 assert_eq!(local_selections.len(), 2);
3356
3357 // moves cursor on excerpt boundary back a line
3358 // and doesn't allow selection to bleed through
3359 assert_eq!(
3360 local_selections[0].range,
3361 DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0)
3362 );
3363 assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0));
3364
3365 // moves cursor on buffer boundary back two lines
3366 // and doesn't allow selection to bleed through
3367 assert_eq!(
3368 local_selections[1].range,
3369 DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0)
3370 );
3371 assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0));
3372 }
3373
3374 #[gpui::test]
3375 fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
3376 init_test(cx, |_| {});
3377
3378 let editor = cx
3379 .add_window(|cx| {
3380 let buffer = MultiBuffer::build_simple("", cx);
3381 Editor::new(EditorMode::Full, buffer, None, None, cx)
3382 })
3383 .root(cx);
3384
3385 editor.update(cx, |editor, cx| {
3386 editor.set_placeholder_text("hello", cx);
3387 editor.insert_blocks(
3388 [BlockProperties {
3389 style: BlockStyle::Fixed,
3390 disposition: BlockDisposition::Above,
3391 height: 3,
3392 position: Anchor::min(),
3393 render: Arc::new(|_| Empty::new().into_any()),
3394 }],
3395 None,
3396 cx,
3397 );
3398
3399 // Blur the editor so that it displays placeholder text.
3400 cx.blur();
3401 });
3402
3403 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3404 let (size, mut state) = editor.update(cx, |editor, cx| {
3405 let mut new_parents = Default::default();
3406 let mut notify_views_if_parents_change = Default::default();
3407 let mut layout_cx = LayoutContext::new(
3408 cx,
3409 &mut new_parents,
3410 &mut notify_views_if_parents_change,
3411 false,
3412 );
3413 element.layout(
3414 SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3415 editor,
3416 &mut layout_cx,
3417 )
3418 });
3419
3420 assert_eq!(state.position_map.line_layouts.len(), 4);
3421 assert_eq!(
3422 state
3423 .line_number_layouts
3424 .iter()
3425 .map(Option::is_some)
3426 .collect::<Vec<_>>(),
3427 &[false, false, false, true]
3428 );
3429
3430 // Don't panic.
3431 let mut scene = SceneBuilder::new(1.0);
3432 let bounds = RectF::new(Default::default(), size);
3433 editor.update(cx, |editor, cx| {
3434 element.paint(
3435 &mut scene,
3436 bounds,
3437 bounds,
3438 &mut state,
3439 editor,
3440 &mut PaintContext::new(cx),
3441 );
3442 });
3443 }
3444
3445 #[gpui::test]
3446 fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
3447 const TAB_SIZE: u32 = 4;
3448
3449 let input_text = "\t \t|\t| a b";
3450 let expected_invisibles = vec![
3451 Invisible::Tab {
3452 line_start_offset: 0,
3453 },
3454 Invisible::Whitespace {
3455 line_offset: TAB_SIZE as usize,
3456 },
3457 Invisible::Tab {
3458 line_start_offset: TAB_SIZE as usize + 1,
3459 },
3460 Invisible::Tab {
3461 line_start_offset: TAB_SIZE as usize * 2 + 1,
3462 },
3463 Invisible::Whitespace {
3464 line_offset: TAB_SIZE as usize * 3 + 1,
3465 },
3466 Invisible::Whitespace {
3467 line_offset: TAB_SIZE as usize * 3 + 3,
3468 },
3469 ];
3470 assert_eq!(
3471 expected_invisibles.len(),
3472 input_text
3473 .chars()
3474 .filter(|initial_char| initial_char.is_whitespace())
3475 .count(),
3476 "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3477 );
3478
3479 init_test(cx, |s| {
3480 s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3481 s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
3482 });
3483
3484 let actual_invisibles =
3485 collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0);
3486
3487 assert_eq!(expected_invisibles, actual_invisibles);
3488 }
3489
3490 #[gpui::test]
3491 fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
3492 init_test(cx, |s| {
3493 s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3494 s.defaults.tab_size = NonZeroU32::new(4);
3495 });
3496
3497 for editor_mode_without_invisibles in [
3498 EditorMode::SingleLine,
3499 EditorMode::AutoHeight { max_lines: 100 },
3500 ] {
3501 let invisibles = collect_invisibles_from_new_editor(
3502 cx,
3503 editor_mode_without_invisibles,
3504 "\t\t\t| | a b",
3505 500.0,
3506 );
3507 assert!(invisibles.is_empty(),
3508 "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}");
3509 }
3510 }
3511
3512 #[gpui::test]
3513 fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) {
3514 let tab_size = 4;
3515 let input_text = "a\tbcd ".repeat(9);
3516 let repeated_invisibles = [
3517 Invisible::Tab {
3518 line_start_offset: 1,
3519 },
3520 Invisible::Whitespace {
3521 line_offset: tab_size as usize + 3,
3522 },
3523 Invisible::Whitespace {
3524 line_offset: tab_size as usize + 4,
3525 },
3526 Invisible::Whitespace {
3527 line_offset: tab_size as usize + 5,
3528 },
3529 ];
3530 let expected_invisibles = std::iter::once(repeated_invisibles)
3531 .cycle()
3532 .take(9)
3533 .flatten()
3534 .collect::<Vec<_>>();
3535 assert_eq!(
3536 expected_invisibles.len(),
3537 input_text
3538 .chars()
3539 .filter(|initial_char| initial_char.is_whitespace())
3540 .count(),
3541 "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3542 );
3543 info!("Expected invisibles: {expected_invisibles:?}");
3544
3545 init_test(cx, |_| {});
3546
3547 // Put the same string with repeating whitespace pattern into editors of various size,
3548 // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
3549 let resize_step = 10.0;
3550 let mut editor_width = 200.0;
3551 while editor_width <= 1000.0 {
3552 update_test_language_settings(cx, |s| {
3553 s.defaults.tab_size = NonZeroU32::new(tab_size);
3554 s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3555 s.defaults.preferred_line_length = Some(editor_width as u32);
3556 s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
3557 });
3558
3559 let actual_invisibles =
3560 collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, editor_width);
3561
3562 // Whatever the editor size is, ensure it has the same invisible kinds in the same order
3563 // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets).
3564 let mut i = 0;
3565 for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() {
3566 i = actual_index;
3567 match expected_invisibles.get(i) {
3568 Some(expected_invisible) => match (expected_invisible, actual_invisible) {
3569 (Invisible::Whitespace { .. }, Invisible::Whitespace { .. })
3570 | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {}
3571 _ => {
3572 panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}")
3573 }
3574 },
3575 None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"),
3576 }
3577 }
3578 let missing_expected_invisibles = &expected_invisibles[i + 1..];
3579 assert!(
3580 missing_expected_invisibles.is_empty(),
3581 "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}"
3582 );
3583
3584 editor_width += resize_step;
3585 }
3586 }
3587
3588 fn collect_invisibles_from_new_editor(
3589 cx: &mut TestAppContext,
3590 editor_mode: EditorMode,
3591 input_text: &str,
3592 editor_width: f32,
3593 ) -> Vec<Invisible> {
3594 info!(
3595 "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'"
3596 );
3597 let editor = cx
3598 .add_window(|cx| {
3599 let buffer = MultiBuffer::build_simple(&input_text, cx);
3600 Editor::new(editor_mode, buffer, None, None, cx)
3601 })
3602 .root(cx);
3603
3604 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3605 let (_, layout_state) = editor.update(cx, |editor, cx| {
3606 editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
3607 editor.set_wrap_width(Some(editor_width), cx);
3608
3609 let mut new_parents = Default::default();
3610 let mut notify_views_if_parents_change = Default::default();
3611 let mut layout_cx = LayoutContext::new(
3612 cx,
3613 &mut new_parents,
3614 &mut notify_views_if_parents_change,
3615 false,
3616 );
3617 element.layout(
3618 SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)),
3619 editor,
3620 &mut layout_cx,
3621 )
3622 });
3623
3624 layout_state
3625 .position_map
3626 .line_layouts
3627 .iter()
3628 .map(|line_with_invisibles| &line_with_invisibles.invisibles)
3629 .flatten()
3630 .cloned()
3631 .collect()
3632 }
3633}