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