terminal_element.rs

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