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