WIP

Kirill Bulatov created

Change summary

Cargo.lock                                    |    1 
crates/editor2/src/scroll.rs                  |    2 
crates/gpui2/src/window.rs                    |    6 
crates/sqlez/src/bindable.rs                  |   17 
crates/terminal_view2/src/persistence.rs      |    1 
crates/terminal_view2/src/terminal_element.rs | 1913 ++++++++++----------
crates/terminal_view2/src/terminal_panel.rs   |  315 +-
crates/terminal_view2/src/terminal_view.rs    |   46 
crates/workspace2/src/persistence/model.rs    |    2 
crates/workspace2/src/workspace2.rs           |   40 
crates/zed2/Cargo.toml                        |    2 
crates/zed2/src/main.rs                       |    2 
crates/zed2/src/zed2.rs                       |    9 
13 files changed, 1,172 insertions(+), 1,184 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -11567,6 +11567,7 @@ dependencies = [
  "smol",
  "sum_tree",
  "tempdir",
+ "terminal_view2",
  "text2",
  "theme2",
  "thiserror",

crates/editor2/src/scroll.rs 🔗

@@ -426,7 +426,7 @@ impl Editor {
 
     pub fn read_scroll_position_from_db(
         &mut self,
-        item_id: usize,
+        item_id: u64,
         workspace_id: WorkspaceId,
         cx: &mut ViewContext<Editor>,
     ) {

crates/gpui2/src/window.rs 🔗

@@ -1830,8 +1830,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
         self.view
     }
 
-    pub fn model(&self) -> Model<V> {
-        self.view.model.clone()
+    pub fn model(&self) -> &Model<V> {
+        &self.view.model
     }
 
     /// Access the underlying window context.
@@ -2163,7 +2163,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
 
     pub fn observe_global<G: 'static>(
         &mut self,
-        f: impl Fn(&mut V, &mut ViewContext<'_, V>) + 'static,
+        mut f: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static,
     ) -> Subscription {
         let window_handle = self.window.handle;
         let view = self.view().downgrade();

crates/sqlez/src/bindable.rs 🔗

@@ -164,6 +164,23 @@ impl Column for i64 {
     }
 }
 
+impl StaticColumnCount for u64 {}
+impl Bind for u64 {
+    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+        statement
+            .bind_int64(start_index, (*self) as i64)
+            .with_context(|| format!("Failed to bind i64 at index {start_index}"))?;
+        Ok(start_index + 1)
+    }
+}
+
+impl Column for u64 {
+    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
+        let result = statement.column_int64(start_index)? as u64;
+        Ok((result, start_index + 1))
+    }
+}
+
 impl StaticColumnCount for u32 {}
 impl Bind for u32 {
     fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {

crates/terminal_view2/src/persistence.rs 🔗

@@ -1,7 +1,6 @@
 use std::path::PathBuf;
 
 use db::{define_connection, query, sqlez_macros::sql};
-use gpui::EntityId;
 use workspace::{ItemId, WorkspaceDb, WorkspaceId};
 
 define_connection! {

crates/terminal_view2/src/terminal_element.rs 🔗

@@ -1,961 +1,952 @@
-use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
-use gpui::{
-    AnyElement, AppContext, Bounds, Component, Element, HighlightStyle, Hsla, LayoutId, Line,
-    ModelContext, MouseButton, Pixels, Point, TextStyle, Underline, ViewContext, WeakModel,
-    WindowContext,
-};
-use itertools::Itertools;
-use language::CursorShape;
-use ordered_float::OrderedFloat;
-use settings::Settings;
-use terminal::{
-    alacritty_terminal::{
-        ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
-        grid::Dimensions,
-        index::Point as AlacPoint,
-        term::{cell::Flags, TermMode},
-    },
-    // mappings::colors::convert_color,
-    terminal_settings::TerminalSettings,
-    IndexedCell,
-    Terminal,
-    TerminalContent,
-    TerminalSize,
-};
-use theme::ThemeSettings;
-
-use std::mem;
-use std::{fmt::Debug, ops::RangeInclusive};
-
-use crate::TerminalView;
-
-///The information generated during layout that is necessary for painting
-pub struct LayoutState {
-    cells: Vec<LayoutCell>,
-    rects: Vec<LayoutRect>,
-    relative_highlighted_ranges: Vec<(RangeInclusive<AlacPoint>, Hsla)>,
-    cursor: Option<Cursor>,
-    background_color: Hsla,
-    size: TerminalSize,
-    mode: TermMode,
-    display_offset: usize,
-    hyperlink_tooltip: Option<AnyElement<TerminalView>>,
-    gutter: f32,
-}
-
-///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
-struct DisplayCursor {
-    line: i32,
-    col: usize,
-}
-
-impl DisplayCursor {
-    fn from(cursor_point: AlacPoint, display_offset: usize) -> Self {
-        Self {
-            line: cursor_point.line.0 + display_offset as i32,
-            col: cursor_point.column.0,
-        }
-    }
-
-    pub fn line(&self) -> i32 {
-        self.line
-    }
-
-    pub fn col(&self) -> usize {
-        self.col
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-struct LayoutCell {
-    point: AlacPoint<i32, i32>,
-    text: Line,
-}
-
-impl LayoutCell {
-    fn new(point: AlacPoint<i32, i32>, text: Line) -> LayoutCell {
-        LayoutCell { point, text }
-    }
-
-    fn paint(
-        &self,
-        origin: Point<Pixels>,
-        layout: &LayoutState,
-        _visible_bounds: Bounds<Pixels>,
-        _view: &mut TerminalView,
-        cx: &mut WindowContext,
-    ) {
-        let pos = {
-            let point = self.point;
-
-            Point::new(
-                (origin.x + point.column as f32 * layout.size.cell_width).floor(),
-                origin.y + point.line as f32 * layout.size.line_height,
-            )
-        };
-
-        self.text.paint(pos, layout.size.line_height, cx);
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-struct LayoutRect {
-    point: AlacPoint<i32, i32>,
-    num_of_cells: usize,
-    color: Hsla,
-}
-
-impl LayoutRect {
-    fn new(point: AlacPoint<i32, i32>, num_of_cells: usize, color: Hsla) -> LayoutRect {
-        LayoutRect {
-            point,
-            num_of_cells,
-            color,
-        }
-    }
-
-    fn extend(&self) -> Self {
-        LayoutRect {
-            point: self.point,
-            num_of_cells: self.num_of_cells + 1,
-            color: self.color,
-        }
-    }
-
-    fn paint(
-        &self,
-        origin: Point<Pixels>,
-        layout: &LayoutState,
-        _view: &mut TerminalView,
-        cx: &mut ViewContext<TerminalView>,
-    ) {
-        let position = {
-            let point = self.point;
-            vec2f(
-                (origin.x() + point.column as f32 * layout.size.cell_width).floor(),
-                origin.y() + point.line as f32 * layout.size.line_height,
-            )
-        };
-        let size = vec2f(
-            (layout.size.cell_width * self.num_of_cells as f32).ceil(),
-            layout.size.line_height,
-        );
-
-        cx.paint_quad(
-            Bounds::new(position, size),
-            Default::default(),
-            self.color,
-            Default::default(),
-            Default::default(),
-        );
-    }
-}
-
-///The GPUI element that paints the terminal.
-///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?
-pub struct TerminalElement {
-    terminal: WeakModel<Terminal>,
-    focused: bool,
-    cursor_visible: bool,
-    can_navigate_to_selected_word: bool,
-}
-
-impl TerminalElement {
-    pub fn new(
-        terminal: WeakModel<Terminal>,
-        focused: bool,
-        cursor_visible: bool,
-        can_navigate_to_selected_word: bool,
-    ) -> TerminalElement {
-        TerminalElement {
-            terminal,
-            focused,
-            cursor_visible,
-            can_navigate_to_selected_word,
-        }
-    }
-
-    //Vec<Range<AlacPoint>> -> Clip out the parts of the ranges
-
-    fn layout_grid(
-        grid: &Vec<IndexedCell>,
-        text_style: &TextStyle,
-        terminal_theme: &TerminalStyle,
-        text_layout_cache: &TextLayoutCache,
-        font_cache: &FontCache,
-        hyperlink: Option<(HighlightStyle, &RangeInclusive<AlacPoint>)>,
-    ) -> (Vec<LayoutCell>, Vec<LayoutRect>) {
-        let mut cells = vec![];
-        let mut rects = vec![];
-
-        let mut cur_rect: Option<LayoutRect> = None;
-        let mut cur_alac_color = None;
-
-        let linegroups = grid.into_iter().group_by(|i| i.point.line);
-        for (line_index, (_, line)) in linegroups.into_iter().enumerate() {
-            for cell in line {
-                let mut fg = cell.fg;
-                let mut bg = cell.bg;
-                if cell.flags.contains(Flags::INVERSE) {
-                    mem::swap(&mut fg, &mut bg);
-                }
-
-                //Expand background rect range
-                {
-                    if matches!(bg, Named(NamedColor::Background)) {
-                        //Continue to next cell, resetting variables if necessary
-                        cur_alac_color = None;
-                        if let Some(rect) = cur_rect {
-                            rects.push(rect);
-                            cur_rect = None
-                        }
-                    } else {
-                        match cur_alac_color {
-                            Some(cur_color) => {
-                                if bg == cur_color {
-                                    cur_rect = cur_rect.take().map(|rect| rect.extend());
-                                } else {
-                                    cur_alac_color = Some(bg);
-                                    if cur_rect.is_some() {
-                                        rects.push(cur_rect.take().unwrap());
-                                    }
-                                    cur_rect = Some(LayoutRect::new(
-                                        AlacPoint::new(
-                                            line_index as i32,
-                                            cell.point.column.0 as i32,
-                                        ),
-                                        1,
-                                        convert_color(&bg, &terminal_theme),
-                                    ));
-                                }
-                            }
-                            None => {
-                                cur_alac_color = Some(bg);
-                                cur_rect = Some(LayoutRect::new(
-                                    AlacPoint::new(line_index as i32, cell.point.column.0 as i32),
-                                    1,
-                                    convert_color(&bg, &terminal_theme),
-                                ));
-                            }
-                        }
-                    }
-                }
-
-                //Layout current cell text
-                {
-                    let cell_text = &cell.c.to_string();
-                    if !is_blank(&cell) {
-                        let cell_style = TerminalElement::cell_style(
-                            &cell,
-                            fg,
-                            terminal_theme,
-                            text_style,
-                            font_cache,
-                            hyperlink,
-                        );
-
-                        let layout_cell = text_layout_cache.layout_str(
-                            cell_text,
-                            text_style.font_size,
-                            &[(cell_text.len(), cell_style)],
-                        );
-
-                        cells.push(LayoutCell::new(
-                            AlacPoint::new(line_index as i32, cell.point.column.0 as i32),
-                            layout_cell,
-                        ))
-                    };
-                }
-            }
-
-            if cur_rect.is_some() {
-                rects.push(cur_rect.take().unwrap());
-            }
-        }
-        (cells, rects)
-    }
-
-    // Compute the cursor position and expected block width, may return a zero width if x_for_index returns
-    // the same position for sequential indexes. Use em_width instead
-    fn shape_cursor(
-        cursor_point: DisplayCursor,
-        size: TerminalSize,
-        text_fragment: &Line,
-    ) -> Option<(Vector2F, f32)> {
-        if cursor_point.line() < size.total_lines() as i32 {
-            let cursor_width = if text_fragment.width == 0. {
-                size.cell_width()
-            } else {
-                text_fragment.width
-            };
-
-            //Cursor should always surround as much of the text as possible,
-            //hence when on pixel boundaries round the origin down and the width up
-            Some((
-                vec2f(
-                    (cursor_point.col() as f32 * size.cell_width()).floor(),
-                    (cursor_point.line() as f32 * size.line_height()).floor(),
-                ),
-                cursor_width.ceil(),
-            ))
-        } else {
-            None
-        }
-    }
-
-    ///Convert the Alacritty cell styles to GPUI text styles and background color
-    fn cell_style(
-        indexed: &IndexedCell,
-        fg: terminal::alacritty_terminal::ansi::Color,
-        style: &TerminalStyle,
-        text_style: &TextStyle,
-        font_cache: &FontCache,
-        hyperlink: Option<(HighlightStyle, &RangeInclusive<AlacPoint>)>,
-    ) -> RunStyle {
-        let flags = indexed.cell.flags;
-        let fg = convert_color(&fg, &style);
-
-        let mut underline = flags
-            .intersects(Flags::ALL_UNDERLINES)
-            .then(|| Underline {
-                color: Some(fg),
-                squiggly: flags.contains(Flags::UNDERCURL),
-                thickness: OrderedFloat(1.),
-            })
-            .unwrap_or_default();
-
-        if indexed.cell.hyperlink().is_some() {
-            if underline.thickness == OrderedFloat(0.) {
-                underline.thickness = OrderedFloat(1.);
-            }
-        }
-
-        let mut properties = Properties::new();
-        if indexed.flags.intersects(Flags::BOLD | Flags::DIM_BOLD) {
-            properties = *properties.weight(Weight::BOLD);
-        }
-        if indexed.flags.intersects(Flags::ITALIC) {
-            properties = *properties.style(Italic);
-        }
-
-        let font_id = font_cache
-            .select_font(text_style.font_family_id, &properties)
-            .unwrap_or(8text_style.font_id);
-
-        let mut result = RunStyle {
-            color: fg,
-            font_id,
-            underline,
-        };
-
-        if let Some((style, range)) = hyperlink {
-            if range.contains(&indexed.point) {
-                if let Some(underline) = style.underline {
-                    result.underline = underline;
-                }
-
-                if let Some(color) = style.color {
-                    result.color = color;
-                }
-            }
-        }
-
-        result
-    }
-
-    fn generic_button_handler<E>(
-        connection: WeakModel<Terminal>,
-        origin: Point<Pixels>,
-        f: impl Fn(&mut Terminal, Vector2F, E, &mut ModelContext<Terminal>),
-    ) -> impl Fn(E, &mut TerminalView, &mut EventContext<TerminalView>) {
-        move |event, _: &mut TerminalView, cx| {
-            cx.focus_parent();
-            if let Some(conn_handle) = connection.upgrade() {
-                conn_handle.update(cx, |terminal, cx| {
-                    f(terminal, origin, event, cx);
-
-                    cx.notify();
-                })
-            }
-        }
-    }
-
-    fn attach_mouse_handlers(
-        &self,
-        origin: Point<Pixels>,
-        visible_bounds: Bounds<Pixels>,
-        mode: TermMode,
-        cx: &mut ViewContext<TerminalView>,
-    ) {
-        let connection = self.terminal;
-
-        let mut region = MouseRegion::new::<Self>(cx.view_id(), 0, visible_bounds);
-
-        // Terminal Emulator controlled behavior:
-        region = region
-            // Start selections
-            .on_down(MouseButton::Left, move |event, v: &mut TerminalView, cx| {
-                let terminal_view = cx.handle();
-                cx.focus(&terminal_view);
-                v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
-                if let Some(conn_handle) = connection.upgrade() {
-                    conn_handle.update(cx, |terminal, cx| {
-                        terminal.mouse_down(&event, origin);
-
-                        cx.notify();
-                    })
-                }
-            })
-            // Update drag selections
-            .on_drag(MouseButton::Left, move |event, _: &mut TerminalView, cx| {
-                if event.end {
-                    return;
-                }
-
-                if cx.is_self_focused() {
-                    if let Some(conn_handle) = connection.upgrade() {
-                        conn_handle.update(cx, |terminal, cx| {
-                            terminal.mouse_drag(event, origin);
-                            cx.notify();
-                        })
-                    }
-                }
-            })
-            // Copy on up behavior
-            .on_up(
-                MouseButton::Left,
-                TerminalElement::generic_button_handler(
-                    connection,
-                    origin,
-                    move |terminal, origin, e, cx| {
-                        terminal.mouse_up(&e, origin, cx);
-                    },
-                ),
-            )
-            // Context menu
-            .on_click(
-                MouseButton::Right,
-                move |event, view: &mut TerminalView, cx| {
-                    let mouse_mode = if let Some(conn_handle) = connection.upgrade() {
-                        conn_handle.update(cx, |terminal, _cx| terminal.mouse_mode(event.shift))
-                    } else {
-                        // If we can't get the model handle, probably can't deploy the context menu
-                        true
-                    };
-                    if !mouse_mode {
-                        view.deploy_context_menu(event.position, cx);
-                    }
-                },
-            )
-            .on_move(move |event, _: &mut TerminalView, cx| {
-                if cx.is_self_focused() {
-                    if let Some(conn_handle) = connection.upgrade() {
-                        conn_handle.update(cx, |terminal, cx| {
-                            terminal.mouse_move(&event, origin);
-                            cx.notify();
-                        })
-                    }
-                }
-            })
-            .on_scroll(move |event, _: &mut TerminalView, cx| {
-                if let Some(conn_handle) = connection.upgrade() {
-                    conn_handle.update(cx, |terminal, cx| {
-                        terminal.scroll_wheel(event, origin);
-                        cx.notify();
-                    })
-                }
-            });
-
-        // Mouse mode handlers:
-        // All mouse modes need the extra click handlers
-        if mode.intersects(TermMode::MOUSE_MODE) {
-            region = region
-                .on_down(
-                    MouseButton::Right,
-                    TerminalElement::generic_button_handler(
-                        connection,
-                        origin,
-                        move |terminal, origin, e, _cx| {
-                            terminal.mouse_down(&e, origin);
-                        },
-                    ),
-                )
-                .on_down(
-                    MouseButton::Middle,
-                    TerminalElement::generic_button_handler(
-                        connection,
-                        origin,
-                        move |terminal, origin, e, _cx| {
-                            terminal.mouse_down(&e, origin);
-                        },
-                    ),
-                )
-                .on_up(
-                    MouseButton::Right,
-                    TerminalElement::generic_button_handler(
-                        connection,
-                        origin,
-                        move |terminal, origin, e, cx| {
-                            terminal.mouse_up(&e, origin, cx);
-                        },
-                    ),
-                )
-                .on_up(
-                    MouseButton::Middle,
-                    TerminalElement::generic_button_handler(
-                        connection,
-                        origin,
-                        move |terminal, origin, e, cx| {
-                            terminal.mouse_up(&e, origin, cx);
-                        },
-                    ),
-                )
-        }
-
-        cx.scene().push_mouse_region(region);
-    }
-}
-
-impl Element<TerminalView> for TerminalElement {
-    type ElementState = LayoutState;
-
-    fn layout(
-        &mut self,
-        view_state: &mut TerminalView,
-        element_state: &mut Self::ElementState,
-        cx: &mut ViewContext<TerminalView>,
-    ) -> LayoutId {
-        let settings = ThemeSettings::get_global(cx);
-        let terminal_settings = TerminalSettings::get_global(cx);
-
-        //Setup layout information
-        let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
-        let link_style = settings.theme.editor.link_definition;
-        let tooltip_style = settings.theme.tooltip.clone();
-
-        let font_cache = cx.font_cache();
-        let font_size = font_size(&terminal_settings, cx).unwrap_or(settings.buffer_font_size(cx));
-        let font_family_name = terminal_settings
-            .font_family
-            .as_ref()
-            .unwrap_or(&settings.buffer_font_family_name);
-        let font_features = terminal_settings
-            .font_features
-            .as_ref()
-            .unwrap_or(&settings.buffer_font_features);
-        let family_id = font_cache
-            .load_family(&[font_family_name], &font_features)
-            .log_err()
-            .unwrap_or(settings.buffer_font_family);
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-
-        let text_style = TextStyle {
-            color: settings.theme.editor.text_color,
-            font_family_id: family_id,
-            font_family_name: font_cache.family_name(family_id).unwrap(),
-            font_id,
-            font_size,
-            font_properties: Default::default(),
-            underline: Default::default(),
-            soft_wrap: false,
-        };
-        let selection_color = settings.theme.editor.selection.selection;
-        let match_color = settings.theme.search.match_background;
-        let gutter;
-        let dimensions = {
-            let line_height = text_style.font_size * terminal_settings.line_height.value();
-            let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size);
-            gutter = cell_width;
-
-            let size = constraint.max - vec2f(gutter, 0.);
-            TerminalSize::new(line_height, cell_width, size)
-        };
-
-        let search_matches = if let Some(terminal_model) = self.terminal.upgrade() {
-            terminal_model.read(cx).matches.clone()
-        } else {
-            Default::default()
-        };
-
-        let background_color = terminal_theme.background;
-        let terminal_handle = self.terminal.upgrade().unwrap();
-
-        let last_hovered_word = terminal_handle.update(cx, |terminal, cx| {
-            terminal.set_size(dimensions);
-            terminal.try_sync(cx);
-            if self.can_navigate_to_selected_word && terminal.can_navigate_to_selected_word() {
-                terminal.last_content.last_hovered_word.clone()
-            } else {
-                None
-            }
-        });
-
-        let hyperlink_tooltip = last_hovered_word.clone().map(|hovered_word| {
-            let mut tooltip = Overlay::new(
-                Empty::new()
-                    .contained()
-                    .constrained()
-                    .with_width(dimensions.width())
-                    .with_height(dimensions.height())
-                    .with_tooltip::<TerminalElement>(
-                        hovered_word.id,
-                        hovered_word.word,
-                        None,
-                        tooltip_style,
-                        cx,
-                    ),
-            )
-            .with_position_mode(gpui::elements::OverlayPositionMode::Local)
-            .into_any();
-
-            tooltip.layout(
-                SizeConstraint::new(Vector2F::zero(), cx.window_size()),
-                view_state,
-                cx,
-            );
-            tooltip
-        });
-
-        let TerminalContent {
-            cells,
-            mode,
-            display_offset,
-            cursor_char,
-            selection,
-            cursor,
-            ..
-        } = { &terminal_handle.read(cx).last_content };
-
-        // searches, highlights to a single range representations
-        let mut relative_highlighted_ranges = Vec::new();
-        for search_match in search_matches {
-            relative_highlighted_ranges.push((search_match, match_color))
-        }
-        if let Some(selection) = selection {
-            relative_highlighted_ranges.push((selection.start..=selection.end, selection_color));
-        }
-
-        // then have that representation be converted to the appropriate highlight data structure
-
-        let (cells, rects) = TerminalElement::layout_grid(
-            cells,
-            &text_style,
-            &terminal_theme,
-            cx.text_layout_cache(),
-            cx.font_cache(),
-            last_hovered_word
-                .as_ref()
-                .map(|last_hovered_word| (link_style, &last_hovered_word.word_match)),
-        );
-
-        //Layout cursor. Rectangle is used for IME, so we should lay it out even
-        //if we don't end up showing it.
-        let cursor = if let AlacCursorShape::Hidden = cursor.shape {
-            None
-        } else {
-            let cursor_point = DisplayCursor::from(cursor.point, *display_offset);
-            let cursor_text = {
-                let str_trxt = cursor_char.to_string();
-
-                let color = if self.focused {
-                    terminal_theme.background
-                } else {
-                    terminal_theme.foreground
-                };
-
-                cx.text_layout_cache().layout_str(
-                    &str_trxt,
-                    text_style.font_size,
-                    &[(
-                        str_trxt.len(),
-                        RunStyle {
-                            font_id: text_style.font_id,
-                            color,
-                            underline: Default::default(),
-                        },
-                    )],
-                )
-            };
-
-            let focused = self.focused;
-            TerminalElement::shape_cursor(cursor_point, dimensions, &cursor_text).map(
-                move |(cursor_position, block_width)| {
-                    let (shape, text) = match cursor.shape {
-                        AlacCursorShape::Block if !focused => (CursorShape::Hollow, None),
-                        AlacCursorShape::Block => (CursorShape::Block, Some(cursor_text)),
-                        AlacCursorShape::Underline => (CursorShape::Underscore, None),
-                        AlacCursorShape::Beam => (CursorShape::Bar, None),
-                        AlacCursorShape::HollowBlock => (CursorShape::Hollow, None),
-                        //This case is handled in the if wrapping the whole cursor layout
-                        AlacCursorShape::Hidden => unreachable!(),
-                    };
-
-                    Cursor::new(
-                        cursor_position,
-                        block_width,
-                        dimensions.line_height,
-                        terminal_theme.cursor,
-                        shape,
-                        text,
-                    )
-                },
-            )
-        };
-
-        //Done!
-        (
-            constraint.max,
-            Self::ElementState {
-                cells,
-                cursor,
-                background_color,
-                size: dimensions,
-                rects,
-                relative_highlighted_ranges,
-                mode: *mode,
-                display_offset: *display_offset,
-                hyperlink_tooltip,
-                gutter,
-            },
-        )
-    }
-
-    fn paint(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        view_state: &mut TerminalView,
-        element_state: &mut Self::ElementState,
-        cx: &mut ViewContext<TerminalView>,
-    ) {
-        let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
-
-        //Setup element stuff
-        let clip_bounds = Some(visible_bounds);
-
-        cx.paint_layer(clip_bounds, |cx| {
-            let origin = bounds.origin() + vec2f(element_state.gutter, 0.);
-
-            // Elements are ephemeral, only at paint time do we know what could be clicked by a mouse
-            self.attach_mouse_handlers(origin, visible_bounds, element_state.mode, cx);
-
-            cx.scene().push_cursor_region(gpui::CursorRegion {
-                bounds,
-                style: if element_state.hyperlink_tooltip.is_some() {
-                    CursorStyle::AlacPointingHand
-                } else {
-                    CursorStyle::IBeam
-                },
-            });
-
-            cx.paint_layer(clip_bounds, |cx| {
-                //Start with a background color
-                cx.scene().push_quad(Quad {
-                    bounds,
-                    background: Some(element_state.background_color),
-                    border: Default::default(),
-                    corner_radii: Default::default(),
-                });
-
-                for rect in &element_state.rects {
-                    rect.paint(origin, element_state, view_state, cx);
-                }
-            });
-
-            //Draw Highlighted Backgrounds
-            cx.paint_layer(clip_bounds, |cx| {
-                for (relative_highlighted_range, color) in
-                    element_state.relative_highlighted_ranges.iter()
-                {
-                    if let Some((start_y, highlighted_range_lines)) = to_highlighted_range_lines(
-                        relative_highlighted_range,
-                        element_state,
-                        origin,
-                    ) {
-                        let hr = HighlightedRange {
-                            start_y, //Need to change this
-                            line_height: element_state.size.line_height,
-                            lines: highlighted_range_lines,
-                            color: color.clone(),
-                            //Copied from editor. TODO: move to theme or something
-                            corner_radius: 0.15 * element_state.size.line_height,
-                        };
-                        hr.paint(bounds, cx);
-                    }
-                }
-            });
-
-            //Draw the text cells
-            cx.paint_layer(clip_bounds, |cx| {
-                for cell in &element_state.cells {
-                    cell.paint(origin, element_state, visible_bounds, view_state, cx);
-                }
-            });
-
-            //Draw cursor
-            if self.cursor_visible {
-                if let Some(cursor) = &element_state.cursor {
-                    cx.paint_layer(clip_bounds, |cx| {
-                        cursor.paint(origin, cx);
-                    })
-                }
-            }
-
-            if let Some(element) = &mut element_state.hyperlink_tooltip {
-                element.paint(origin, visible_bounds, view_state, cx)
-            }
-        });
-    }
-
-    fn id(&self) -> Option<gpui::ElementId> {
-        todo!()
-    }
-
-    fn initialize(
-        &mut self,
-        view_state: &mut TerminalView,
-        element_state: Option<Self::ElementState>,
-        cx: &mut ViewContext<TerminalView>,
-    ) -> Self::ElementState {
-        todo!()
-    }
-
-    // todo!() remove?
-    // fn metadata(&self) -> Option<&dyn std::any::Any> {
-    //     None
-    // }
-
-    // fn debug(
-    //     &self,
-    //     _: Bounds<Pixels>,
-    //     _: &Self::ElementState,
-    //     _: &Self::PaintState,
-    //     _: &TerminalView,
-    //     _: &gpui::ViewContext<TerminalView>,
-    // ) -> gpui::serde_json::Value {
-    //     json!({
-    //         "type": "TerminalElement",
-    //     })
-    // }
-
-    // fn rect_for_text_range(
-    //     &self,
-    //     _: Range<usize>,
-    //     bounds: Bounds<Pixels>,
-    //     _: Bounds<Pixels>,
-    //     layout: &Self::ElementState,
-    //     _: &Self::PaintState,
-    //     _: &TerminalView,
-    //     _: &gpui::ViewContext<TerminalView>,
-    // ) -> Option<Bounds<Pixels>> {
-    //     // Use the same origin that's passed to `Cursor::paint` in the paint
-    //     // method bove.
-    //     let mut origin = bounds.origin() + vec2f(layout.size.cell_width, 0.);
-
-    //     // TODO - Why is it necessary to move downward one line to get correct
-    //     // positioning? I would think that we'd want the same rect that is
-    //     // painted for the cursor.
-    //     origin += vec2f(0., layout.size.line_height);
-
-    //     Some(layout.cursor.as_ref()?.bounding_rect(origin))
-    // }
-}
-
-impl Component<TerminalView> for TerminalElement {
-    fn render(self) -> AnyElement<TerminalView> {
-        todo!()
-    }
-}
-
-fn is_blank(cell: &IndexedCell) -> bool {
-    if cell.c != ' ' {
-        return false;
-    }
-
-    if cell.bg != AnsiColor::Named(NamedColor::Background) {
-        return false;
-    }
-
-    if cell.hyperlink().is_some() {
-        return false;
-    }
-
-    if cell
-        .flags
-        .intersects(Flags::ALL_UNDERLINES | Flags::INVERSE | Flags::STRIKEOUT)
-    {
-        return false;
-    }
-
-    return true;
-}
-
-fn to_highlighted_range_lines(
-    range: &RangeInclusive<AlacPoint>,
-    layout: &LayoutState,
-    origin: Point<Pixels>,
-) -> Option<(f32, Vec<HighlightedRangeLine>)> {
-    // Step 1. Normalize the points to be viewport relative.
-    // When display_offset = 1, here's how the grid is arranged:
-    //-2,0 -2,1...
-    //--- Viewport top
-    //-1,0 -1,1...
-    //--------- Terminal Top
-    // 0,0  0,1...
-    // 1,0  1,1...
-    //--- Viewport Bottom
-    // 2,0  2,1...
-    //--------- Terminal Bottom
-
-    // Normalize to viewport relative, from terminal relative.
-    // lines are i32s, which are negative above the top left corner of the terminal
-    // If the user has scrolled, we use the display_offset to tell us which offset
-    // of the grid data we should be looking at. But for the rendering step, we don't
-    // want negatives. We want things relative to the 'viewport' (the area of the grid
-    // which is currently shown according to the display offset)
-    let unclamped_start = AlacPoint::new(
-        range.start().line + layout.display_offset,
-        range.start().column,
-    );
-    let unclamped_end =
-        AlacPoint::new(range.end().line + layout.display_offset, range.end().column);
-
-    // Step 2. Clamp range to viewport, and return None if it doesn't overlap
-    if unclamped_end.line.0 < 0 || unclamped_start.line.0 > layout.size.num_lines() as i32 {
-        return None;
-    }
-
-    let clamped_start_line = unclamped_start.line.0.max(0) as usize;
-    let clamped_end_line = unclamped_end.line.0.min(layout.size.num_lines() as i32) as usize;
-    //Convert the start of the range to pixels
-    let start_y = origin.y + clamped_start_line as f32 * layout.size.line_height;
-
-    // Step 3. Expand ranges that cross lines into a collection of single-line ranges.
-    //  (also convert to pixels)
-    let mut highlighted_range_lines = Vec::new();
-    for line in clamped_start_line..=clamped_end_line {
-        let mut line_start = 0;
-        let mut line_end = layout.size.columns();
-
-        if line == clamped_start_line {
-            line_start = unclamped_start.column.0 as usize;
-        }
-        if line == clamped_end_line {
-            line_end = unclamped_end.column.0 as usize + 1; //+1 for inclusive
-        }
-
-        highlighted_range_lines.push(HighlightedRangeLine {
-            start_x: origin.x() + line_start as f32 * layout.size.cell_width,
-            end_x: origin.x() + line_end as f32 * layout.size.cell_width,
-        });
-    }
-
-    Some((start_y, highlighted_range_lines))
-}
-
-fn font_size(terminal_settings: &TerminalSettings, cx: &mut AppContext) -> Option<Pixels> {
-    terminal_settings
-        .font_size
-        .map(|size| theme::adjusted_font_size(size, cx))
-}
+// use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
+// use gpui::{
+//     AnyElement, AppContext, Bounds, Component, Element, HighlightStyle, Hsla, LayoutId, Line,
+//     ModelContext, MouseButton, Pixels, Point, TextStyle, Underline, ViewContext, WeakModel,
+//     WindowContext,
+// };
+// use itertools::Itertools;
+// use language::CursorShape;
+// use ordered_float::OrderedFloat;
+// use settings::Settings;
+// use terminal::{
+//     alacritty_terminal::{
+//         ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
+//         grid::Dimensions,
+//         index::Point as AlacPoint,
+//         term::{cell::Flags, TermMode},
+//     },
+//     // mappings::colors::convert_color,
+//     terminal_settings::TerminalSettings,
+//     IndexedCell,
+//     Terminal,
+//     TerminalContent,
+//     TerminalSize,
+// };
+// use theme::ThemeSettings;
+
+// use std::mem;
+// use std::{fmt::Debug, ops::RangeInclusive};
+
+// use crate::TerminalView;
+
+// ///The information generated during layout that is necessary for painting
+// pub struct LayoutState {
+//     cells: Vec<LayoutCell>,
+//     rects: Vec<LayoutRect>,
+//     relative_highlighted_ranges: Vec<(RangeInclusive<AlacPoint>, Hsla)>,
+//     cursor: Option<Cursor>,
+//     background_color: Hsla,
+//     size: TerminalSize,
+//     mode: TermMode,
+//     display_offset: usize,
+//     hyperlink_tooltip: Option<AnyElement<TerminalView>>,
+//     gutter: f32,
+// }
+
+// ///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
+// struct DisplayCursor {
+//     line: i32,
+//     col: usize,
+// }
+
+// impl DisplayCursor {
+//     fn from(cursor_point: AlacPoint, display_offset: usize) -> Self {
+//         Self {
+//             line: cursor_point.line.0 + display_offset as i32,
+//             col: cursor_point.column.0,
+//         }
+//     }
+
+//     pub fn line(&self) -> i32 {
+//         self.line
+//     }
+
+//     pub fn col(&self) -> usize {
+//         self.col
+//     }
+// }
+
+// #[derive(Clone, Debug, Default)]
+// struct LayoutCell {
+//     point: AlacPoint<i32, i32>,
+//     text: Line,
+// }
+
+// impl LayoutCell {
+//     fn new(point: AlacPoint<i32, i32>, text: Line) -> LayoutCell {
+//         LayoutCell { point, text }
+//     }
+
+//     fn paint(
+//         &self,
+//         origin: Point<Pixels>,
+//         layout: &LayoutState,
+//         _visible_bounds: Bounds<Pixels>,
+//         _view: &mut TerminalView,
+//         cx: &mut WindowContext,
+//     ) {
+//         let pos = {
+//             let point = self.point;
+
+//             Point::new(
+//                 (origin.x + point.column as f32 * layout.size.cell_width).floor(),
+//                 origin.y + point.line as f32 * layout.size.line_height,
+//             )
+//         };
+
+//         self.text.paint(pos, layout.size.line_height, cx);
+//     }
+// }
+
+// #[derive(Clone, Debug, Default)]
+// struct LayoutRect {
+//     point: AlacPoint<i32, i32>,
+//     num_of_cells: usize,
+//     color: Hsla,
+// }
+
+// impl LayoutRect {
+//     fn new(point: AlacPoint<i32, i32>, num_of_cells: usize, color: Hsla) -> LayoutRect {
+//         LayoutRect {
+//             point,
+//             num_of_cells,
+//             color,
+//         }
+//     }
+
+//     fn extend(&self) -> Self {
+//         LayoutRect {
+//             point: self.point,
+//             num_of_cells: self.num_of_cells + 1,
+//             color: self.color,
+//         }
+//     }
+
+//     fn paint(
+//         &self,
+//         origin: Point<Pixels>,
+//         layout: &LayoutState,
+//         _view: &mut TerminalView,
+//         cx: &mut ViewContext<TerminalView>,
+//     ) {
+//         let position = {
+//             let point = self.point;
+//             vec2f(
+//                 (origin.x() + point.column as f32 * layout.size.cell_width).floor(),
+//                 origin.y() + point.line as f32 * layout.size.line_height,
+//             )
+//         };
+//         let size = vec2f(
+//             (layout.size.cell_width * self.num_of_cells as f32).ceil(),
+//             layout.size.line_height,
+//         );
+
+//         cx.paint_quad(
+//             Bounds::new(position, size),
+//             Default::default(),
+//             self.color,
+//             Default::default(),
+//             Default::default(),
+//         );
+//     }
+// }
+
+// ///The GPUI element that paints the terminal.
+// ///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?
+// pub struct TerminalElement {
+//     terminal: WeakModel<Terminal>,
+//     focused: bool,
+//     cursor_visible: bool,
+//     can_navigate_to_selected_word: bool,
+// }
+
+// impl TerminalElement {
+//     pub fn new(
+//         terminal: WeakModel<Terminal>,
+//         focused: bool,
+//         cursor_visible: bool,
+//         can_navigate_to_selected_word: bool,
+//     ) -> TerminalElement {
+//         TerminalElement {
+//             terminal,
+//             focused,
+//             cursor_visible,
+//             can_navigate_to_selected_word,
+//         }
+//     }
+
+//     //Vec<Range<AlacPoint>> -> Clip out the parts of the ranges
+
+//     fn layout_grid(
+//         grid: &Vec<IndexedCell>,
+//         text_style: &TextStyle,
+//         terminal_theme: &TerminalStyle,
+//         text_layout_cache: &TextLayoutCache,
+//         font_cache: &FontCache,
+//         hyperlink: Option<(HighlightStyle, &RangeInclusive<AlacPoint>)>,
+//     ) -> (Vec<LayoutCell>, Vec<LayoutRect>) {
+//         let mut cells = vec![];
+//         let mut rects = vec![];
+
+//         let mut cur_rect: Option<LayoutRect> = None;
+//         let mut cur_alac_color = None;
+
+//         let linegroups = grid.into_iter().group_by(|i| i.point.line);
+//         for (line_index, (_, line)) in linegroups.into_iter().enumerate() {
+//             for cell in line {
+//                 let mut fg = cell.fg;
+//                 let mut bg = cell.bg;
+//                 if cell.flags.contains(Flags::INVERSE) {
+//                     mem::swap(&mut fg, &mut bg);
+//                 }
+
+//                 //Expand background rect range
+//                 {
+//                     if matches!(bg, Named(NamedColor::Background)) {
+//                         //Continue to next cell, resetting variables if necessary
+//                         cur_alac_color = None;
+//                         if let Some(rect) = cur_rect {
+//                             rects.push(rect);
+//                             cur_rect = None
+//                         }
+//                     } else {
+//                         match cur_alac_color {
+//                             Some(cur_color) => {
+//                                 if bg == cur_color {
+//                                     cur_rect = cur_rect.take().map(|rect| rect.extend());
+//                                 } else {
+//                                     cur_alac_color = Some(bg);
+//                                     if cur_rect.is_some() {
+//                                         rects.push(cur_rect.take().unwrap());
+//                                     }
+//                                     cur_rect = Some(LayoutRect::new(
+//                                         AlacPoint::new(
+//                                             line_index as i32,
+//                                             cell.point.column.0 as i32,
+//                                         ),
+//                                         1,
+//                                         convert_color(&bg, &terminal_theme),
+//                                     ));
+//                                 }
+//                             }
+//                             None => {
+//                                 cur_alac_color = Some(bg);
+//                                 cur_rect = Some(LayoutRect::new(
+//                                     AlacPoint::new(line_index as i32, cell.point.column.0 as i32),
+//                                     1,
+//                                     convert_color(&bg, &terminal_theme),
+//                                 ));
+//                             }
+//                         }
+//                     }
+//                 }
+
+//                 //Layout current cell text
+//                 {
+//                     let cell_text = &cell.c.to_string();
+//                     if !is_blank(&cell) {
+//                         let cell_style = TerminalElement::cell_style(
+//                             &cell,
+//                             fg,
+//                             terminal_theme,
+//                             text_style,
+//                             font_cache,
+//                             hyperlink,
+//                         );
+
+//                         let layout_cell = text_layout_cache.layout_str(
+//                             cell_text,
+//                             text_style.font_size,
+//                             &[(cell_text.len(), cell_style)],
+//                         );
+
+//                         cells.push(LayoutCell::new(
+//                             AlacPoint::new(line_index as i32, cell.point.column.0 as i32),
+//                             layout_cell,
+//                         ))
+//                     };
+//                 }
+//             }
+
+//             if cur_rect.is_some() {
+//                 rects.push(cur_rect.take().unwrap());
+//             }
+//         }
+//         (cells, rects)
+//     }
+
+//     // Compute the cursor position and expected block width, may return a zero width if x_for_index returns
+//     // the same position for sequential indexes. Use em_width instead
+//     fn shape_cursor(
+//         cursor_point: DisplayCursor,
+//         size: TerminalSize,
+//         text_fragment: &Line,
+//     ) -> Option<(Vector2F, f32)> {
+//         if cursor_point.line() < size.total_lines() as i32 {
+//             let cursor_width = if text_fragment.width == 0. {
+//                 size.cell_width()
+//             } else {
+//                 text_fragment.width
+//             };
+
+//             //Cursor should always surround as much of the text as possible,
+//             //hence when on pixel boundaries round the origin down and the width up
+//             Some((
+//                 vec2f(
+//                     (cursor_point.col() as f32 * size.cell_width()).floor(),
+//                     (cursor_point.line() as f32 * size.line_height()).floor(),
+//                 ),
+//                 cursor_width.ceil(),
+//             ))
+//         } else {
+//             None
+//         }
+//     }
+
+//     ///Convert the Alacritty cell styles to GPUI text styles and background color
+//     fn cell_style(
+//         indexed: &IndexedCell,
+//         fg: terminal::alacritty_terminal::ansi::Color,
+//         style: &TerminalStyle,
+//         text_style: &TextStyle,
+//         font_cache: &FontCache,
+//         hyperlink: Option<(HighlightStyle, &RangeInclusive<AlacPoint>)>,
+//     ) -> RunStyle {
+//         let flags = indexed.cell.flags;
+//         let fg = convert_color(&fg, &style);
+
+//         let mut underline = flags
+//             .intersects(Flags::ALL_UNDERLINES)
+//             .then(|| Underline {
+//                 color: Some(fg),
+//                 squiggly: flags.contains(Flags::UNDERCURL),
+//                 thickness: OrderedFloat(1.),
+//             })
+//             .unwrap_or_default();
+
+//         if indexed.cell.hyperlink().is_some() {
+//             if underline.thickness == OrderedFloat(0.) {
+//                 underline.thickness = OrderedFloat(1.);
+//             }
+//         }
+
+//         let mut properties = Properties::new();
+//         if indexed.flags.intersects(Flags::BOLD | Flags::DIM_BOLD) {
+//             properties = *properties.weight(Weight::BOLD);
+//         }
+//         if indexed.flags.intersects(Flags::ITALIC) {
+//             properties = *properties.style(Italic);
+//         }
+
+//         let font_id = font_cache
+//             .select_font(text_style.font_family_id, &properties)
+//             .unwrap_or(8text_style.font_id);
+
+//         let mut result = RunStyle {
+//             color: fg,
+//             font_id,
+//             underline,
+//         };
+
+//         if let Some((style, range)) = hyperlink {
+//             if range.contains(&indexed.point) {
+//                 if let Some(underline) = style.underline {
+//                     result.underline = underline;
+//                 }
+
+//                 if let Some(color) = style.color {
+//                     result.color = color;
+//                 }
+//             }
+//         }
+
+//         result
+//     }
+
+//     fn generic_button_handler<E>(
+//         connection: WeakModel<Terminal>,
+//         origin: Point<Pixels>,
+//         f: impl Fn(&mut Terminal, Vector2F, E, &mut ModelContext<Terminal>),
+//     ) -> impl Fn(E, &mut TerminalView, &mut EventContext<TerminalView>) {
+//         move |event, _: &mut TerminalView, cx| {
+//             cx.focus_parent();
+//             if let Some(conn_handle) = connection.upgrade() {
+//                 conn_handle.update(cx, |terminal, cx| {
+//                     f(terminal, origin, event, cx);
+
+//                     cx.notify();
+//                 })
+//             }
+//         }
+//     }
+
+//     fn attach_mouse_handlers(
+//         &self,
+//         origin: Point<Pixels>,
+//         visible_bounds: Bounds<Pixels>,
+//         mode: TermMode,
+//         cx: &mut ViewContext<TerminalView>,
+//     ) {
+//         let connection = self.terminal;
+
+//         let mut region = MouseRegion::new::<Self>(cx.view_id(), 0, visible_bounds);
+
+//         // Terminal Emulator controlled behavior:
+//         region = region
+//             // Start selections
+//             .on_down(MouseButton::Left, move |event, v: &mut TerminalView, cx| {
+//                 let terminal_view = cx.handle();
+//                 cx.focus(&terminal_view);
+//                 v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
+//                 if let Some(conn_handle) = connection.upgrade() {
+//                     conn_handle.update(cx, |terminal, cx| {
+//                         terminal.mouse_down(&event, origin);
+
+//                         cx.notify();
+//                     })
+//                 }
+//             })
+//             // Update drag selections
+//             .on_drag(MouseButton::Left, move |event, _: &mut TerminalView, cx| {
+//                 if event.end {
+//                     return;
+//                 }
+
+//                 if cx.is_self_focused() {
+//                     if let Some(conn_handle) = connection.upgrade() {
+//                         conn_handle.update(cx, |terminal, cx| {
+//                             terminal.mouse_drag(event, origin);
+//                             cx.notify();
+//                         })
+//                     }
+//                 }
+//             })
+//             // Copy on up behavior
+//             .on_up(
+//                 MouseButton::Left,
+//                 TerminalElement::generic_button_handler(
+//                     connection,
+//                     origin,
+//                     move |terminal, origin, e, cx| {
+//                         terminal.mouse_up(&e, origin, cx);
+//                     },
+//                 ),
+//             )
+//             // Context menu
+//             .on_click(
+//                 MouseButton::Right,
+//                 move |event, view: &mut TerminalView, cx| {
+//                     let mouse_mode = if let Some(conn_handle) = connection.upgrade() {
+//                         conn_handle.update(cx, |terminal, _cx| terminal.mouse_mode(event.shift))
+//                     } else {
+//                         // If we can't get the model handle, probably can't deploy the context menu
+//                         true
+//                     };
+//                     if !mouse_mode {
+//                         view.deploy_context_menu(event.position, cx);
+//                     }
+//                 },
+//             )
+//             .on_move(move |event, _: &mut TerminalView, cx| {
+//                 if cx.is_self_focused() {
+//                     if let Some(conn_handle) = connection.upgrade() {
+//                         conn_handle.update(cx, |terminal, cx| {
+//                             terminal.mouse_move(&event, origin);
+//                             cx.notify();
+//                         })
+//                     }
+//                 }
+//             })
+//             .on_scroll(move |event, _: &mut TerminalView, cx| {
+//                 if let Some(conn_handle) = connection.upgrade() {
+//                     conn_handle.update(cx, |terminal, cx| {
+//                         terminal.scroll_wheel(event, origin);
+//                         cx.notify();
+//                     })
+//                 }
+//             });
+
+//         // Mouse mode handlers:
+//         // All mouse modes need the extra click handlers
+//         if mode.intersects(TermMode::MOUSE_MODE) {
+//             region = region
+//                 .on_down(
+//                     MouseButton::Right,
+//                     TerminalElement::generic_button_handler(
+//                         connection,
+//                         origin,
+//                         move |terminal, origin, e, _cx| {
+//                             terminal.mouse_down(&e, origin);
+//                         },
+//                     ),
+//                 )
+//                 .on_down(
+//                     MouseButton::Middle,
+//                     TerminalElement::generic_button_handler(
+//                         connection,
+//                         origin,
+//                         move |terminal, origin, e, _cx| {
+//                             terminal.mouse_down(&e, origin);
+//                         },
+//                     ),
+//                 )
+//                 .on_up(
+//                     MouseButton::Right,
+//                     TerminalElement::generic_button_handler(
+//                         connection,
+//                         origin,
+//                         move |terminal, origin, e, cx| {
+//                             terminal.mouse_up(&e, origin, cx);
+//                         },
+//                     ),
+//                 )
+//                 .on_up(
+//                     MouseButton::Middle,
+//                     TerminalElement::generic_button_handler(
+//                         connection,
+//                         origin,
+//                         move |terminal, origin, e, cx| {
+//                             terminal.mouse_up(&e, origin, cx);
+//                         },
+//                     ),
+//                 )
+//         }
+
+//         cx.scene().push_mouse_region(region);
+//     }
+// }
+
+// impl Element<TerminalView> for TerminalElement {
+//     type ElementState = LayoutState;
+
+//     fn layout(
+//         &mut self,
+//         view_state: &mut TerminalView,
+//         element_state: &mut Self::ElementState,
+//         cx: &mut ViewContext<TerminalView>,
+//     ) -> LayoutId {
+//         let settings = ThemeSettings::get_global(cx);
+//         let terminal_settings = TerminalSettings::get_global(cx);
+
+//         //Setup layout information
+//         let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
+//         let link_style = settings.theme.editor.link_definition;
+//         let tooltip_style = settings.theme.tooltip.clone();
+
+//         let font_cache = cx.font_cache();
+//         let font_size = font_size(&terminal_settings, cx).unwrap_or(settings.buffer_font_size(cx));
+//         let font_family_name = terminal_settings
+//             .font_family
+//             .as_ref()
+//             .unwrap_or(&settings.buffer_font_family_name);
+//         let font_features = terminal_settings
+//             .font_features
+//             .as_ref()
+//             .unwrap_or(&settings.buffer_font_features);
+//         let family_id = font_cache
+//             .load_family(&[font_family_name], &font_features)
+//             .log_err()
+//             .unwrap_or(settings.buffer_font_family);
+//         let font_id = font_cache
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+
+//         let text_style = TextStyle {
+//             color: settings.theme.editor.text_color,
+//             font_family_id: family_id,
+//             font_family_name: font_cache.family_name(family_id).unwrap(),
+//             font_id,
+//             font_size,
+//             font_properties: Default::default(),
+//             underline: Default::default(),
+//             soft_wrap: false,
+//         };
+//         let selection_color = settings.theme.editor.selection.selection;
+//         let match_color = settings.theme.search.match_background;
+//         let gutter;
+//         let dimensions = {
+//             let line_height = text_style.font_size * terminal_settings.line_height.value();
+//             let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size);
+//             gutter = cell_width;
+
+//             let size = constraint.max - vec2f(gutter, 0.);
+//             TerminalSize::new(line_height, cell_width, size)
+//         };
+
+//         let search_matches = if let Some(terminal_model) = self.terminal.upgrade() {
+//             terminal_model.read(cx).matches.clone()
+//         } else {
+//             Default::default()
+//         };
+
+//         let background_color = terminal_theme.background;
+//         let terminal_handle = self.terminal.upgrade().unwrap();
+
+//         let last_hovered_word = terminal_handle.update(cx, |terminal, cx| {
+//             terminal.set_size(dimensions);
+//             terminal.try_sync(cx);
+//             if self.can_navigate_to_selected_word && terminal.can_navigate_to_selected_word() {
+//                 terminal.last_content.last_hovered_word.clone()
+//             } else {
+//                 None
+//             }
+//         });
+
+//         let hyperlink_tooltip = last_hovered_word.clone().map(|hovered_word| {
+//             let mut tooltip = Overlay::new(
+//                 Empty::new()
+//                     .contained()
+//                     .constrained()
+//                     .with_width(dimensions.width())
+//                     .with_height(dimensions.height())
+//                     .with_tooltip::<TerminalElement>(
+//                         hovered_word.id,
+//                         hovered_word.word,
+//                         None,
+//                         tooltip_style,
+//                         cx,
+//                     ),
+//             )
+//             .with_position_mode(gpui::elements::OverlayPositionMode::Local)
+//             .into_any();
+
+//             tooltip.layout(
+//                 SizeConstraint::new(Vector2F::zero(), cx.window_size()),
+//                 view_state,
+//                 cx,
+//             );
+//             tooltip
+//         });
+
+//         let TerminalContent {
+//             cells,
+//             mode,
+//             display_offset,
+//             cursor_char,
+//             selection,
+//             cursor,
+//             ..
+//         } = { &terminal_handle.read(cx).last_content };
+
+//         // searches, highlights to a single range representations
+//         let mut relative_highlighted_ranges = Vec::new();
+//         for search_match in search_matches {
+//             relative_highlighted_ranges.push((search_match, match_color))
+//         }
+//         if let Some(selection) = selection {
+//             relative_highlighted_ranges.push((selection.start..=selection.end, selection_color));
+//         }
+
+//         // then have that representation be converted to the appropriate highlight data structure
+
+//         let (cells, rects) = TerminalElement::layout_grid(
+//             cells,
+//             &text_style,
+//             &terminal_theme,
+//             cx.text_layout_cache(),
+//             cx.font_cache(),
+//             last_hovered_word
+//                 .as_ref()
+//                 .map(|last_hovered_word| (link_style, &last_hovered_word.word_match)),
+//         );
+
+//         //Layout cursor. Rectangle is used for IME, so we should lay it out even
+//         //if we don't end up showing it.
+//         let cursor = if let AlacCursorShape::Hidden = cursor.shape {
+//             None
+//         } else {
+//             let cursor_point = DisplayCursor::from(cursor.point, *display_offset);
+//             let cursor_text = {
+//                 let str_trxt = cursor_char.to_string();
+
+//                 let color = if self.focused {
+//                     terminal_theme.background
+//                 } else {
+//                     terminal_theme.foreground
+//                 };
+
+//                 cx.text_layout_cache().layout_str(
+//                     &str_trxt,
+//                     text_style.font_size,
+//                     &[(
+//                         str_trxt.len(),
+//                         RunStyle {
+//                             font_id: text_style.font_id,
+//                             color,
+//                             underline: Default::default(),
+//                         },
+//                     )],
+//                 )
+//             };
+
+//             let focused = self.focused;
+//             TerminalElement::shape_cursor(cursor_point, dimensions, &cursor_text).map(
+//                 move |(cursor_position, block_width)| {
+//                     let (shape, text) = match cursor.shape {
+//                         AlacCursorShape::Block if !focused => (CursorShape::Hollow, None),
+//                         AlacCursorShape::Block => (CursorShape::Block, Some(cursor_text)),
+//                         AlacCursorShape::Underline => (CursorShape::Underscore, None),
+//                         AlacCursorShape::Beam => (CursorShape::Bar, None),
+//                         AlacCursorShape::HollowBlock => (CursorShape::Hollow, None),
+//                         //This case is handled in the if wrapping the whole cursor layout
+//                         AlacCursorShape::Hidden => unreachable!(),
+//                     };
+
+//                     Cursor::new(
+//                         cursor_position,
+//                         block_width,
+//                         dimensions.line_height,
+//                         terminal_theme.cursor,
+//                         shape,
+//                         text,
+//                     )
+//                 },
+//             )
+//         };
+
+//         //Done!
+//         (
+//             constraint.max,
+//             Self::ElementState {
+//                 cells,
+//                 cursor,
+//                 background_color,
+//                 size: dimensions,
+//                 rects,
+//                 relative_highlighted_ranges,
+//                 mode: *mode,
+//                 display_offset: *display_offset,
+//                 hyperlink_tooltip,
+//                 gutter,
+//             },
+//         )
+//     }
+
+//     fn paint(
+//         &mut self,
+//         bounds: Bounds<Pixels>,
+//         view_state: &mut TerminalView,
+//         element_state: &mut Self::ElementState,
+//         cx: &mut ViewContext<TerminalView>,
+//     ) {
+//         let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
+
+//         //Setup element stuff
+//         let clip_bounds = Some(visible_bounds);
+
+//         cx.paint_layer(clip_bounds, |cx| {
+//             let origin = bounds.origin() + vec2f(element_state.gutter, 0.);
+
+//             // Elements are ephemeral, only at paint time do we know what could be clicked by a mouse
+//             self.attach_mouse_handlers(origin, visible_bounds, element_state.mode, cx);
+
+//             cx.scene().push_cursor_region(gpui::CursorRegion {
+//                 bounds,
+//                 style: if element_state.hyperlink_tooltip.is_some() {
+//                     CursorStyle::AlacPointingHand
+//                 } else {
+//                     CursorStyle::IBeam
+//                 },
+//             });
+
+//             cx.paint_layer(clip_bounds, |cx| {
+//                 //Start with a background color
+//                 cx.scene().push_quad(Quad {
+//                     bounds,
+//                     background: Some(element_state.background_color),
+//                     border: Default::default(),
+//                     corner_radii: Default::default(),
+//                 });
+
+//                 for rect in &element_state.rects {
+//                     rect.paint(origin, element_state, view_state, cx);
+//                 }
+//             });
+
+//             //Draw Highlighted Backgrounds
+//             cx.paint_layer(clip_bounds, |cx| {
+//                 for (relative_highlighted_range, color) in
+//                     element_state.relative_highlighted_ranges.iter()
+//                 {
+//                     if let Some((start_y, highlighted_range_lines)) = to_highlighted_range_lines(
+//                         relative_highlighted_range,
+//                         element_state,
+//                         origin,
+//                     ) {
+//                         let hr = HighlightedRange {
+//                             start_y, //Need to change this
+//                             line_height: element_state.size.line_height,
+//                             lines: highlighted_range_lines,
+//                             color: color.clone(),
+//                             //Copied from editor. TODO: move to theme or something
+//                             corner_radius: 0.15 * element_state.size.line_height,
+//                         };
+//                         hr.paint(bounds, cx);
+//                     }
+//                 }
+//             });
+
+//             //Draw the text cells
+//             cx.paint_layer(clip_bounds, |cx| {
+//                 for cell in &element_state.cells {
+//                     cell.paint(origin, element_state, visible_bounds, view_state, cx);
+//                 }
+//             });
+
+//             //Draw cursor
+//             if self.cursor_visible {
+//                 if let Some(cursor) = &element_state.cursor {
+//                     cx.paint_layer(clip_bounds, |cx| {
+//                         cursor.paint(origin, cx);
+//                     })
+//                 }
+//             }
+
+//             if let Some(element) = &mut element_state.hyperlink_tooltip {
+//                 element.paint(origin, visible_bounds, view_state, cx)
+//             }
+//         });
+//     }
+
+//     fn id(&self) -> Option<gpui::ElementId> {
+//         todo!()
+//     }
+
+//     // todo!() remove?
+//     // fn metadata(&self) -> Option<&dyn std::any::Any> {
+//     //     None
+//     // }
+
+//     // fn debug(
+//     //     &self,
+//     //     _: Bounds<Pixels>,
+//     //     _: &Self::ElementState,
+//     //     _: &Self::PaintState,
+//     //     _: &TerminalView,
+//     //     _: &gpui::ViewContext<TerminalView>,
+//     // ) -> gpui::serde_json::Value {
+//     //     json!({
+//     //         "type": "TerminalElement",
+//     //     })
+//     // }
+
+//     // fn rect_for_text_range(
+//     //     &self,
+//     //     _: Range<usize>,
+//     //     bounds: Bounds<Pixels>,
+//     //     _: Bounds<Pixels>,
+//     //     layout: &Self::ElementState,
+//     //     _: &Self::PaintState,
+//     //     _: &TerminalView,
+//     //     _: &gpui::ViewContext<TerminalView>,
+//     // ) -> Option<Bounds<Pixels>> {
+//     //     // Use the same origin that's passed to `Cursor::paint` in the paint
+//     //     // method bove.
+//     //     let mut origin = bounds.origin() + vec2f(layout.size.cell_width, 0.);
+
+//     //     // TODO - Why is it necessary to move downward one line to get correct
+//     //     // positioning? I would think that we'd want the same rect that is
+//     //     // painted for the cursor.
+//     //     origin += vec2f(0., layout.size.line_height);
+
+//     //     Some(layout.cursor.as_ref()?.bounding_rect(origin))
+//     // }
+// }
+
+// impl Component<TerminalView> for TerminalElement {
+//     fn render(self) -> AnyElement<TerminalView> {
+//         todo!()
+//     }
+// }
+
+// fn is_blank(cell: &IndexedCell) -> bool {
+//     if cell.c != ' ' {
+//         return false;
+//     }
+
+//     if cell.bg != AnsiColor::Named(NamedColor::Background) {
+//         return false;
+//     }
+
+//     if cell.hyperlink().is_some() {
+//         return false;
+//     }
+
+//     if cell
+//         .flags
+//         .intersects(Flags::ALL_UNDERLINES | Flags::INVERSE | Flags::STRIKEOUT)
+//     {
+//         return false;
+//     }
+
+//     return true;
+// }
+
+// fn to_highlighted_range_lines(
+//     range: &RangeInclusive<AlacPoint>,
+//     layout: &LayoutState,
+//     origin: Point<Pixels>,
+// ) -> Option<(f32, Vec<HighlightedRangeLine>)> {
+//     // Step 1. Normalize the points to be viewport relative.
+//     // When display_offset = 1, here's how the grid is arranged:
+//     //-2,0 -2,1...
+//     //--- Viewport top
+//     //-1,0 -1,1...
+//     //--------- Terminal Top
+//     // 0,0  0,1...
+//     // 1,0  1,1...
+//     //--- Viewport Bottom
+//     // 2,0  2,1...
+//     //--------- Terminal Bottom
+
+//     // Normalize to viewport relative, from terminal relative.
+//     // lines are i32s, which are negative above the top left corner of the terminal
+//     // If the user has scrolled, we use the display_offset to tell us which offset
+//     // of the grid data we should be looking at. But for the rendering step, we don't
+//     // want negatives. We want things relative to the 'viewport' (the area of the grid
+//     // which is currently shown according to the display offset)
+//     let unclamped_start = AlacPoint::new(
+//         range.start().line + layout.display_offset,
+//         range.start().column,
+//     );
+//     let unclamped_end =
+//         AlacPoint::new(range.end().line + layout.display_offset, range.end().column);
+
+//     // Step 2. Clamp range to viewport, and return None if it doesn't overlap
+//     if unclamped_end.line.0 < 0 || unclamped_start.line.0 > layout.size.num_lines() as i32 {
+//         return None;
+//     }
+
+//     let clamped_start_line = unclamped_start.line.0.max(0) as usize;
+//     let clamped_end_line = unclamped_end.line.0.min(layout.size.num_lines() as i32) as usize;
+//     //Convert the start of the range to pixels
+//     let start_y = origin.y + clamped_start_line as f32 * layout.size.line_height;
+
+//     // Step 3. Expand ranges that cross lines into a collection of single-line ranges.
+//     //  (also convert to pixels)
+//     let mut highlighted_range_lines = Vec::new();
+//     for line in clamped_start_line..=clamped_end_line {
+//         let mut line_start = 0;
+//         let mut line_end = layout.size.columns();
+
+//         if line == clamped_start_line {
+//             line_start = unclamped_start.column.0 as usize;
+//         }
+//         if line == clamped_end_line {
+//             line_end = unclamped_end.column.0 as usize + 1; //+1 for inclusive
+//         }
+
+//         highlighted_range_lines.push(HighlightedRangeLine {
+//             start_x: origin.x() + line_start as f32 * layout.size.cell_width,
+//             end_x: origin.x() + line_end as f32 * layout.size.cell_width,
+//         });
+//     }
+
+//     Some((start_y, highlighted_range_lines))
+// }
+
+// fn font_size(terminal_settings: &TerminalSettings, cx: &mut AppContext) -> Option<Pixels> {
+//     terminal_settings
+//         .font_size
+//         .map(|size| theme::adjusted_font_size(size, cx))
+// }

crates/terminal_view2/src/terminal_panel.rs 🔗

@@ -3,8 +3,9 @@ use std::{path::PathBuf, sync::Arc};
 use crate::TerminalView;
 use db::kvp::KEY_VALUE_STORE;
 use gpui::{
-    actions, serde_json, Action, AppContext, AsyncAppContext, Entity, EventEmitter, Render,
-    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+    actions, div, serde_json, AppContext, AsyncWindowContext, Div, Entity, EventEmitter,
+    FocusHandle, FocusableView, ParentComponent, Render, Subscription, Task, View, ViewContext,
+    VisualContext, WeakView, WindowContext,
 };
 use project::Fs;
 use serde::{Deserialize, Serialize};
@@ -14,7 +15,9 @@ use util::{ResultExt, TryFutureExt};
 use workspace::{
     dock::{DockPosition, Panel, PanelEvent},
     item::Item,
-    pane, Pane, Workspace,
+    pane,
+    ui::Icon,
+    Pane, Workspace,
 };
 
 use anyhow::Result;
@@ -28,20 +31,14 @@ pub fn init(cx: &mut AppContext) {
         |workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
             workspace.register_action(TerminalPanel::new_terminal);
             workspace.register_action(TerminalPanel::open_terminal);
+            workspace.register_action(|workspace, _: &ToggleFocus, cx| {
+                workspace.toggle_panel_focus::<TerminalPanel>(cx);
+            });
         },
     )
     .detach();
 }
 
-#[derive(Debug)]
-pub enum Event {
-    Close,
-    DockPositionChanged,
-    ZoomIn,
-    ZoomOut,
-    Focus,
-}
-
 pub struct TerminalPanel {
     pane: View<Pane>,
     fs: Arc<dyn Fs>,
@@ -54,9 +51,9 @@ pub struct TerminalPanel {
 
 impl TerminalPanel {
     fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
-        let weak_self = cx.weak_handle();
+        let _weak_self = cx.view().downgrade();
         let pane = cx.build_view(|cx| {
-            let window = cx.window_handle();
+            let _window = cx.window_handle();
             let mut pane = Pane::new(
                 workspace.weak_handle(),
                 workspace.project().clone(),
@@ -65,54 +62,55 @@ impl TerminalPanel {
             );
             pane.set_can_split(false, cx);
             pane.set_can_navigate(false, cx);
-            pane.on_can_drop(move |drag_and_drop, cx| {
-                drag_and_drop
-                    .currently_dragged::<DraggedItem>(window)
-                    .map_or(false, |(_, item)| {
-                        item.handle.act_as::<TerminalView>(cx).is_some()
-                    })
-            });
-            pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
-                let this = weak_self.clone();
-                Flex::row()
-                    .with_child(Pane::render_tab_bar_button(
-                        0,
-                        "icons/plus.svg",
-                        false,
-                        Some(("New Terminal", Some(Box::new(workspace::NewTerminal)))),
-                        cx,
-                        move |_, cx| {
-                            let this = this.clone();
-                            cx.window_context().defer(move |cx| {
-                                if let Some(this) = this.upgrade() {
-                                    this.update(cx, |this, cx| {
-                                        this.add_terminal(None, cx);
-                                    });
-                                }
-                            })
-                        },
-                        |_, _| {},
-                        None,
-                    ))
-                    .with_child(Pane::render_tab_bar_button(
-                        1,
-                        if pane.is_zoomed() {
-                            "icons/minimize.svg"
-                        } else {
-                            "icons/maximize.svg"
-                        },
-                        pane.is_zoomed(),
-                        Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))),
-                        cx,
-                        move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
-                        |_, _| {},
-                        None,
-                    ))
-                    .into_any()
-            });
-            let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
-            pane.toolbar()
-                .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
+            // todo!()
+            // pane.on_can_drop(move |drag_and_drop, cx| {
+            //     drag_and_drop
+            //         .currently_dragged::<DraggedItem>(window)
+            //         .map_or(false, |(_, item)| {
+            //             item.handle.act_as::<TerminalView>(cx).is_some()
+            //         })
+            // });
+            // pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
+            //     let this = weak_self.clone();
+            //     Flex::row()
+            //         .with_child(Pane::render_tab_bar_button(
+            //             0,
+            //             "icons/plus.svg",
+            //             false,
+            //             Some(("New Terminal", Some(Box::new(workspace::NewTerminal)))),
+            //             cx,
+            //             move |_, cx| {
+            //                 let this = this.clone();
+            //                 cx.window_context().defer(move |cx| {
+            //                     if let Some(this) = this.upgrade() {
+            //                         this.update(cx, |this, cx| {
+            //                             this.add_terminal(None, cx);
+            //                         });
+            //                     }
+            //                 })
+            //             },
+            //             |_, _| {},
+            //             None,
+            //         ))
+            //         .with_child(Pane::render_tab_bar_button(
+            //             1,
+            //             if pane.is_zoomed() {
+            //                 "icons/minimize.svg"
+            //             } else {
+            //                 "icons/maximize.svg"
+            //             },
+            //             pane.is_zoomed(),
+            //             Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))),
+            //             cx,
+            //             move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
+            //             |_, _| {},
+            //             None,
+            //         ))
+            //         .into_any()
+            // });
+            // let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
+            // pane.toolbar()
+            //     .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
             pane
         });
         let subscriptions = vec![
@@ -133,80 +131,81 @@ impl TerminalPanel {
             let new_dock_position = this.position(cx);
             if new_dock_position != old_dock_position {
                 old_dock_position = new_dock_position;
-                cx.emit(Event::DockPositionChanged);
+                cx.emit(PanelEvent::ChangePosition);
             }
         })
         .detach();
         this
     }
 
-    pub fn load(workspace: WeakView<Workspace>, cx: AsyncAppContext) -> Task<Result<View<Self>>> {
-        cx.spawn(|mut cx| async move {
-            let serialized_panel = if let Some(panel) = cx
-                .background_executor()
-                .spawn(async move { KEY_VALUE_STORE.read_kvp(TERMINAL_PANEL_KEY) })
-                .await
-                .log_err()
-                .flatten()
-            {
-                Some(serde_json::from_str::<SerializedTerminalPanel>(&panel)?)
+    pub async fn load(
+        workspace: WeakView<Workspace>,
+        mut cx: AsyncWindowContext,
+    ) -> Result<View<Self>> {
+        let serialized_panel = cx
+            .background_executor()
+            .spawn(async move { KEY_VALUE_STORE.read_kvp(TERMINAL_PANEL_KEY) })
+            .await
+            .log_err()
+            .flatten()
+            .map(|panel| serde_json::from_str::<SerializedTerminalPanel>(&panel))
+            .transpose()
+            .log_err()
+            .flatten();
+
+        let (panel, pane, items) = workspace.update(&mut cx, |workspace, cx| {
+            let panel = cx.build_view(|cx| TerminalPanel::new(workspace, cx));
+            let items = if let Some(serialized_panel) = serialized_panel.as_ref() {
+                panel.update(cx, |panel, cx| {
+                    cx.notify();
+                    panel.height = serialized_panel.height;
+                    panel.width = serialized_panel.width;
+                    panel.pane.update(cx, |_, cx| {
+                        serialized_panel
+                            .items
+                            .iter()
+                            .map(|item_id| {
+                                TerminalView::deserialize(
+                                    workspace.project().clone(),
+                                    workspace.weak_handle(),
+                                    workspace.database_id(),
+                                    *item_id,
+                                    cx,
+                                )
+                            })
+                            .collect::<Vec<_>>()
+                    })
+                })
             } else {
-                None
+                Default::default()
             };
-            let (panel, pane, items) = workspace.update(&mut cx, |workspace, cx| {
-                let panel = cx.build_view(|cx| TerminalPanel::new(workspace, cx));
-                let items = if let Some(serialized_panel) = serialized_panel.as_ref() {
-                    panel.update(cx, |panel, cx| {
-                        cx.notify();
-                        panel.height = serialized_panel.height;
-                        panel.width = serialized_panel.width;
-                        panel.pane.update(cx, |_, cx| {
-                            serialized_panel
-                                .items
-                                .iter()
-                                .map(|item_id| {
-                                    TerminalView::deserialize(
-                                        workspace.project().clone(),
-                                        workspace.weak_handle(),
-                                        workspace.database_id(),
-                                        *item_id,
-                                        cx,
-                                    )
-                                })
-                                .collect::<Vec<_>>()
-                        })
-                    })
-                } else {
-                    Default::default()
-                };
-                let pane = panel.read(cx).pane.clone();
-                (panel, pane, items)
-            })?;
-
-            let pane = pane.downgrade();
-            let items = futures::future::join_all(items).await;
-            pane.update(&mut cx, |pane, cx| {
-                let active_item_id = serialized_panel
-                    .as_ref()
-                    .and_then(|panel| panel.active_item_id);
-                let mut active_ix = None;
-                for item in items {
-                    if let Some(item) = item.log_err() {
-                        let item_id = item.entity_id().as_u64();
-                        pane.add_item(Box::new(item), false, false, None, cx);
-                        if Some(item_id) == active_item_id {
-                            active_ix = Some(pane.items_len() - 1);
-                        }
+            let pane = panel.read(cx).pane.clone();
+            (panel, pane, items)
+        })?;
+
+        let pane = pane.downgrade();
+        let items = futures::future::join_all(items).await;
+        pane.update(&mut cx, |pane, cx| {
+            let active_item_id = serialized_panel
+                .as_ref()
+                .and_then(|panel| panel.active_item_id);
+            let mut active_ix = None;
+            for item in items {
+                if let Some(item) = item.log_err() {
+                    let item_id = item.entity_id().as_u64();
+                    pane.add_item(Box::new(item), false, false, None, cx);
+                    if Some(item_id) == active_item_id {
+                        active_ix = Some(pane.items_len() - 1);
                     }
                 }
+            }
 
-                if let Some(active_ix) = active_ix {
-                    pane.activate_item(active_ix, false, false, cx)
-                }
-            })?;
+            if let Some(active_ix) = active_ix {
+                pane.activate_item(active_ix, false, false, cx)
+            }
+        })?;
 
-            Ok(panel)
-        })
+        Ok(panel)
     }
 
     fn handle_pane_event(
@@ -218,10 +217,10 @@ impl TerminalPanel {
         match event {
             pane::Event::ActivateItem { .. } => self.serialize(cx),
             pane::Event::RemoveItem { .. } => self.serialize(cx),
-            pane::Event::Remove => cx.emit(Event::Close),
-            pane::Event::ZoomIn => cx.emit(Event::ZoomIn),
-            pane::Event::ZoomOut => cx.emit(Event::ZoomOut),
-            pane::Event::Focus => cx.emit(Event::Focus),
+            pane::Event::Remove => cx.emit(PanelEvent::Close),
+            pane::Event::ZoomIn => cx.emit(PanelEvent::ZoomIn),
+            pane::Event::ZoomOut => cx.emit(PanelEvent::ZoomOut),
+            pane::Event::Focus => cx.emit(PanelEvent::Focus),
 
             pane::Event::AddItem { item } => {
                 if let Some(workspace) = self.workspace.upgrade() {
@@ -334,20 +333,20 @@ impl TerminalPanel {
     }
 }
 
-impl EventEmitter<Event> for TerminalPanel {}
 impl EventEmitter<PanelEvent> for TerminalPanel {}
 
 impl Render for TerminalPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
-        ChildView::new(&self.pane, cx).into_any()
+    type Element = Div<Self>;
+
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+        div().child(self.pane.clone())
     }
+}
 
-    // todo!()
-    // fn focus_in(&mut self, _: gpui::AnyView, cx: &mut ViewContext<Self>) {
-    //     if cx.is_self_focused() {
-    //         cx.focus(&self.pane);
-    //     }
-    // }
+impl FocusableView for TerminalPanel {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.pane.focus_handle(cx)
+    }
 }
 
 impl Panel for TerminalPanel {
@@ -407,14 +406,6 @@ impl Panel for TerminalPanel {
         }
     }
 
-    fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
-        Some("icons/terminal.svg")
-    }
-
-    fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
-        ("Terminal Panel".into(), Some(Box::new(ToggleFocus)))
-    }
-
     fn icon_label(&self, cx: &WindowContext) -> Option<String> {
         let count = self.pane.read(cx).items_len();
         if count == 0 {
@@ -428,34 +419,22 @@ impl Panel for TerminalPanel {
         self.pane.read(cx).has_focus(cx)
     }
 
-    fn persistent_name(&self) -> &'static str {
-        todo!()
+    fn persistent_name() -> &'static str {
+        "TerminalPanel"
     }
 
-    // todo!() is it needed?
-    // fn should_change_position_on_event(event: &Self::Event) -> bool {
-    //     matches!(event, Event::DockPositionChanged)
-    // }
-
-    // fn should_activate_on_event(_: &Self::Event) -> bool {
-    //     false
-    // }
-
-    // fn should_close_on_event(event: &Event) -> bool {
-    //     matches!(event, Event::Close)
-    // }
-
-    // fn is_focus_event(event: &Self::Event) -> bool {
-    //     matches!(event, Event::Focus)
+    // todo!()
+    // fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
+    //     ("Terminal Panel".into(), Some(Box::new(ToggleFocus)))
     // }
 
-    // fn should_zoom_in_on_event(event: &Event) -> bool {
-    //     matches!(event, Event::ZoomIn)
-    // }
+    fn icon(&self, _cx: &WindowContext) -> Option<Icon> {
+        Some(Icon::Terminal)
+    }
 
-    // fn should_zoom_out_on_event(event: &Event) -> bool {
-    //     matches!(event, Event::ZoomOut)
-    // }
+    fn toggle_action(&self) -> Box<dyn gpui::Action> {
+        Box::new(ToggleFocus)
+    }
 }
 
 #[derive(Serialize, Deserialize)]

crates/terminal_view2/src/terminal_view.rs 🔗

@@ -1,22 +1,23 @@
 #![allow(unused_variables)]
 //todo!(remove)
 
-// mod persistence;
+mod persistence;
 pub mod terminal_element;
 pub mod terminal_panel;
 
-use crate::terminal_element::TerminalElement;
+// todo!()
+// use crate::terminal_element::TerminalElement;
 use anyhow::Context;
 use dirs::home_dir;
 use editor::{scroll::autoscroll::Autoscroll, Editor};
 use gpui::{
     actions, div, img, red, register_action, AnyElement, AppContext, Component, DispatchPhase, Div,
-    EventEmitter, FocusEvent, FocusHandle, Focusable, FocusableKeyDispatch, InputHandler,
-    KeyDownEvent, Keystroke, Model, ParentElement, Pixels, Render, SharedString,
-    StatefulInteractivity, StatelessInteractive, Styled, Task, View, ViewContext, VisualContext,
-    WeakView,
+    EventEmitter, FocusEvent, FocusHandle, Focusable, FocusableComponent, FocusableView,
+    InputHandler, InteractiveComponent, KeyDownEvent, Keystroke, Model, ParentComponent, Pixels,
+    Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView,
 };
 use language::Bias;
+use persistence::TERMINAL_DB;
 use project::{search::SearchQuery, LocalWorktree, Project};
 use serde::Deserialize;
 use settings::Settings;
@@ -68,7 +69,7 @@ pub fn init(cx: &mut AppContext) {
 
     cx.observe_new_views(
         |workspace: &mut Workspace, cx: &mut ViewContext<Workspace>| {
-            workspace.register_action(TerminalView::deploy)
+            workspace.register_action(TerminalView::deploy);
         },
     )
     .detach();
@@ -94,6 +95,12 @@ impl EventEmitter<Event> for TerminalView {}
 impl EventEmitter<ItemEvent> for TerminalView {}
 impl EventEmitter<SearchEvent> for TerminalView {}
 
+impl FocusableView for TerminalView {
+    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+        self.focus_handle.clone()
+    }
+}
+
 impl TerminalView {
     ///Create a new Terminal in the current working directory or the user's home directory
     pub fn deploy(
@@ -159,15 +166,14 @@ impl TerminalView {
 
                     let item_id = cx.entity_id();
                     let workspace_id = this.workspace_id;
-                    // todo!(persistence)
-                    // cx.background_executor()
-                    //     .spawn(async move {
-                    //         TERMINAL_DB
-                    //             .save_working_directory(item_id, workspace_id, cwd)
-                    //             .await
-                    //             .log_err();
-                    //     })
-                    //     .detach();
+                    cx.background_executor()
+                        .spawn(async move {
+                            TERMINAL_DB
+                                .save_working_directory(item_id.as_u64(), workspace_id, cwd)
+                                .await
+                                .log_err();
+                        })
+                        .detach();
                 }
             }
 
@@ -526,7 +532,7 @@ impl TerminalView {
 }
 
 impl Render for TerminalView {
-    type Element = Div<Self, StatefulInteractivity<Self>, FocusableKeyDispatch<Self>>;
+    type Element = Focusable<Self, Div<Self>>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let terminal_handle = self.terminal.clone().downgrade();
@@ -536,7 +542,7 @@ impl Render for TerminalView {
 
         div()
             .track_focus(&self.focus_handle)
-            .on_focus_in(Self::focus_out)
+            .on_focus_in(Self::focus_in)
             .on_focus_out(Self::focus_out)
             .on_key_down(Self::key_down)
             .on_action(TerminalView::send_text)
@@ -828,10 +834,6 @@ impl Item for TerminalView {
         //     .detach();
         self.workspace_id = workspace.database_id();
     }
-
-    fn focus_handle(&self) -> FocusHandle {
-        self.focus_handle.clone()
-    }
 }
 
 impl SearchableItem for TerminalView {

crates/workspace2/src/persistence/model.rs 🔗

@@ -277,7 +277,7 @@ impl SerializedPane {
 
 pub type GroupId = i64;
 pub type PaneId = i64;
-pub type ItemId = usize;
+pub type ItemId = u64;
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub struct SerializedItem {

crates/workspace2/src/workspace2.rs 🔗

@@ -15,13 +15,6 @@ mod status_bar;
 mod toolbar;
 mod workspace_settings;
 
-pub use crate::persistence::{
-    model::{
-        DockData, DockStructure, ItemId, SerializedItem, SerializedPane, SerializedPaneGroup,
-        SerializedWorkspace,
-    },
-    WorkspaceDb,
-};
 use anyhow::{anyhow, Context as _, Result};
 use call2::ActiveCall;
 use client2::{
@@ -37,11 +30,10 @@ use futures::{
 };
 use gpui::{
     actions, div, point, register_action, size, Action, AnyModel, AnyView, AnyWeakView, AppContext,
-    AsyncAppContext, AsyncWindowContext, Bounds, Div, Entity, EntityId, EventEmitter, FocusHandle,
-    FocusableView, GlobalPixels, KeyContext, Model, ModelContext, ParentElement, Point, Render,
-    Size, StatefulInteractive, StatelessInteractive, StatelessInteractivity, Styled, Subscription,
-    Task, View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle,
-    WindowOptions,
+    AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity, EntityId, EventEmitter,
+    FocusHandle, FocusableView, GlobalPixels, InteractiveComponent, KeyContext, Model,
+    ModelContext, ParentComponent, Point, Render, Size, Styled, Subscription, Task, View,
+    ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -52,7 +44,10 @@ use node_runtime::NodeRuntime;
 use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
 pub use pane::*;
 pub use pane_group::*;
-use persistence::{model::WorkspaceLocation, DB};
+pub use persistence::{
+    model::{ItemId, SerializedWorkspace, WorkspaceLocation},
+    WorkspaceDb, DB,
+};
 use postage::stream::Stream;
 use project2::{Project, ProjectEntryId, ProjectPath, Worktree};
 use serde::Deserialize;
@@ -69,10 +64,15 @@ use std::{
 };
 use theme2::{ActiveTheme, ThemeSettings};
 pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
+pub use ui;
 use util::ResultExt;
 use uuid::Uuid;
 pub use workspace_settings::{AutosaveSetting, WorkspaceSettings};
 
+use crate::persistence::model::{
+    DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup,
+};
+
 lazy_static! {
     static ref ZED_WINDOW_SIZE: Option<Size<GlobalPixels>> = env::var("ZED_WINDOW_SIZE")
         .ok()
@@ -1582,13 +1582,11 @@ impl Workspace {
         self.serialize_workspace(cx);
     }
 
-    //     /// Transfer focus to the panel of the given type.
-    //     pub fn focus_panel<T: Panel>(&mut self, cx: &mut ViewContext<Self>) -> Option<View<T>> {
-    //         self.focus_or_unfocus_panel::<T>(cx, |_, _| true)?
-    //             .as_any()
-    //             .clone()
-    //             .downcast()
-    //     }
+    /// Transfer focus to the panel of the given type.
+    pub fn focus_panel<T: Panel>(&mut self, cx: &mut ViewContext<Self>) -> Option<View<T>> {
+        let panel = self.focus_or_unfocus_panel::<T>(cx, |_, _| true)?;
+        panel.to_any().downcast().ok()
+    }
 
     /// Focus the panel of the given type if it isn't already focused. If it is
     /// already focused, then transfer focus back to the workspace center.
@@ -2981,7 +2979,7 @@ impl Workspace {
                         .filter_map(|item_handle| {
                             Some(SerializedItem {
                                 kind: Arc::from(item_handle.serialized_item_kind()?),
-                                item_id: item_handle.id().as_u64() as usize,
+                                item_id: item_handle.id().as_u64(),
                                 active: Some(item_handle.id()) == active_item_id,
                             })
                         })

crates/zed2/Cargo.toml 🔗

@@ -66,7 +66,7 @@ feature_flags = { package = "feature_flags2", path = "../feature_flags2" }
 sum_tree = { path = "../sum_tree" }
 shellexpand = "2.1.0"
 text = { package = "text2", path = "../text2" }
-# terminal_view = { path = "../terminal_view" }
+terminal_view = { package = "terminal_view2", path = "../terminal_view2" }
 theme = { package = "theme2", path = "../theme2" }
 # theme_selector = { path = "../theme_selector" }
 util = { path = "../util" }

crates/zed2/src/main.rs 🔗

@@ -198,7 +198,7 @@ fn main() {
         // search::init(cx);
         // semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);
         // vim::init(cx);
-        // terminal_view::init(cx);
+        terminal_view::init(cx);
 
         // journal2::init(app_state.clone(), cx);
         // language_selector::init(cx);

crates/zed2/src/zed2.rs 🔗

@@ -20,6 +20,7 @@ use anyhow::{anyhow, Context as _};
 use project_panel::ProjectPanel;
 use settings::{initial_local_settings_content, Settings};
 use std::{borrow::Cow, ops::Deref, sync::Arc};
+use terminal_view::terminal_panel::TerminalPanel;
 use util::{
     asset_str,
     channel::ReleaseChannel,
@@ -174,7 +175,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
 
         cx.spawn(|workspace_handle, mut cx| async move {
             let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
-            // let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
+            let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
             // let assistant_panel = AssistantPanel::load(workspace_handle.clone(), cx.clone());
             let channels_panel =
                 collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone());
@@ -186,14 +187,14 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
             // );
             let (
                 project_panel,
-                //     terminal_panel,
+                terminal_panel,
                 //     assistant_panel,
                 channels_panel,
                 //     chat_panel,
                 //     notification_panel,
             ) = futures::try_join!(
                 project_panel,
-                //     terminal_panel,
+                terminal_panel,
                 //     assistant_panel,
                 channels_panel,
                 //     chat_panel,
@@ -203,7 +204,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
             workspace_handle.update(&mut cx, |workspace, cx| {
                 let project_panel_position = project_panel.position(cx);
                 workspace.add_panel(project_panel, cx);
-                //     workspace.add_panel(terminal_panel, cx);
+                workspace.add_panel(terminal_panel, cx);
                 //     workspace.add_panel(assistant_panel, cx);
                 workspace.add_panel(channels_panel, cx);
                 //     workspace.add_panel(chat_panel, cx);