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// }