terminal_element.rs

  1// use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
  2// use gpui::{
  3//     point, transparent_black, AnyElement, AppContext, Bounds, Component, CursorStyle, Element,
  4//     FontStyle, FontWeight, HighlightStyle, Hsla, LayoutId, Line, ModelContext, MouseButton,
  5//     Overlay, Pixels, Point, Quad, TextStyle, Underline, ViewContext, WeakModel, WindowContext,
  6// };
  7// use itertools::Itertools;
  8// use language::CursorShape;
  9// use ordered_float::OrderedFloat;
 10// use settings::Settings;
 11// use terminal::{
 12//     alacritty_terminal::{
 13//         ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
 14//         grid::Dimensions,
 15//         index::Point as AlacPoint,
 16//         term::{cell::Flags, TermMode},
 17//     },
 18//     // mappings::colors::convert_color,
 19//     terminal_settings::TerminalSettings,
 20//     IndexedCell,
 21//     Terminal,
 22//     TerminalContent,
 23//     TerminalSize,
 24// };
 25// use theme::ThemeSettings;
 26// use workspace::ElementId;
 27
 28// use std::mem;
 29// use std::{fmt::Debug, ops::RangeInclusive};
 30
 31// use crate::TerminalView;
 32
 33// ///The information generated during layout that is necessary for painting
 34// pub struct LayoutState {
 35//     cells: Vec<LayoutCell>,
 36//     rects: Vec<LayoutRect>,
 37//     relative_highlighted_ranges: Vec<(RangeInclusive<AlacPoint>, Hsla)>,
 38//     cursor: Option<Cursor>,
 39//     background_color: Hsla,
 40//     size: TerminalSize,
 41//     mode: TermMode,
 42//     display_offset: usize,
 43//     hyperlink_tooltip: Option<AnyElement<TerminalView>>,
 44//     gutter: f32,
 45// }
 46
 47// ///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
 48// struct DisplayCursor {
 49//     line: i32,
 50//     col: usize,
 51// }
 52
 53// impl DisplayCursor {
 54//     fn from(cursor_point: AlacPoint, display_offset: usize) -> Self {
 55//         Self {
 56//             line: cursor_point.line.0 + display_offset as i32,
 57//             col: cursor_point.column.0,
 58//         }
 59//     }
 60
 61//     pub fn line(&self) -> i32 {
 62//         self.line
 63//     }
 64
 65//     pub fn col(&self) -> usize {
 66//         self.col
 67//     }
 68// }
 69
 70// #[derive(Clone, Debug, Default)]
 71// struct LayoutCell {
 72//     point: AlacPoint<i32, i32>,
 73//     text: Line,
 74// }
 75
 76// impl LayoutCell {
 77//     fn new(point: AlacPoint<i32, i32>, text: Line) -> LayoutCell {
 78//         LayoutCell { point, text }
 79//     }
 80
 81//     fn paint(
 82//         &self,
 83//         origin: Point<Pixels>,
 84//         layout: &LayoutState,
 85//         _visible_bounds: Bounds<Pixels>,
 86//         _view: &mut TerminalView,
 87//         cx: &mut WindowContext,
 88//     ) {
 89//         let pos = {
 90//             let point = self.point;
 91
 92//             Point::new(
 93//                 (origin.x + point.column as f32 * layout.size.cell_width).floor(),
 94//                 origin.y + point.line as f32 * layout.size.line_height,
 95//             )
 96//         };
 97
 98//         self.text.paint(pos, layout.size.line_height, cx);
 99//     }
100// }
101
102// #[derive(Clone, Debug, Default)]
103// struct LayoutRect {
104//     point: AlacPoint<i32, i32>,
105//     num_of_cells: usize,
106//     color: Hsla,
107// }
108
109// impl LayoutRect {
110//     fn new(point: AlacPoint<i32, i32>, num_of_cells: usize, color: Hsla) -> LayoutRect {
111//         LayoutRect {
112//             point,
113//             num_of_cells,
114//             color,
115//         }
116//     }
117
118//     fn extend(&self) -> Self {
119//         LayoutRect {
120//             point: self.point,
121//             num_of_cells: self.num_of_cells + 1,
122//             color: self.color,
123//         }
124//     }
125
126//     fn paint(
127//         &self,
128//         origin: Point<Pixels>,
129//         layout: &LayoutState,
130//         _view: &mut TerminalView,
131//         cx: &mut ViewContext<TerminalView>,
132//     ) {
133//         let position = {
134//             let alac_point = self.point;
135//             point(
136//                 (origin.x + alac_point.column as f32 * layout.size.cell_width).floor(),
137//                 origin.y + alac_point.line as f32 * layout.size.line_height,
138//             )
139//         };
140//         let size = point(
141//             (layout.size.cell_width * self.num_of_cells as f32).ceil(),
142//             layout.size.line_height,
143//         )
144//         .into();
145
146//         cx.paint_quad(
147//             Bounds::new(position, size),
148//             Default::default(),
149//             self.color,
150//             Default::default(),
151//             transparent_black(),
152//         );
153//     }
154// }
155
156// ///The GPUI element that paints the terminal.
157// ///We need to keep a reference to the view for mouse events, do we need it for any other terminal stuff, or can we move that to connection?
158// pub struct TerminalElement {
159//     terminal: WeakModel<Terminal>,
160//     focused: bool,
161//     cursor_visible: bool,
162//     can_navigate_to_selected_word: bool,
163// }
164
165// impl TerminalElement {
166//     pub fn new(
167//         terminal: WeakModel<Terminal>,
168//         focused: bool,
169//         cursor_visible: bool,
170//         can_navigate_to_selected_word: bool,
171//     ) -> TerminalElement {
172//         TerminalElement {
173//             terminal,
174//             focused,
175//             cursor_visible,
176//             can_navigate_to_selected_word,
177//         }
178//     }
179
180//     //Vec<Range<AlacPoint>> -> Clip out the parts of the ranges
181
182//     fn layout_grid(
183//         grid: &Vec<IndexedCell>,
184//         text_style: &TextStyle,
185//         terminal_theme: &TerminalStyle,
186//         text_layout_cache: &TextLayoutCache,
187//         font_cache: &FontCache,
188//         hyperlink: Option<(HighlightStyle, &RangeInclusive<AlacPoint>)>,
189//     ) -> (Vec<LayoutCell>, Vec<LayoutRect>) {
190//         let mut cells = vec![];
191//         let mut rects = vec![];
192
193//         let mut cur_rect: Option<LayoutRect> = None;
194//         let mut cur_alac_color = None;
195
196//         let linegroups = grid.into_iter().group_by(|i| i.point.line);
197//         for (line_index, (_, line)) in linegroups.into_iter().enumerate() {
198//             for cell in line {
199//                 let mut fg = cell.fg;
200//                 let mut bg = cell.bg;
201//                 if cell.flags.contains(Flags::INVERSE) {
202//                     mem::swap(&mut fg, &mut bg);
203//                 }
204
205//                 //Expand background rect range
206//                 {
207//                     if matches!(bg, Named(NamedColor::Background)) {
208//                         //Continue to next cell, resetting variables if necessary
209//                         cur_alac_color = None;
210//                         if let Some(rect) = cur_rect {
211//                             rects.push(rect);
212//                             cur_rect = None
213//                         }
214//                     } else {
215//                         match cur_alac_color {
216//                             Some(cur_color) => {
217//                                 if bg == cur_color {
218//                                     cur_rect = cur_rect.take().map(|rect| rect.extend());
219//                                 } else {
220//                                     cur_alac_color = Some(bg);
221//                                     if cur_rect.is_some() {
222//                                         rects.push(cur_rect.take().unwrap());
223//                                     }
224//                                     cur_rect = Some(LayoutRect::new(
225//                                         AlacPoint::new(
226//                                             line_index as i32,
227//                                             cell.point.column.0 as i32,
228//                                         ),
229//                                         1,
230//                                         convert_color(&bg, &terminal_theme),
231//                                     ));
232//                                 }
233//                             }
234//                             None => {
235//                                 cur_alac_color = Some(bg);
236//                                 cur_rect = Some(LayoutRect::new(
237//                                     AlacPoint::new(line_index as i32, cell.point.column.0 as i32),
238//                                     1,
239//                                     convert_color(&bg, &terminal_theme),
240//                                 ));
241//                             }
242//                         }
243//                     }
244//                 }
245
246//                 //Layout current cell text
247//                 {
248//                     let cell_text = &cell.c.to_string();
249//                     if !is_blank(&cell) {
250//                         let cell_style = TerminalElement::cell_style(
251//                             &cell,
252//                             fg,
253//                             terminal_theme,
254//                             text_style,
255//                             font_cache,
256//                             hyperlink,
257//                         );
258
259//                         let layout_cell = text_layout_cache.layout_str(
260//                             cell_text,
261//                             text_style.font_size,
262//                             &[(cell_text.len(), cell_style)],
263//                         );
264
265//                         cells.push(LayoutCell::new(
266//                             AlacPoint::new(line_index as i32, cell.point.column.0 as i32),
267//                             layout_cell,
268//                         ))
269//                     };
270//                 }
271//             }
272
273//             if cur_rect.is_some() {
274//                 rects.push(cur_rect.take().unwrap());
275//             }
276//         }
277//         (cells, rects)
278//     }
279
280//     // Compute the cursor position and expected block width, may return a zero width if x_for_index returns
281//     // the same position for sequential indexes. Use em_width instead
282//     fn shape_cursor(
283//         cursor_point: DisplayCursor,
284//         size: TerminalSize,
285//         text_fragment: &Line,
286//     ) -> Option<(Point<Pixels>, Pixels)> {
287//         if cursor_point.line() < size.total_lines() as i32 {
288//             let cursor_width = if text_fragment.width == Pixels::ZERO {
289//                 size.cell_width()
290//             } else {
291//                 text_fragment.width
292//             };
293
294//             //Cursor should always surround as much of the text as possible,
295//             //hence when on pixel boundaries round the origin down and the width up
296//             Some((
297//                 point(
298//                     (cursor_point.col() as f32 * size.cell_width()).floor(),
299//                     (cursor_point.line() as f32 * size.line_height()).floor(),
300//                 ),
301//                 cursor_width.ceil(),
302//             ))
303//         } else {
304//             None
305//         }
306//     }
307
308//     ///Convert the Alacritty cell styles to GPUI text styles and background color
309//     fn cell_style(
310//         indexed: &IndexedCell,
311//         fg: terminal::alacritty_terminal::ansi::Color,
312//         style: &TerminalStyle,
313//         text_style: &TextStyle,
314//         font_cache: &FontCache,
315//         hyperlink: Option<(HighlightStyle, &RangeInclusive<AlacPoint>)>,
316//     ) -> RunStyle {
317//         let flags = indexed.cell.flags;
318//         let fg = convert_color(&fg, &style);
319
320//         let mut underline = flags
321//             .intersects(Flags::ALL_UNDERLINES)
322//             .then(|| Underline {
323//                 color: Some(fg),
324//                 squiggly: flags.contains(Flags::UNDERCURL),
325//                 thickness: OrderedFloat(1.),
326//             })
327//             .unwrap_or_default();
328
329//         if indexed.cell.hyperlink().is_some() {
330//             if underline.thickness == OrderedFloat(0.) {
331//                 underline.thickness = OrderedFloat(1.);
332//             }
333//         }
334
335//         let mut properties = Properties::new();
336//         if indexed.flags.intersects(Flags::BOLD | Flags::DIM_BOLD) {
337//             properties = *properties.weight(FontWeight::BOLD);
338//         }
339//         if indexed.flags.intersects(Flags::ITALIC) {
340//             properties = *properties.style(FontStyle::Italic);
341//         }
342
343//         let font_id = font_cache
344//             .select_font(text_style.font_family, &properties)
345//             .unwrap_or(text_style.font_id);
346
347//         let mut result = RunStyle {
348//             color: fg,
349//             font_id,
350//             underline,
351//         };
352
353//         if let Some((style, range)) = hyperlink {
354//             if range.contains(&indexed.point) {
355//                 if let Some(underline) = style.underline {
356//                     result.underline = underline;
357//                 }
358
359//                 if let Some(color) = style.color {
360//                     result.color = color;
361//                 }
362//             }
363//         }
364
365//         result
366//     }
367
368//     fn generic_button_handler<E>(
369//         connection: WeakModel<Terminal>,
370//         origin: Point<Pixels>,
371//         f: impl Fn(&mut Terminal, Point<Pixels>, E, &mut ModelContext<Terminal>),
372//     ) -> impl Fn(E, &mut TerminalView, &mut EventContext<TerminalView>) {
373//         move |event, _: &mut TerminalView, cx| {
374//             cx.focus_parent();
375//             if let Some(conn_handle) = connection.upgrade() {
376//                 conn_handle.update(cx, |terminal, cx| {
377//                     f(terminal, origin, event, cx);
378
379//                     cx.notify();
380//                 })
381//             }
382//         }
383//     }
384
385//     fn attach_mouse_handlers(
386//         &self,
387//         origin: Point<Pixels>,
388//         visible_bounds: Bounds<Pixels>,
389//         mode: TermMode,
390//         cx: &mut ViewContext<TerminalView>,
391//     ) {
392//         let connection = self.terminal;
393
394//         let mut region = MouseRegion::new::<Self>(cx.view_id(), 0, visible_bounds);
395
396//         // Terminal Emulator controlled behavior:
397//         region = region
398//             // Start selections
399//             .on_down(MouseButton::Left, move |event, v: &mut TerminalView, cx| {
400//                 let terminal_view = cx.handle();
401//                 cx.focus(&terminal_view);
402//                 v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
403//                 if let Some(conn_handle) = connection.upgrade() {
404//                     conn_handle.update(cx, |terminal, cx| {
405//                         terminal.mouse_down(&event, origin);
406
407//                         cx.notify();
408//                     })
409//                 }
410//             })
411//             // Update drag selections
412//             .on_drag(MouseButton::Left, move |event, _: &mut TerminalView, cx| {
413//                 if event.end {
414//                     return;
415//                 }
416
417//                 if cx.is_self_focused() {
418//                     if let Some(conn_handle) = connection.upgrade() {
419//                         conn_handle.update(cx, |terminal, cx| {
420//                             terminal.mouse_drag(event, origin);
421//                             cx.notify();
422//                         })
423//                     }
424//                 }
425//             })
426//             // Copy on up behavior
427//             .on_up(
428//                 MouseButton::Left,
429//                 TerminalElement::generic_button_handler(
430//                     connection,
431//                     origin,
432//                     move |terminal, origin, e, cx| {
433//                         terminal.mouse_up(&e, origin, cx);
434//                     },
435//                 ),
436//             )
437//             // Context menu
438//             .on_click(
439//                 MouseButton::Right,
440//                 move |event, view: &mut TerminalView, cx| {
441//                     let mouse_mode = if let Some(conn_handle) = connection.upgrade() {
442//                         conn_handle.update(cx, |terminal, _cx| terminal.mouse_mode(event.shift))
443//                     } else {
444//                         // If we can't get the model handle, probably can't deploy the context menu
445//                         true
446//                     };
447//                     if !mouse_mode {
448//                         view.deploy_context_menu(event.position, cx);
449//                     }
450//                 },
451//             )
452//             .on_move(move |event, _: &mut TerminalView, cx| {
453//                 if cx.is_self_focused() {
454//                     if let Some(conn_handle) = connection.upgrade() {
455//                         conn_handle.update(cx, |terminal, cx| {
456//                             terminal.mouse_move(&event, origin);
457//                             cx.notify();
458//                         })
459//                     }
460//                 }
461//             })
462//             .on_scroll(move |event, _: &mut TerminalView, cx| {
463//                 if let Some(conn_handle) = connection.upgrade() {
464//                     conn_handle.update(cx, |terminal, cx| {
465//                         terminal.scroll_wheel(event, origin);
466//                         cx.notify();
467//                     })
468//                 }
469//             });
470
471//         // Mouse mode handlers:
472//         // All mouse modes need the extra click handlers
473//         if mode.intersects(TermMode::MOUSE_MODE) {
474//             region = region
475//                 .on_down(
476//                     MouseButton::Right,
477//                     TerminalElement::generic_button_handler(
478//                         connection,
479//                         origin,
480//                         move |terminal, origin, e, _cx| {
481//                             terminal.mouse_down(&e, origin);
482//                         },
483//                     ),
484//                 )
485//                 .on_down(
486//                     MouseButton::Middle,
487//                     TerminalElement::generic_button_handler(
488//                         connection,
489//                         origin,
490//                         move |terminal, origin, e, _cx| {
491//                             terminal.mouse_down(&e, origin);
492//                         },
493//                     ),
494//                 )
495//                 .on_up(
496//                     MouseButton::Right,
497//                     TerminalElement::generic_button_handler(
498//                         connection,
499//                         origin,
500//                         move |terminal, origin, e, cx| {
501//                             terminal.mouse_up(&e, origin, cx);
502//                         },
503//                     ),
504//                 )
505//                 .on_up(
506//                     MouseButton::Middle,
507//                     TerminalElement::generic_button_handler(
508//                         connection,
509//                         origin,
510//                         move |terminal, origin, e, cx| {
511//                             terminal.mouse_up(&e, origin, cx);
512//                         },
513//                     ),
514//                 )
515//         }
516
517//         cx.scene().push_mouse_region(region);
518//     }
519// }
520
521// impl Element<TerminalView> for TerminalElement {
522//     type ElementState = LayoutState;
523
524//     fn layout(
525//         &mut self,
526//         view_state: &mut TerminalView,
527//         element_state: Option<Self::ElementState>,
528//         cx: &mut ViewContext<TerminalView>,
529//     ) -> (LayoutId, Self::ElementState) {
530//         let settings = ThemeSettings::get_global(cx);
531//         let terminal_settings = TerminalSettings::get_global(cx);
532
533//         //Setup layout information
534//         let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
535//         let link_style = settings.theme.editor.link_definition;
536//         let tooltip_style = settings.theme.tooltip.clone();
537
538//         let font_cache = cx.font_cache();
539//         let font_size = font_size(&terminal_settings, cx).unwrap_or(settings.buffer_font_size(cx));
540//         let font_family_name = terminal_settings
541//             .font_family
542//             .as_ref()
543//             .unwrap_or(&settings.buffer_font_family_name);
544//         let font_features = terminal_settings
545//             .font_features
546//             .as_ref()
547//             .unwrap_or(&settings.buffer_font_features);
548//         let family_id = font_cache
549//             .load_family(&[font_family_name], &font_features)
550//             .log_err()
551//             .unwrap_or(settings.buffer_font_family);
552//         let font_id = font_cache
553//             .select_font(family_id, &Default::default())
554//             .unwrap();
555
556//         let text_style = TextStyle {
557//             color: settings.theme.editor.text_color,
558//             font_family_id: family_id,
559//             font_family_name: font_cache.family_name(family_id).unwrap(),
560//             font_id,
561//             font_size,
562//             font_properties: Default::default(),
563//             underline: Default::default(),
564//             soft_wrap: false,
565//         };
566//         let selection_color = settings.theme.editor.selection.selection;
567//         let match_color = settings.theme.search.match_background;
568//         let gutter;
569//         let dimensions = {
570//             let line_height = text_style.font_size * terminal_settings.line_height.value();
571//             let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size);
572//             gutter = cell_width;
573
574//             let size = constraint.max - point(gutter, 0.);
575//             TerminalSize::new(line_height, cell_width, size)
576//         };
577
578//         let search_matches = if let Some(terminal_model) = self.terminal.upgrade() {
579//             terminal_model.read(cx).matches.clone()
580//         } else {
581//             Default::default()
582//         };
583
584//         let background_color = terminal_theme.background;
585//         let terminal_handle = self.terminal.upgrade().unwrap();
586
587//         let last_hovered_word = terminal_handle.update(cx, |terminal, cx| {
588//             terminal.set_size(dimensions);
589//             terminal.try_sync(cx);
590//             if self.can_navigate_to_selected_word && terminal.can_navigate_to_selected_word() {
591//                 terminal.last_content.last_hovered_word.clone()
592//             } else {
593//                 None
594//             }
595//         });
596
597//         let hyperlink_tooltip = last_hovered_word.clone().map(|hovered_word| {
598//             let mut tooltip = Overlay::new(
599//                 Empty::new()
600//                     .contained()
601//                     .constrained()
602//                     .with_width(dimensions.width())
603//                     .with_height(dimensions.height())
604//                     .with_tooltip::<TerminalElement>(
605//                         hovered_word.id,
606//                         hovered_word.word,
607//                         None,
608//                         tooltip_style,
609//                         cx,
610//                     ),
611//             )
612//             .with_position_mode(gpui::OverlayPositionMode::Local)
613//             .into_any();
614
615//             tooltip.layout(
616//                 SizeConstraint::new(Point::zero(), cx.window_size()),
617//                 view_state,
618//                 cx,
619//             );
620//             tooltip
621//         });
622
623//         let TerminalContent {
624//             cells,
625//             mode,
626//             display_offset,
627//             cursor_char,
628//             selection,
629//             cursor,
630//             ..
631//         } = { &terminal_handle.read(cx).last_content };
632
633//         // searches, highlights to a single range representations
634//         let mut relative_highlighted_ranges = Vec::new();
635//         for search_match in search_matches {
636//             relative_highlighted_ranges.push((search_match, match_color))
637//         }
638//         if let Some(selection) = selection {
639//             relative_highlighted_ranges.push((selection.start..=selection.end, selection_color));
640//         }
641
642//         // then have that representation be converted to the appropriate highlight data structure
643
644//         let (cells, rects) = TerminalElement::layout_grid(
645//             cells,
646//             &text_style,
647//             &terminal_theme,
648//             cx.text_layout_cache(),
649//             cx.font_cache(),
650//             last_hovered_word
651//                 .as_ref()
652//                 .map(|last_hovered_word| (link_style, &last_hovered_word.word_match)),
653//         );
654
655//         //Layout cursor. Rectangle is used for IME, so we should lay it out even
656//         //if we don't end up showing it.
657//         let cursor = if let AlacCursorShape::Hidden = cursor.shape {
658//             None
659//         } else {
660//             let cursor_point = DisplayCursor::from(cursor.point, *display_offset);
661//             let cursor_text = {
662//                 let str_trxt = cursor_char.to_string();
663
664//                 let color = if self.focused {
665//                     terminal_theme.background
666//                 } else {
667//                     terminal_theme.foreground
668//                 };
669
670//                 cx.text_layout_cache().layout_str(
671//                     &str_trxt,
672//                     text_style.font_size,
673//                     &[(
674//                         str_trxt.len(),
675//                         RunStyle {
676//                             font_id: text_style.font_id,
677//                             color,
678//                             underline: Default::default(),
679//                         },
680//                     )],
681//                 )
682//             };
683
684//             let focused = self.focused;
685//             TerminalElement::shape_cursor(cursor_point, dimensions, &cursor_text).map(
686//                 move |(cursor_position, block_width)| {
687//                     let (shape, text) = match cursor.shape {
688//                         AlacCursorShape::Block if !focused => (CursorShape::Hollow, None),
689//                         AlacCursorShape::Block => (CursorShape::Block, Some(cursor_text)),
690//                         AlacCursorShape::Underline => (CursorShape::Underscore, None),
691//                         AlacCursorShape::Beam => (CursorShape::Bar, None),
692//                         AlacCursorShape::HollowBlock => (CursorShape::Hollow, None),
693//                         //This case is handled in the if wrapping the whole cursor layout
694//                         AlacCursorShape::Hidden => unreachable!(),
695//                     };
696
697//                     Cursor::new(
698//                         cursor_position,
699//                         block_width,
700//                         dimensions.line_height,
701//                         terminal_theme.cursor,
702//                         shape,
703//                         text,
704//                     )
705//                 },
706//             )
707//         };
708
709//         //Done!
710//         (
711//             constraint.max,
712//             Self::ElementState {
713//                 cells,
714//                 cursor,
715//                 background_color,
716//                 size: dimensions,
717//                 rects,
718//                 relative_highlighted_ranges,
719//                 mode: *mode,
720//                 display_offset: *display_offset,
721//                 hyperlink_tooltip,
722//                 gutter,
723//             },
724//         )
725//     }
726
727//     fn paint(
728//         &mut self,
729//         bounds: Bounds<Pixels>,
730//         view_state: &mut TerminalView,
731//         element_state: &mut Self::ElementState,
732//         cx: &mut ViewContext<TerminalView>,
733//     ) {
734//         let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
735
736//         //Setup element stuff
737//         let clip_bounds = Some(visible_bounds);
738
739//         cx.paint_layer(clip_bounds, |cx| {
740//             let origin = bounds.origin + point(element_state.gutter, 0.);
741
742//             // Elements are ephemeral, only at paint time do we know what could be clicked by a mouse
743//             self.attach_mouse_handlers(origin, visible_bounds, element_state.mode, cx);
744
745//             cx.scene().push_cursor_region(gpui::CursorRegion {
746//                 bounds,
747//                 style: if element_state.hyperlink_tooltip.is_some() {
748//                     CursorStyle::AlacPointingHand
749//                 } else {
750//                     CursorStyle::IBeam
751//                 },
752//             });
753
754//             cx.paint_layer(clip_bounds, |cx| {
755//                 //Start with a background color
756//                 cx.scene().push_quad(Quad {
757//                     bounds,
758//                     background: Some(element_state.background_color),
759//                     border: Default::default(),
760//                     corner_radii: Default::default(),
761//                 });
762
763//                 for rect in &element_state.rects {
764//                     rect.paint(origin, element_state, view_state, cx);
765//                 }
766//             });
767
768//             //Draw Highlighted Backgrounds
769//             cx.paint_layer(clip_bounds, |cx| {
770//                 for (relative_highlighted_range, color) in
771//                     element_state.relative_highlighted_ranges.iter()
772//                 {
773//                     if let Some((start_y, highlighted_range_lines)) = to_highlighted_range_lines(
774//                         relative_highlighted_range,
775//                         element_state,
776//                         origin,
777//                     ) {
778//                         let hr = HighlightedRange {
779//                             start_y, //Need to change this
780//                             line_height: element_state.size.line_height,
781//                             lines: highlighted_range_lines,
782//                             color: color.clone(),
783//                             //Copied from editor. TODO: move to theme or something
784//                             corner_radius: 0.15 * element_state.size.line_height,
785//                         };
786//                         hr.paint(bounds, cx);
787//                     }
788//                 }
789//             });
790
791//             //Draw the text cells
792//             cx.paint_layer(clip_bounds, |cx| {
793//                 for cell in &element_state.cells {
794//                     cell.paint(origin, element_state, visible_bounds, view_state, cx);
795//                 }
796//             });
797
798//             //Draw cursor
799//             if self.cursor_visible {
800//                 if let Some(cursor) = &element_state.cursor {
801//                     cx.paint_layer(clip_bounds, |cx| {
802//                         cursor.paint(origin, cx);
803//                     })
804//                 }
805//             }
806
807//             if let Some(element) = &mut element_state.hyperlink_tooltip {
808//                 element.paint(origin, visible_bounds, view_state, cx)
809//             }
810//         });
811//     }
812
813//     fn element_id(&self) -> Option<ElementId> {
814//         todo!()
815//     }
816
817//     // todo!() remove?
818//     // fn metadata(&self) -> Option<&dyn std::any::Any> {
819//     //     None
820//     // }
821
822//     // fn debug(
823//     //     &self,
824//     //     _: Bounds<Pixels>,
825//     //     _: &Self::ElementState,
826//     //     _: &Self::PaintState,
827//     //     _: &TerminalView,
828//     //     _: &gpui::ViewContext<TerminalView>,
829//     // ) -> gpui::serde_json::Value {
830//     //     json!({
831//     //         "type": "TerminalElement",
832//     //     })
833//     // }
834
835//     // fn rect_for_text_range(
836//     //     &self,
837//     //     _: Range<usize>,
838//     //     bounds: Bounds<Pixels>,
839//     //     _: Bounds<Pixels>,
840//     //     layout: &Self::ElementState,
841//     //     _: &Self::PaintState,
842//     //     _: &TerminalView,
843//     //     _: &gpui::ViewContext<TerminalView>,
844//     // ) -> Option<Bounds<Pixels>> {
845//     //     // Use the same origin that's passed to `Cursor::paint` in the paint
846//     //     // method bove.
847//     //     let mut origin = bounds.origin() + point(layout.size.cell_width, 0.);
848
849//     //     // TODO - Why is it necessary to move downward one line to get correct
850//     //     // positioning? I would think that we'd want the same rect that is
851//     //     // painted for the cursor.
852//     //     origin += point(0., layout.size.line_height);
853
854//     //     Some(layout.cursor.as_ref()?.bounding_rect(origin))
855//     // }
856// }
857
858// impl Component<TerminalView> for TerminalElement {
859//     fn render(self) -> AnyElement<TerminalView> {
860//         todo!()
861//     }
862// }
863
864// fn is_blank(cell: &IndexedCell) -> bool {
865//     if cell.c != ' ' {
866//         return false;
867//     }
868
869//     if cell.bg != AnsiColor::Named(NamedColor::Background) {
870//         return false;
871//     }
872
873//     if cell.hyperlink().is_some() {
874//         return false;
875//     }
876
877//     if cell
878//         .flags
879//         .intersects(Flags::ALL_UNDERLINES | Flags::INVERSE | Flags::STRIKEOUT)
880//     {
881//         return false;
882//     }
883
884//     return true;
885// }
886
887// fn to_highlighted_range_lines(
888//     range: &RangeInclusive<AlacPoint>,
889//     layout: &LayoutState,
890//     origin: Point<Pixels>,
891// ) -> Option<(Pixels, Vec<HighlightedRangeLine>)> {
892//     // Step 1. Normalize the points to be viewport relative.
893//     // When display_offset = 1, here's how the grid is arranged:
894//     //-2,0 -2,1...
895//     //--- Viewport top
896//     //-1,0 -1,1...
897//     //--------- Terminal Top
898//     // 0,0  0,1...
899//     // 1,0  1,1...
900//     //--- Viewport Bottom
901//     // 2,0  2,1...
902//     //--------- Terminal Bottom
903
904//     // Normalize to viewport relative, from terminal relative.
905//     // lines are i32s, which are negative above the top left corner of the terminal
906//     // If the user has scrolled, we use the display_offset to tell us which offset
907//     // of the grid data we should be looking at. But for the rendering step, we don't
908//     // want negatives. We want things relative to the 'viewport' (the area of the grid
909//     // which is currently shown according to the display offset)
910//     let unclamped_start = AlacPoint::new(
911//         range.start().line + layout.display_offset,
912//         range.start().column,
913//     );
914//     let unclamped_end =
915//         AlacPoint::new(range.end().line + layout.display_offset, range.end().column);
916
917//     // Step 2. Clamp range to viewport, and return None if it doesn't overlap
918//     if unclamped_end.line.0 < 0 || unclamped_start.line.0 > layout.size.num_lines() as i32 {
919//         return None;
920//     }
921
922//     let clamped_start_line = unclamped_start.line.0.max(0) as usize;
923//     let clamped_end_line = unclamped_end.line.0.min(layout.size.num_lines() as i32) as usize;
924//     //Convert the start of the range to pixels
925//     let start_y = origin.y + clamped_start_line as f32 * layout.size.line_height;
926
927//     // Step 3. Expand ranges that cross lines into a collection of single-line ranges.
928//     //  (also convert to pixels)
929//     let mut highlighted_range_lines = Vec::new();
930//     for line in clamped_start_line..=clamped_end_line {
931//         let mut line_start = 0;
932//         let mut line_end = layout.size.columns();
933
934//         if line == clamped_start_line {
935//             line_start = unclamped_start.column.0 as usize;
936//         }
937//         if line == clamped_end_line {
938//             line_end = unclamped_end.column.0 as usize + 1; //+1 for inclusive
939//         }
940
941//         highlighted_range_lines.push(HighlightedRangeLine {
942//             start_x: origin.x + line_start as f32 * layout.size.cell_width,
943//             end_x: origin.x + line_end as f32 * layout.size.cell_width,
944//         });
945//     }
946
947//     Some((start_y, highlighted_range_lines))
948// }
949
950// fn font_size(terminal_settings: &TerminalSettings, cx: &mut AppContext) -> Option<Pixels> {
951//     terminal_settings
952//         .font_size
953//         .map(|size| theme::adjusted_font_size(size, cx))
954// }