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, InlayRange,
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, part_range)) =
1912 find_hovered_hint_part(
1913 label_parts,
1914 hint_start_offset..hint_end_offset,
1915 hovered_offset,
1916 )
1917 {
1918 if hovered_hint_part.tooltip.is_some() {
1919 dbg!(&hovered_hint_part.tooltip); // TODO kb
1920 // hover_at_point = Some(hovered_offset);
1921 }
1922 if let Some(location) = hovered_hint_part.location {
1923 if let Some(buffer) = cached_hint
1924 .position
1925 .buffer_id
1926 .and_then(|buffer_id| buffer.buffer(buffer_id))
1927 {
1928 go_to_definition_updated = true;
1929 update_go_to_definition_link(
1930 editor,
1931 GoToDefinitionTrigger::InlayHint(
1932 InlayRange {
1933 inlay_position: hovered_hint.position,
1934 highlight_start: part_range.start,
1935 highlight_end: part_range.end,
1936 },
1937 LocationLink {
1938 origin: Some(Location {
1939 buffer,
1940 range: cached_hint.position
1941 ..cached_hint.position,
1942 }),
1943 target: location,
1944 },
1945 ),
1946 cmd_held,
1947 shift_held,
1948 cx,
1949 );
1950 }
1951 }
1952 }
1953 }
1954 };
1955 }
1956 ResolveState::Resolving => {}
1957 }
1958 }
1959 }
1960
1961 if !go_to_definition_updated {
1962 update_go_to_definition_link(
1963 editor,
1964 GoToDefinitionTrigger::None,
1965 cmd_held,
1966 shift_held,
1967 cx,
1968 );
1969 }
1970 if !hover_updated {
1971 hover_at(editor, None, cx);
1972 }
1973 }
1974}
1975
1976fn find_hovered_hint_part(
1977 label_parts: Vec<InlayHintLabelPart>,
1978 hint_range: Range<InlayOffset>,
1979 hovered_offset: InlayOffset,
1980) -> Option<(InlayHintLabelPart, Range<InlayOffset>)> {
1981 if hovered_offset >= hint_range.start && hovered_offset <= hint_range.end {
1982 let mut hovered_character = (hovered_offset - hint_range.start).0;
1983 let mut part_start = hint_range.start;
1984 for part in label_parts {
1985 let part_len = part.value.chars().count();
1986 if hovered_character >= part_len {
1987 hovered_character -= part_len;
1988 part_start.0 += part_len;
1989 } else {
1990 return Some((part, part_start..InlayOffset(part_start.0 + part_len)));
1991 }
1992 }
1993 }
1994 None
1995}
1996
1997struct HighlightedChunk<'a> {
1998 chunk: &'a str,
1999 style: Option<HighlightStyle>,
2000 is_tab: bool,
2001}
2002
2003#[derive(Debug)]
2004pub struct LineWithInvisibles {
2005 pub line: Line,
2006 invisibles: Vec<Invisible>,
2007}
2008
2009impl LineWithInvisibles {
2010 fn from_chunks<'a>(
2011 chunks: impl Iterator<Item = HighlightedChunk<'a>>,
2012 text_style: &TextStyle,
2013 text_layout_cache: &TextLayoutCache,
2014 font_cache: &Arc<FontCache>,
2015 max_line_len: usize,
2016 max_line_count: usize,
2017 line_number_layouts: &[Option<Line>],
2018 editor_mode: EditorMode,
2019 ) -> Vec<Self> {
2020 let mut layouts = Vec::with_capacity(max_line_count);
2021 let mut line = String::new();
2022 let mut invisibles = Vec::new();
2023 let mut styles = Vec::new();
2024 let mut non_whitespace_added = false;
2025 let mut row = 0;
2026 let mut line_exceeded_max_len = false;
2027 for highlighted_chunk in chunks.chain([HighlightedChunk {
2028 chunk: "\n",
2029 style: None,
2030 is_tab: false,
2031 }]) {
2032 for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
2033 if ix > 0 {
2034 layouts.push(Self {
2035 line: text_layout_cache.layout_str(&line, text_style.font_size, &styles),
2036 invisibles: invisibles.drain(..).collect(),
2037 });
2038
2039 line.clear();
2040 styles.clear();
2041 row += 1;
2042 line_exceeded_max_len = false;
2043 non_whitespace_added = false;
2044 if row == max_line_count {
2045 return layouts;
2046 }
2047 }
2048
2049 if !line_chunk.is_empty() && !line_exceeded_max_len {
2050 let text_style = if let Some(style) = highlighted_chunk.style {
2051 text_style
2052 .clone()
2053 .highlight(style, font_cache)
2054 .map(Cow::Owned)
2055 .unwrap_or_else(|_| Cow::Borrowed(text_style))
2056 } else {
2057 Cow::Borrowed(text_style)
2058 };
2059
2060 if line.len() + line_chunk.len() > max_line_len {
2061 let mut chunk_len = max_line_len - line.len();
2062 while !line_chunk.is_char_boundary(chunk_len) {
2063 chunk_len -= 1;
2064 }
2065 line_chunk = &line_chunk[..chunk_len];
2066 line_exceeded_max_len = true;
2067 }
2068
2069 styles.push((
2070 line_chunk.len(),
2071 RunStyle {
2072 font_id: text_style.font_id,
2073 color: text_style.color,
2074 underline: text_style.underline,
2075 },
2076 ));
2077
2078 if editor_mode == EditorMode::Full {
2079 // Line wrap pads its contents with fake whitespaces,
2080 // avoid printing them
2081 let inside_wrapped_string = line_number_layouts
2082 .get(row)
2083 .and_then(|layout| layout.as_ref())
2084 .is_none();
2085 if highlighted_chunk.is_tab {
2086 if non_whitespace_added || !inside_wrapped_string {
2087 invisibles.push(Invisible::Tab {
2088 line_start_offset: line.len(),
2089 });
2090 }
2091 } else {
2092 invisibles.extend(
2093 line_chunk
2094 .chars()
2095 .enumerate()
2096 .filter(|(_, line_char)| {
2097 let is_whitespace = line_char.is_whitespace();
2098 non_whitespace_added |= !is_whitespace;
2099 is_whitespace
2100 && (non_whitespace_added || !inside_wrapped_string)
2101 })
2102 .map(|(whitespace_index, _)| Invisible::Whitespace {
2103 line_offset: line.len() + whitespace_index,
2104 }),
2105 )
2106 }
2107 }
2108
2109 line.push_str(line_chunk);
2110 }
2111 }
2112 }
2113
2114 layouts
2115 }
2116
2117 fn draw(
2118 &self,
2119 layout: &LayoutState,
2120 row: u32,
2121 scroll_top: f32,
2122 scene: &mut SceneBuilder,
2123 content_origin: Vector2F,
2124 scroll_left: f32,
2125 visible_text_bounds: RectF,
2126 whitespace_setting: ShowWhitespaceSetting,
2127 selection_ranges: &[Range<DisplayPoint>],
2128 visible_bounds: RectF,
2129 cx: &mut ViewContext<Editor>,
2130 ) {
2131 let line_height = layout.position_map.line_height;
2132 let line_y = row as f32 * line_height - scroll_top;
2133
2134 self.line.paint(
2135 scene,
2136 content_origin + vec2f(-scroll_left, line_y),
2137 visible_text_bounds,
2138 line_height,
2139 cx,
2140 );
2141
2142 self.draw_invisibles(
2143 &selection_ranges,
2144 layout,
2145 content_origin,
2146 scroll_left,
2147 line_y,
2148 row,
2149 scene,
2150 visible_bounds,
2151 line_height,
2152 whitespace_setting,
2153 cx,
2154 );
2155 }
2156
2157 fn draw_invisibles(
2158 &self,
2159 selection_ranges: &[Range<DisplayPoint>],
2160 layout: &LayoutState,
2161 content_origin: Vector2F,
2162 scroll_left: f32,
2163 line_y: f32,
2164 row: u32,
2165 scene: &mut SceneBuilder,
2166 visible_bounds: RectF,
2167 line_height: f32,
2168 whitespace_setting: ShowWhitespaceSetting,
2169 cx: &mut ViewContext<Editor>,
2170 ) {
2171 let allowed_invisibles_regions = match whitespace_setting {
2172 ShowWhitespaceSetting::None => return,
2173 ShowWhitespaceSetting::Selection => Some(selection_ranges),
2174 ShowWhitespaceSetting::All => None,
2175 };
2176
2177 for invisible in &self.invisibles {
2178 let (&token_offset, invisible_symbol) = match invisible {
2179 Invisible::Tab { line_start_offset } => (line_start_offset, &layout.tab_invisible),
2180 Invisible::Whitespace { line_offset } => (line_offset, &layout.space_invisible),
2181 };
2182
2183 let x_offset = self.line.x_for_index(token_offset);
2184 let invisible_offset =
2185 (layout.position_map.em_width - invisible_symbol.width()).max(0.0) / 2.0;
2186 let origin = content_origin + vec2f(-scroll_left + x_offset + invisible_offset, line_y);
2187
2188 if let Some(allowed_regions) = allowed_invisibles_regions {
2189 let invisible_point = DisplayPoint::new(row, token_offset as u32);
2190 if !allowed_regions
2191 .iter()
2192 .any(|region| region.start <= invisible_point && invisible_point < region.end)
2193 {
2194 continue;
2195 }
2196 }
2197 invisible_symbol.paint(scene, origin, visible_bounds, line_height, cx);
2198 }
2199 }
2200}
2201
2202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2203enum Invisible {
2204 Tab { line_start_offset: usize },
2205 Whitespace { line_offset: usize },
2206}
2207
2208impl Element<Editor> for EditorElement {
2209 type LayoutState = LayoutState;
2210 type PaintState = ();
2211
2212 fn layout(
2213 &mut self,
2214 constraint: SizeConstraint,
2215 editor: &mut Editor,
2216 cx: &mut LayoutContext<Editor>,
2217 ) -> (Vector2F, Self::LayoutState) {
2218 let mut size = constraint.max;
2219 if size.x().is_infinite() {
2220 unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
2221 }
2222
2223 let snapshot = editor.snapshot(cx);
2224 let style = self.style.clone();
2225
2226 let line_height = (style.text.font_size * style.line_height_scalar).round();
2227
2228 let gutter_padding;
2229 let gutter_width;
2230 let gutter_margin;
2231 if snapshot.show_gutter {
2232 let em_width = style.text.em_width(cx.font_cache());
2233 gutter_padding = (em_width * style.gutter_padding_factor).round();
2234 gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
2235 gutter_margin = -style.text.descent(cx.font_cache());
2236 } else {
2237 gutter_padding = 0.0;
2238 gutter_width = 0.0;
2239 gutter_margin = 0.0;
2240 };
2241
2242 let text_width = size.x() - gutter_width;
2243 let em_width = style.text.em_width(cx.font_cache());
2244 let em_advance = style.text.em_advance(cx.font_cache());
2245 let overscroll = vec2f(em_width, 0.);
2246 let snapshot = {
2247 editor.set_visible_line_count(size.y() / line_height, cx);
2248
2249 let editor_width = text_width - gutter_margin - overscroll.x() - em_width;
2250 let wrap_width = match editor.soft_wrap_mode(cx) {
2251 SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
2252 SoftWrap::EditorWidth => editor_width,
2253 SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
2254 };
2255
2256 if editor.set_wrap_width(Some(wrap_width), cx) {
2257 editor.snapshot(cx)
2258 } else {
2259 snapshot
2260 }
2261 };
2262
2263 let wrap_guides = editor
2264 .wrap_guides(cx)
2265 .iter()
2266 .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
2267 .collect();
2268
2269 let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
2270 if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
2271 size.set_y(
2272 scroll_height
2273 .min(constraint.max_along(Axis::Vertical))
2274 .max(constraint.min_along(Axis::Vertical))
2275 .min(line_height * max_lines as f32),
2276 )
2277 } else if let EditorMode::SingleLine = snapshot.mode {
2278 size.set_y(
2279 line_height
2280 .min(constraint.max_along(Axis::Vertical))
2281 .max(constraint.min_along(Axis::Vertical)),
2282 )
2283 } else if size.y().is_infinite() {
2284 size.set_y(scroll_height);
2285 }
2286 let gutter_size = vec2f(gutter_width, size.y());
2287 let text_size = vec2f(text_width, size.y());
2288
2289 let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), line_height, cx);
2290 let mut snapshot = editor.snapshot(cx);
2291
2292 let scroll_position = snapshot.scroll_position();
2293 // The scroll position is a fractional point, the whole number of which represents
2294 // the top of the window in terms of display rows.
2295 let start_row = scroll_position.y() as u32;
2296 let height_in_lines = size.y() / line_height;
2297 let max_row = snapshot.max_point().row();
2298
2299 // Add 1 to ensure selections bleed off screen
2300 let end_row = 1 + cmp::min(
2301 (scroll_position.y() + height_in_lines).ceil() as u32,
2302 max_row,
2303 );
2304
2305 let start_anchor = if start_row == 0 {
2306 Anchor::min()
2307 } else {
2308 snapshot
2309 .buffer_snapshot
2310 .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
2311 };
2312 let end_anchor = if end_row > max_row {
2313 Anchor::max()
2314 } else {
2315 snapshot
2316 .buffer_snapshot
2317 .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
2318 };
2319
2320 let mut selections: Vec<(Option<ReplicaId>, Vec<SelectionLayout>)> = Vec::new();
2321 let mut active_rows = BTreeMap::new();
2322 let mut fold_ranges = Vec::new();
2323 let is_singleton = editor.is_singleton(cx);
2324
2325 let highlighted_rows = editor.highlighted_rows();
2326 let theme = theme::current(cx);
2327 let highlighted_ranges = editor.background_highlights_in_range(
2328 start_anchor..end_anchor,
2329 &snapshot.display_snapshot,
2330 theme.as_ref(),
2331 );
2332
2333 fold_ranges.extend(
2334 snapshot
2335 .folds_in_range(start_anchor..end_anchor)
2336 .map(|anchor| {
2337 let start = anchor.start.to_point(&snapshot.buffer_snapshot);
2338 (
2339 start.row,
2340 start.to_display_point(&snapshot.display_snapshot)
2341 ..anchor.end.to_display_point(&snapshot),
2342 )
2343 }),
2344 );
2345
2346 let mut remote_selections = HashMap::default();
2347 for (replica_id, line_mode, cursor_shape, selection) in snapshot
2348 .buffer_snapshot
2349 .remote_selections_in_range(&(start_anchor..end_anchor))
2350 {
2351 let replica_id = if let Some(mapping) = &editor.replica_id_mapping {
2352 mapping.get(&replica_id).copied()
2353 } else {
2354 None
2355 };
2356
2357 // The local selections match the leader's selections.
2358 if replica_id.is_some() && replica_id == editor.leader_replica_id {
2359 continue;
2360 }
2361 remote_selections
2362 .entry(replica_id)
2363 .or_insert(Vec::new())
2364 .push(SelectionLayout::new(
2365 selection,
2366 line_mode,
2367 cursor_shape,
2368 &snapshot.display_snapshot,
2369 false,
2370 false,
2371 ));
2372 }
2373 selections.extend(remote_selections);
2374
2375 let mut newest_selection_head = None;
2376
2377 if editor.show_local_selections {
2378 let mut local_selections: Vec<Selection<Point>> = editor
2379 .selections
2380 .disjoint_in_range(start_anchor..end_anchor, cx);
2381 local_selections.extend(editor.selections.pending(cx));
2382 let mut layouts = Vec::new();
2383 let newest = editor.selections.newest(cx);
2384 for selection in local_selections.drain(..) {
2385 let is_empty = selection.start == selection.end;
2386 let is_newest = selection == newest;
2387
2388 let layout = SelectionLayout::new(
2389 selection,
2390 editor.selections.line_mode,
2391 editor.cursor_shape,
2392 &snapshot.display_snapshot,
2393 is_newest,
2394 true,
2395 );
2396 if is_newest {
2397 newest_selection_head = Some(layout.head);
2398 }
2399
2400 for row in cmp::max(layout.active_rows.start, start_row)
2401 ..=cmp::min(layout.active_rows.end, end_row)
2402 {
2403 let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
2404 *contains_non_empty_selection |= !is_empty;
2405 }
2406 layouts.push(layout);
2407 }
2408
2409 // Render the local selections in the leader's color when following.
2410 let local_replica_id = if let Some(leader_replica_id) = editor.leader_replica_id {
2411 leader_replica_id
2412 } else {
2413 let replica_id = editor.replica_id(cx);
2414 if let Some(mapping) = &editor.replica_id_mapping {
2415 mapping.get(&replica_id).copied().unwrap_or(replica_id)
2416 } else {
2417 replica_id
2418 }
2419 };
2420
2421 selections.push((Some(local_replica_id), layouts));
2422 }
2423
2424 let scrollbar_settings = &settings::get::<EditorSettings>(cx).scrollbar;
2425 let show_scrollbars = match scrollbar_settings.show {
2426 ShowScrollbar::Auto => {
2427 // Git
2428 (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
2429 ||
2430 // Selections
2431 (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
2432 // Scrollmanager
2433 || editor.scroll_manager.scrollbars_visible()
2434 }
2435 ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
2436 ShowScrollbar::Always => true,
2437 ShowScrollbar::Never => false,
2438 };
2439
2440 let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)> = fold_ranges
2441 .into_iter()
2442 .map(|(id, fold)| {
2443 let color = self
2444 .style
2445 .folds
2446 .ellipses
2447 .background
2448 .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
2449 .color;
2450
2451 (id, fold, color)
2452 })
2453 .collect();
2454
2455 let (line_number_layouts, fold_statuses) = self.layout_line_numbers(
2456 start_row..end_row,
2457 &active_rows,
2458 is_singleton,
2459 &snapshot,
2460 cx,
2461 );
2462
2463 let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
2464
2465 let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines);
2466
2467 let mut max_visible_line_width = 0.0;
2468 let line_layouts =
2469 self.layout_lines(start_row..end_row, &line_number_layouts, &snapshot, cx);
2470 for line_with_invisibles in &line_layouts {
2471 if line_with_invisibles.line.width() > max_visible_line_width {
2472 max_visible_line_width = line_with_invisibles.line.width();
2473 }
2474 }
2475
2476 let style = self.style.clone();
2477 let longest_line_width = layout_line(
2478 snapshot.longest_row(),
2479 &snapshot,
2480 &style,
2481 cx.text_layout_cache(),
2482 )
2483 .width();
2484 let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x();
2485 let em_width = style.text.em_width(cx.font_cache());
2486 let (scroll_width, blocks) = self.layout_blocks(
2487 start_row..end_row,
2488 &snapshot,
2489 size.x(),
2490 scroll_width,
2491 gutter_padding,
2492 gutter_width,
2493 em_width,
2494 gutter_width + gutter_margin,
2495 line_height,
2496 &style,
2497 &line_layouts,
2498 editor,
2499 cx,
2500 );
2501
2502 let scroll_max = vec2f(
2503 ((scroll_width - text_size.x()) / em_width).max(0.0),
2504 max_row as f32,
2505 );
2506
2507 let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x());
2508
2509 let autoscrolled = if autoscroll_horizontally {
2510 editor.autoscroll_horizontally(
2511 start_row,
2512 text_size.x(),
2513 scroll_width,
2514 em_width,
2515 &line_layouts,
2516 cx,
2517 )
2518 } else {
2519 false
2520 };
2521
2522 if clamped || autoscrolled {
2523 snapshot = editor.snapshot(cx);
2524 }
2525
2526 let style = editor.style(cx);
2527
2528 let mut context_menu = None;
2529 let mut code_actions_indicator = None;
2530 if let Some(newest_selection_head) = newest_selection_head {
2531 if (start_row..end_row).contains(&newest_selection_head.row()) {
2532 if editor.context_menu_visible() {
2533 context_menu =
2534 editor.render_context_menu(newest_selection_head, style.clone(), cx);
2535 }
2536
2537 let active = matches!(
2538 editor.context_menu,
2539 Some(crate::ContextMenu::CodeActions(_))
2540 );
2541
2542 code_actions_indicator = editor
2543 .render_code_actions_indicator(&style, active, cx)
2544 .map(|indicator| (newest_selection_head.row(), indicator));
2545 }
2546 }
2547
2548 let visible_rows = start_row..start_row + line_layouts.len() as u32;
2549 let mut hover = editor
2550 .hover_state
2551 .render(&snapshot, &style, visible_rows, cx);
2552 let mode = editor.mode;
2553
2554 let mut fold_indicators = editor.render_fold_indicators(
2555 fold_statuses,
2556 &style,
2557 editor.gutter_hovered,
2558 line_height,
2559 gutter_margin,
2560 cx,
2561 );
2562
2563 if let Some((_, context_menu)) = context_menu.as_mut() {
2564 context_menu.layout(
2565 SizeConstraint {
2566 min: Vector2F::zero(),
2567 max: vec2f(
2568 cx.window_size().x() * 0.7,
2569 (12. * line_height).min((size.y() - line_height) / 2.),
2570 ),
2571 },
2572 editor,
2573 cx,
2574 );
2575 }
2576
2577 if let Some((_, indicator)) = code_actions_indicator.as_mut() {
2578 indicator.layout(
2579 SizeConstraint::strict_along(
2580 Axis::Vertical,
2581 line_height * style.code_actions.vertical_scale,
2582 ),
2583 editor,
2584 cx,
2585 );
2586 }
2587
2588 for fold_indicator in fold_indicators.iter_mut() {
2589 if let Some(indicator) = fold_indicator.as_mut() {
2590 indicator.layout(
2591 SizeConstraint::strict_along(
2592 Axis::Vertical,
2593 line_height * style.code_actions.vertical_scale,
2594 ),
2595 editor,
2596 cx,
2597 );
2598 }
2599 }
2600
2601 if let Some((_, hover_popovers)) = hover.as_mut() {
2602 for hover_popover in hover_popovers.iter_mut() {
2603 hover_popover.layout(
2604 SizeConstraint {
2605 min: Vector2F::zero(),
2606 max: vec2f(
2607 (120. * em_width) // Default size
2608 .min(size.x() / 2.) // Shrink to half of the editor width
2609 .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
2610 (16. * line_height) // Default size
2611 .min(size.y() / 2.) // Shrink to half of the editor height
2612 .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
2613 ),
2614 },
2615 editor,
2616 cx,
2617 );
2618 }
2619 }
2620
2621 let invisible_symbol_font_size = self.style.text.font_size / 2.0;
2622 let invisible_symbol_style = RunStyle {
2623 color: self.style.whitespace,
2624 font_id: self.style.text.font_id,
2625 underline: Default::default(),
2626 };
2627
2628 (
2629 size,
2630 LayoutState {
2631 mode,
2632 position_map: Arc::new(PositionMap {
2633 size,
2634 scroll_max,
2635 line_layouts,
2636 line_height,
2637 em_width,
2638 em_advance,
2639 snapshot,
2640 }),
2641 visible_display_row_range: start_row..end_row,
2642 wrap_guides,
2643 gutter_size,
2644 gutter_padding,
2645 text_size,
2646 scrollbar_row_range,
2647 show_scrollbars,
2648 is_singleton,
2649 max_row,
2650 gutter_margin,
2651 active_rows,
2652 highlighted_rows,
2653 highlighted_ranges,
2654 fold_ranges,
2655 line_number_layouts,
2656 display_hunks,
2657 blocks,
2658 selections,
2659 context_menu,
2660 code_actions_indicator,
2661 fold_indicators,
2662 tab_invisible: cx.text_layout_cache().layout_str(
2663 "→",
2664 invisible_symbol_font_size,
2665 &[("→".len(), invisible_symbol_style)],
2666 ),
2667 space_invisible: cx.text_layout_cache().layout_str(
2668 "•",
2669 invisible_symbol_font_size,
2670 &[("•".len(), invisible_symbol_style)],
2671 ),
2672 hover_popovers: hover,
2673 },
2674 )
2675 }
2676
2677 fn paint(
2678 &mut self,
2679 scene: &mut SceneBuilder,
2680 bounds: RectF,
2681 visible_bounds: RectF,
2682 layout: &mut Self::LayoutState,
2683 editor: &mut Editor,
2684 cx: &mut PaintContext<Editor>,
2685 ) -> Self::PaintState {
2686 let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
2687 scene.push_layer(Some(visible_bounds));
2688
2689 let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
2690 let text_bounds = RectF::new(
2691 bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2692 layout.text_size,
2693 );
2694
2695 Self::attach_mouse_handlers(
2696 scene,
2697 &layout.position_map,
2698 layout.hover_popovers.is_some(),
2699 visible_bounds,
2700 text_bounds,
2701 gutter_bounds,
2702 bounds,
2703 cx,
2704 );
2705
2706 self.paint_background(scene, gutter_bounds, text_bounds, layout);
2707 if layout.gutter_size.x() > 0. {
2708 self.paint_gutter(scene, gutter_bounds, visible_bounds, layout, editor, cx);
2709 }
2710 self.paint_text(scene, text_bounds, visible_bounds, layout, editor, cx);
2711
2712 scene.push_layer(Some(bounds));
2713 if !layout.blocks.is_empty() {
2714 self.paint_blocks(scene, bounds, visible_bounds, layout, editor, cx);
2715 }
2716 self.paint_scrollbar(scene, bounds, layout, cx, &editor);
2717 scene.pop_layer();
2718
2719 scene.pop_layer();
2720 }
2721
2722 fn rect_for_text_range(
2723 &self,
2724 range_utf16: Range<usize>,
2725 bounds: RectF,
2726 _: RectF,
2727 layout: &Self::LayoutState,
2728 _: &Self::PaintState,
2729 _: &Editor,
2730 _: &ViewContext<Editor>,
2731 ) -> Option<RectF> {
2732 let text_bounds = RectF::new(
2733 bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
2734 layout.text_size,
2735 );
2736 let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.);
2737 let scroll_position = layout.position_map.snapshot.scroll_position();
2738 let start_row = scroll_position.y() as u32;
2739 let scroll_top = scroll_position.y() * layout.position_map.line_height;
2740 let scroll_left = scroll_position.x() * layout.position_map.em_width;
2741
2742 let range_start = OffsetUtf16(range_utf16.start)
2743 .to_display_point(&layout.position_map.snapshot.display_snapshot);
2744 if range_start.row() < start_row {
2745 return None;
2746 }
2747
2748 let line = &layout
2749 .position_map
2750 .line_layouts
2751 .get((range_start.row() - start_row) as usize)?
2752 .line;
2753 let range_start_x = line.x_for_index(range_start.column() as usize);
2754 let range_start_y = range_start.row() as f32 * layout.position_map.line_height;
2755 Some(RectF::new(
2756 content_origin
2757 + vec2f(
2758 range_start_x,
2759 range_start_y + layout.position_map.line_height,
2760 )
2761 - vec2f(scroll_left, scroll_top),
2762 vec2f(
2763 layout.position_map.em_width,
2764 layout.position_map.line_height,
2765 ),
2766 ))
2767 }
2768
2769 fn debug(
2770 &self,
2771 bounds: RectF,
2772 _: &Self::LayoutState,
2773 _: &Self::PaintState,
2774 _: &Editor,
2775 _: &ViewContext<Editor>,
2776 ) -> json::Value {
2777 json!({
2778 "type": "BufferElement",
2779 "bounds": bounds.to_json()
2780 })
2781 }
2782}
2783
2784type BufferRow = u32;
2785
2786pub struct LayoutState {
2787 position_map: Arc<PositionMap>,
2788 gutter_size: Vector2F,
2789 gutter_padding: f32,
2790 gutter_margin: f32,
2791 text_size: Vector2F,
2792 mode: EditorMode,
2793 wrap_guides: SmallVec<[(f32, bool); 2]>,
2794 visible_display_row_range: Range<u32>,
2795 active_rows: BTreeMap<u32, bool>,
2796 highlighted_rows: Option<Range<u32>>,
2797 line_number_layouts: Vec<Option<text_layout::Line>>,
2798 display_hunks: Vec<DisplayDiffHunk>,
2799 blocks: Vec<BlockLayout>,
2800 highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
2801 fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)>,
2802 selections: Vec<(Option<ReplicaId>, Vec<SelectionLayout>)>,
2803 scrollbar_row_range: Range<f32>,
2804 show_scrollbars: bool,
2805 is_singleton: bool,
2806 max_row: u32,
2807 context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
2808 code_actions_indicator: Option<(u32, AnyElement<Editor>)>,
2809 hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
2810 fold_indicators: Vec<Option<AnyElement<Editor>>>,
2811 tab_invisible: Line,
2812 space_invisible: Line,
2813}
2814
2815struct PositionMap {
2816 size: Vector2F,
2817 line_height: f32,
2818 scroll_max: Vector2F,
2819 em_width: f32,
2820 em_advance: f32,
2821 line_layouts: Vec<LineWithInvisibles>,
2822 snapshot: EditorSnapshot,
2823}
2824
2825#[derive(Debug)]
2826pub struct PointForPosition {
2827 previous_valid: DisplayPoint,
2828 pub next_valid: DisplayPoint,
2829 exact_unclipped: DisplayPoint,
2830 column_overshoot_after_line_end: u32,
2831}
2832
2833impl PointForPosition {
2834 #[cfg(test)]
2835 pub fn valid(valid: DisplayPoint) -> Self {
2836 Self {
2837 previous_valid: valid,
2838 next_valid: valid,
2839 exact_unclipped: valid,
2840 column_overshoot_after_line_end: 0,
2841 }
2842 }
2843
2844 fn as_valid(&self) -> Option<DisplayPoint> {
2845 if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
2846 Some(self.previous_valid)
2847 } else {
2848 None
2849 }
2850 }
2851}
2852
2853impl PositionMap {
2854 fn point_for_position(&self, text_bounds: RectF, position: Vector2F) -> PointForPosition {
2855 let scroll_position = self.snapshot.scroll_position();
2856 let position = position - text_bounds.origin();
2857 let y = position.y().max(0.0).min(self.size.y());
2858 let x = position.x() + (scroll_position.x() * self.em_width);
2859 let row = (y / self.line_height + scroll_position.y()) as u32;
2860 let (column, x_overshoot_after_line_end) = if let Some(line) = self
2861 .line_layouts
2862 .get(row as usize - scroll_position.y() as usize)
2863 .map(|line_with_spaces| &line_with_spaces.line)
2864 {
2865 if let Some(ix) = line.index_for_x(x) {
2866 (ix as u32, 0.0)
2867 } else {
2868 (line.len() as u32, 0f32.max(x - line.width()))
2869 }
2870 } else {
2871 (0, x)
2872 };
2873
2874 let mut exact_unclipped = DisplayPoint::new(row, column);
2875 let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
2876 let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
2877
2878 let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
2879 *exact_unclipped.column_mut() += column_overshoot_after_line_end;
2880 PointForPosition {
2881 previous_valid,
2882 next_valid,
2883 exact_unclipped,
2884 column_overshoot_after_line_end,
2885 }
2886 }
2887}
2888
2889struct BlockLayout {
2890 row: u32,
2891 element: AnyElement<Editor>,
2892 style: BlockStyle,
2893}
2894
2895fn layout_line(
2896 row: u32,
2897 snapshot: &EditorSnapshot,
2898 style: &EditorStyle,
2899 layout_cache: &TextLayoutCache,
2900) -> text_layout::Line {
2901 let mut line = snapshot.line(row);
2902
2903 if line.len() > MAX_LINE_LEN {
2904 let mut len = MAX_LINE_LEN;
2905 while !line.is_char_boundary(len) {
2906 len -= 1;
2907 }
2908
2909 line.truncate(len);
2910 }
2911
2912 layout_cache.layout_str(
2913 &line,
2914 style.text.font_size,
2915 &[(
2916 snapshot.line_len(row) as usize,
2917 RunStyle {
2918 font_id: style.text.font_id,
2919 color: Color::black(),
2920 underline: Default::default(),
2921 },
2922 )],
2923 )
2924}
2925
2926#[derive(Debug)]
2927pub struct Cursor {
2928 origin: Vector2F,
2929 block_width: f32,
2930 line_height: f32,
2931 color: Color,
2932 shape: CursorShape,
2933 block_text: Option<Line>,
2934}
2935
2936impl Cursor {
2937 pub fn new(
2938 origin: Vector2F,
2939 block_width: f32,
2940 line_height: f32,
2941 color: Color,
2942 shape: CursorShape,
2943 block_text: Option<Line>,
2944 ) -> Cursor {
2945 Cursor {
2946 origin,
2947 block_width,
2948 line_height,
2949 color,
2950 shape,
2951 block_text,
2952 }
2953 }
2954
2955 pub fn bounding_rect(&self, origin: Vector2F) -> RectF {
2956 RectF::new(
2957 self.origin + origin,
2958 vec2f(self.block_width, self.line_height),
2959 )
2960 }
2961
2962 pub fn paint(&self, scene: &mut SceneBuilder, origin: Vector2F, cx: &mut WindowContext) {
2963 let bounds = match self.shape {
2964 CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
2965 CursorShape::Block | CursorShape::Hollow => RectF::new(
2966 self.origin + origin,
2967 vec2f(self.block_width, self.line_height),
2968 ),
2969 CursorShape::Underscore => RectF::new(
2970 self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
2971 vec2f(self.block_width, 2.0),
2972 ),
2973 };
2974
2975 //Draw background or border quad
2976 if matches!(self.shape, CursorShape::Hollow) {
2977 scene.push_quad(Quad {
2978 bounds,
2979 background: None,
2980 border: Border::all(1., self.color),
2981 corner_radii: Default::default(),
2982 });
2983 } else {
2984 scene.push_quad(Quad {
2985 bounds,
2986 background: Some(self.color),
2987 border: Default::default(),
2988 corner_radii: Default::default(),
2989 });
2990 }
2991
2992 if let Some(block_text) = &self.block_text {
2993 block_text.paint(scene, self.origin + origin, bounds, self.line_height, cx);
2994 }
2995 }
2996
2997 pub fn shape(&self) -> CursorShape {
2998 self.shape
2999 }
3000}
3001
3002#[derive(Debug)]
3003pub struct HighlightedRange {
3004 pub start_y: f32,
3005 pub line_height: f32,
3006 pub lines: Vec<HighlightedRangeLine>,
3007 pub color: Color,
3008 pub corner_radius: f32,
3009}
3010
3011#[derive(Debug)]
3012pub struct HighlightedRangeLine {
3013 pub start_x: f32,
3014 pub end_x: f32,
3015}
3016
3017impl HighlightedRange {
3018 pub fn paint(&self, bounds: RectF, scene: &mut SceneBuilder) {
3019 if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
3020 self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
3021 self.paint_lines(
3022 self.start_y + self.line_height,
3023 &self.lines[1..],
3024 bounds,
3025 scene,
3026 );
3027 } else {
3028 self.paint_lines(self.start_y, &self.lines, bounds, scene);
3029 }
3030 }
3031
3032 fn paint_lines(
3033 &self,
3034 start_y: f32,
3035 lines: &[HighlightedRangeLine],
3036 bounds: RectF,
3037 scene: &mut SceneBuilder,
3038 ) {
3039 if lines.is_empty() {
3040 return;
3041 }
3042
3043 let mut path = PathBuilder::new();
3044 let first_line = lines.first().unwrap();
3045 let last_line = lines.last().unwrap();
3046
3047 let first_top_left = vec2f(first_line.start_x, start_y);
3048 let first_top_right = vec2f(first_line.end_x, start_y);
3049
3050 let curve_height = vec2f(0., self.corner_radius);
3051 let curve_width = |start_x: f32, end_x: f32| {
3052 let max = (end_x - start_x) / 2.;
3053 let width = if max < self.corner_radius {
3054 max
3055 } else {
3056 self.corner_radius
3057 };
3058
3059 vec2f(width, 0.)
3060 };
3061
3062 let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
3063 path.reset(first_top_right - top_curve_width);
3064 path.curve_to(first_top_right + curve_height, first_top_right);
3065
3066 let mut iter = lines.iter().enumerate().peekable();
3067 while let Some((ix, line)) = iter.next() {
3068 let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
3069
3070 if let Some((_, next_line)) = iter.peek() {
3071 let next_top_right = vec2f(next_line.end_x, bottom_right.y());
3072
3073 match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() {
3074 Ordering::Equal => {
3075 path.line_to(bottom_right);
3076 }
3077 Ordering::Less => {
3078 let curve_width = curve_width(next_top_right.x(), bottom_right.x());
3079 path.line_to(bottom_right - curve_height);
3080 if self.corner_radius > 0. {
3081 path.curve_to(bottom_right - curve_width, bottom_right);
3082 }
3083 path.line_to(next_top_right + curve_width);
3084 if self.corner_radius > 0. {
3085 path.curve_to(next_top_right + curve_height, next_top_right);
3086 }
3087 }
3088 Ordering::Greater => {
3089 let curve_width = curve_width(bottom_right.x(), next_top_right.x());
3090 path.line_to(bottom_right - curve_height);
3091 if self.corner_radius > 0. {
3092 path.curve_to(bottom_right + curve_width, bottom_right);
3093 }
3094 path.line_to(next_top_right - curve_width);
3095 if self.corner_radius > 0. {
3096 path.curve_to(next_top_right + curve_height, next_top_right);
3097 }
3098 }
3099 }
3100 } else {
3101 let curve_width = curve_width(line.start_x, line.end_x);
3102 path.line_to(bottom_right - curve_height);
3103 if self.corner_radius > 0. {
3104 path.curve_to(bottom_right - curve_width, bottom_right);
3105 }
3106
3107 let bottom_left = vec2f(line.start_x, bottom_right.y());
3108 path.line_to(bottom_left + curve_width);
3109 if self.corner_radius > 0. {
3110 path.curve_to(bottom_left - curve_height, bottom_left);
3111 }
3112 }
3113 }
3114
3115 if first_line.start_x > last_line.start_x {
3116 let curve_width = curve_width(last_line.start_x, first_line.start_x);
3117 let second_top_left = vec2f(last_line.start_x, start_y + self.line_height);
3118 path.line_to(second_top_left + curve_height);
3119 if self.corner_radius > 0. {
3120 path.curve_to(second_top_left + curve_width, second_top_left);
3121 }
3122 let first_bottom_left = vec2f(first_line.start_x, second_top_left.y());
3123 path.line_to(first_bottom_left - curve_width);
3124 if self.corner_radius > 0. {
3125 path.curve_to(first_bottom_left - curve_height, first_bottom_left);
3126 }
3127 }
3128
3129 path.line_to(first_top_left + curve_height);
3130 if self.corner_radius > 0. {
3131 path.curve_to(first_top_left + top_curve_width, first_top_left);
3132 }
3133 path.line_to(first_top_right - top_curve_width);
3134
3135 scene.push_path(path.build(self.color, Some(bounds)));
3136 }
3137}
3138
3139fn range_to_bounds(
3140 range: &Range<DisplayPoint>,
3141 content_origin: Vector2F,
3142 scroll_left: f32,
3143 scroll_top: f32,
3144 visible_row_range: &Range<u32>,
3145 line_end_overshoot: f32,
3146 position_map: &PositionMap,
3147) -> impl Iterator<Item = RectF> {
3148 let mut bounds: SmallVec<[RectF; 1]> = SmallVec::new();
3149
3150 if range.start == range.end {
3151 return bounds.into_iter();
3152 }
3153
3154 let start_row = visible_row_range.start;
3155 let end_row = visible_row_range.end;
3156
3157 let row_range = if range.end.column() == 0 {
3158 cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
3159 } else {
3160 cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
3161 };
3162
3163 let first_y =
3164 content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top;
3165
3166 for (idx, row) in row_range.enumerate() {
3167 let line_layout = &position_map.line_layouts[(row - start_row) as usize].line;
3168
3169 let start_x = if row == range.start.row() {
3170 content_origin.x() + line_layout.x_for_index(range.start.column() as usize)
3171 - scroll_left
3172 } else {
3173 content_origin.x() - scroll_left
3174 };
3175
3176 let end_x = if row == range.end.row() {
3177 content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left
3178 } else {
3179 content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left
3180 };
3181
3182 bounds.push(RectF::from_points(
3183 vec2f(start_x, first_y + position_map.line_height * idx as f32),
3184 vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32),
3185 ))
3186 }
3187
3188 bounds.into_iter()
3189}
3190
3191pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
3192 delta.powf(1.5) / 100.0
3193}
3194
3195fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
3196 delta.powf(1.2) / 300.0
3197}
3198
3199#[cfg(test)]
3200mod tests {
3201 use super::*;
3202 use crate::{
3203 display_map::{BlockDisposition, BlockProperties},
3204 editor_tests::{init_test, update_test_language_settings},
3205 Editor, MultiBuffer,
3206 };
3207 use gpui::TestAppContext;
3208 use language::language_settings;
3209 use log::info;
3210 use std::{num::NonZeroU32, sync::Arc};
3211 use util::test::sample_text;
3212
3213 #[gpui::test]
3214 fn test_layout_line_numbers(cx: &mut TestAppContext) {
3215 init_test(cx, |_| {});
3216
3217 let editor = cx
3218 .add_window(|cx| {
3219 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
3220 Editor::new(EditorMode::Full, buffer, None, None, cx)
3221 })
3222 .root(cx);
3223 let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3224
3225 let layouts = editor.update(cx, |editor, cx| {
3226 let snapshot = editor.snapshot(cx);
3227 element
3228 .layout_line_numbers(0..6, &Default::default(), false, &snapshot, cx)
3229 .0
3230 });
3231 assert_eq!(layouts.len(), 6);
3232 }
3233
3234 #[gpui::test]
3235 async fn test_vim_visual_selections(cx: &mut TestAppContext) {
3236 init_test(cx, |_| {});
3237
3238 let editor = cx
3239 .add_window(|cx| {
3240 let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
3241 Editor::new(EditorMode::Full, buffer, None, None, cx)
3242 })
3243 .root(cx);
3244 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3245 let (_, state) = editor.update(cx, |editor, cx| {
3246 editor.cursor_shape = CursorShape::Block;
3247 editor.change_selections(None, cx, |s| {
3248 s.select_ranges([
3249 Point::new(0, 0)..Point::new(1, 0),
3250 Point::new(3, 2)..Point::new(3, 3),
3251 Point::new(5, 6)..Point::new(6, 0),
3252 ]);
3253 });
3254 let mut new_parents = Default::default();
3255 let mut notify_views_if_parents_change = Default::default();
3256 let mut layout_cx = LayoutContext::new(
3257 cx,
3258 &mut new_parents,
3259 &mut notify_views_if_parents_change,
3260 false,
3261 );
3262 element.layout(
3263 SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3264 editor,
3265 &mut layout_cx,
3266 )
3267 });
3268 assert_eq!(state.selections.len(), 1);
3269 let local_selections = &state.selections[0].1;
3270 assert_eq!(local_selections.len(), 3);
3271 // moves cursor back one line
3272 assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6));
3273 assert_eq!(
3274 local_selections[0].range,
3275 DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0)
3276 );
3277
3278 // moves cursor back one column
3279 assert_eq!(
3280 local_selections[1].range,
3281 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3)
3282 );
3283 assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2));
3284
3285 // leaves cursor on the max point
3286 assert_eq!(
3287 local_selections[2].range,
3288 DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0)
3289 );
3290 assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0));
3291
3292 // active lines does not include 1 (even though the range of the selection does)
3293 assert_eq!(
3294 state.active_rows.keys().cloned().collect::<Vec<u32>>(),
3295 vec![0, 3, 5, 6]
3296 );
3297
3298 // multi-buffer support
3299 // in DisplayPoint co-ordinates, this is what we're dealing with:
3300 // 0: [[file
3301 // 1: header]]
3302 // 2: aaaaaa
3303 // 3: bbbbbb
3304 // 4: cccccc
3305 // 5:
3306 // 6: ...
3307 // 7: ffffff
3308 // 8: gggggg
3309 // 9: hhhhhh
3310 // 10:
3311 // 11: [[file
3312 // 12: header]]
3313 // 13: bbbbbb
3314 // 14: cccccc
3315 // 15: dddddd
3316 let editor = cx
3317 .add_window(|cx| {
3318 let buffer = MultiBuffer::build_multi(
3319 [
3320 (
3321 &(sample_text(8, 6, 'a') + "\n"),
3322 vec![
3323 Point::new(0, 0)..Point::new(3, 0),
3324 Point::new(4, 0)..Point::new(7, 0),
3325 ],
3326 ),
3327 (
3328 &(sample_text(8, 6, 'a') + "\n"),
3329 vec![Point::new(1, 0)..Point::new(3, 0)],
3330 ),
3331 ],
3332 cx,
3333 );
3334 Editor::new(EditorMode::Full, buffer, None, None, cx)
3335 })
3336 .root(cx);
3337 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3338 let (_, state) = editor.update(cx, |editor, cx| {
3339 editor.cursor_shape = CursorShape::Block;
3340 editor.change_selections(None, cx, |s| {
3341 s.select_display_ranges([
3342 DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0),
3343 DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
3344 ]);
3345 });
3346 let mut new_parents = Default::default();
3347 let mut notify_views_if_parents_change = Default::default();
3348 let mut layout_cx = LayoutContext::new(
3349 cx,
3350 &mut new_parents,
3351 &mut notify_views_if_parents_change,
3352 false,
3353 );
3354 element.layout(
3355 SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3356 editor,
3357 &mut layout_cx,
3358 )
3359 });
3360
3361 assert_eq!(state.selections.len(), 1);
3362 let local_selections = &state.selections[0].1;
3363 assert_eq!(local_selections.len(), 2);
3364
3365 // moves cursor on excerpt boundary back a line
3366 // and doesn't allow selection to bleed through
3367 assert_eq!(
3368 local_selections[0].range,
3369 DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0)
3370 );
3371 assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0));
3372
3373 // moves cursor on buffer boundary back two lines
3374 // and doesn't allow selection to bleed through
3375 assert_eq!(
3376 local_selections[1].range,
3377 DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0)
3378 );
3379 assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0));
3380 }
3381
3382 #[gpui::test]
3383 fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
3384 init_test(cx, |_| {});
3385
3386 let editor = cx
3387 .add_window(|cx| {
3388 let buffer = MultiBuffer::build_simple("", cx);
3389 Editor::new(EditorMode::Full, buffer, None, None, cx)
3390 })
3391 .root(cx);
3392
3393 editor.update(cx, |editor, cx| {
3394 editor.set_placeholder_text("hello", cx);
3395 editor.insert_blocks(
3396 [BlockProperties {
3397 style: BlockStyle::Fixed,
3398 disposition: BlockDisposition::Above,
3399 height: 3,
3400 position: Anchor::min(),
3401 render: Arc::new(|_| Empty::new().into_any()),
3402 }],
3403 None,
3404 cx,
3405 );
3406
3407 // Blur the editor so that it displays placeholder text.
3408 cx.blur();
3409 });
3410
3411 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3412 let (size, mut state) = editor.update(cx, |editor, cx| {
3413 let mut new_parents = Default::default();
3414 let mut notify_views_if_parents_change = Default::default();
3415 let mut layout_cx = LayoutContext::new(
3416 cx,
3417 &mut new_parents,
3418 &mut notify_views_if_parents_change,
3419 false,
3420 );
3421 element.layout(
3422 SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
3423 editor,
3424 &mut layout_cx,
3425 )
3426 });
3427
3428 assert_eq!(state.position_map.line_layouts.len(), 4);
3429 assert_eq!(
3430 state
3431 .line_number_layouts
3432 .iter()
3433 .map(Option::is_some)
3434 .collect::<Vec<_>>(),
3435 &[false, false, false, true]
3436 );
3437
3438 // Don't panic.
3439 let mut scene = SceneBuilder::new(1.0);
3440 let bounds = RectF::new(Default::default(), size);
3441 editor.update(cx, |editor, cx| {
3442 element.paint(
3443 &mut scene,
3444 bounds,
3445 bounds,
3446 &mut state,
3447 editor,
3448 &mut PaintContext::new(cx),
3449 );
3450 });
3451 }
3452
3453 #[gpui::test]
3454 fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
3455 const TAB_SIZE: u32 = 4;
3456
3457 let input_text = "\t \t|\t| a b";
3458 let expected_invisibles = vec![
3459 Invisible::Tab {
3460 line_start_offset: 0,
3461 },
3462 Invisible::Whitespace {
3463 line_offset: TAB_SIZE as usize,
3464 },
3465 Invisible::Tab {
3466 line_start_offset: TAB_SIZE as usize + 1,
3467 },
3468 Invisible::Tab {
3469 line_start_offset: TAB_SIZE as usize * 2 + 1,
3470 },
3471 Invisible::Whitespace {
3472 line_offset: TAB_SIZE as usize * 3 + 1,
3473 },
3474 Invisible::Whitespace {
3475 line_offset: TAB_SIZE as usize * 3 + 3,
3476 },
3477 ];
3478 assert_eq!(
3479 expected_invisibles.len(),
3480 input_text
3481 .chars()
3482 .filter(|initial_char| initial_char.is_whitespace())
3483 .count(),
3484 "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3485 );
3486
3487 init_test(cx, |s| {
3488 s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3489 s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
3490 });
3491
3492 let actual_invisibles =
3493 collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0);
3494
3495 assert_eq!(expected_invisibles, actual_invisibles);
3496 }
3497
3498 #[gpui::test]
3499 fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
3500 init_test(cx, |s| {
3501 s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3502 s.defaults.tab_size = NonZeroU32::new(4);
3503 });
3504
3505 for editor_mode_without_invisibles in [
3506 EditorMode::SingleLine,
3507 EditorMode::AutoHeight { max_lines: 100 },
3508 ] {
3509 let invisibles = collect_invisibles_from_new_editor(
3510 cx,
3511 editor_mode_without_invisibles,
3512 "\t\t\t| | a b",
3513 500.0,
3514 );
3515 assert!(invisibles.is_empty(),
3516 "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}");
3517 }
3518 }
3519
3520 #[gpui::test]
3521 fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) {
3522 let tab_size = 4;
3523 let input_text = "a\tbcd ".repeat(9);
3524 let repeated_invisibles = [
3525 Invisible::Tab {
3526 line_start_offset: 1,
3527 },
3528 Invisible::Whitespace {
3529 line_offset: tab_size as usize + 3,
3530 },
3531 Invisible::Whitespace {
3532 line_offset: tab_size as usize + 4,
3533 },
3534 Invisible::Whitespace {
3535 line_offset: tab_size as usize + 5,
3536 },
3537 ];
3538 let expected_invisibles = std::iter::once(repeated_invisibles)
3539 .cycle()
3540 .take(9)
3541 .flatten()
3542 .collect::<Vec<_>>();
3543 assert_eq!(
3544 expected_invisibles.len(),
3545 input_text
3546 .chars()
3547 .filter(|initial_char| initial_char.is_whitespace())
3548 .count(),
3549 "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
3550 );
3551 info!("Expected invisibles: {expected_invisibles:?}");
3552
3553 init_test(cx, |_| {});
3554
3555 // Put the same string with repeating whitespace pattern into editors of various size,
3556 // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
3557 let resize_step = 10.0;
3558 let mut editor_width = 200.0;
3559 while editor_width <= 1000.0 {
3560 update_test_language_settings(cx, |s| {
3561 s.defaults.tab_size = NonZeroU32::new(tab_size);
3562 s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
3563 s.defaults.preferred_line_length = Some(editor_width as u32);
3564 s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
3565 });
3566
3567 let actual_invisibles =
3568 collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, editor_width);
3569
3570 // Whatever the editor size is, ensure it has the same invisible kinds in the same order
3571 // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets).
3572 let mut i = 0;
3573 for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() {
3574 i = actual_index;
3575 match expected_invisibles.get(i) {
3576 Some(expected_invisible) => match (expected_invisible, actual_invisible) {
3577 (Invisible::Whitespace { .. }, Invisible::Whitespace { .. })
3578 | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {}
3579 _ => {
3580 panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}")
3581 }
3582 },
3583 None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"),
3584 }
3585 }
3586 let missing_expected_invisibles = &expected_invisibles[i + 1..];
3587 assert!(
3588 missing_expected_invisibles.is_empty(),
3589 "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}"
3590 );
3591
3592 editor_width += resize_step;
3593 }
3594 }
3595
3596 fn collect_invisibles_from_new_editor(
3597 cx: &mut TestAppContext,
3598 editor_mode: EditorMode,
3599 input_text: &str,
3600 editor_width: f32,
3601 ) -> Vec<Invisible> {
3602 info!(
3603 "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'"
3604 );
3605 let editor = cx
3606 .add_window(|cx| {
3607 let buffer = MultiBuffer::build_simple(&input_text, cx);
3608 Editor::new(editor_mode, buffer, None, None, cx)
3609 })
3610 .root(cx);
3611
3612 let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
3613 let (_, layout_state) = editor.update(cx, |editor, cx| {
3614 editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
3615 editor.set_wrap_width(Some(editor_width), cx);
3616
3617 let mut new_parents = Default::default();
3618 let mut notify_views_if_parents_change = Default::default();
3619 let mut layout_cx = LayoutContext::new(
3620 cx,
3621 &mut new_parents,
3622 &mut notify_views_if_parents_change,
3623 false,
3624 );
3625 element.layout(
3626 SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)),
3627 editor,
3628 &mut layout_cx,
3629 )
3630 });
3631
3632 layout_state
3633 .position_map
3634 .line_layouts
3635 .iter()
3636 .map(|line_with_invisibles| &line_with_invisibles.invisibles)
3637 .flatten()
3638 .cloned()
3639 .collect()
3640 }
3641}