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