diff --git a/Cargo.lock b/Cargo.lock index 0e155e9e99..4bbada23d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11567,6 +11567,7 @@ dependencies = [ "smol", "sum_tree", "tempdir", + "terminal_view2", "text2", "theme2", "thiserror", diff --git a/crates/editor2/src/scroll.rs b/crates/editor2/src/scroll.rs index 52e7498f97..360c1e3c36 100644 --- a/crates/editor2/src/scroll.rs +++ b/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, ) { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 19280d5beb..b0d9d07df2 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1830,8 +1830,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { self.view } - pub fn model(&self) -> Model { - self.view.model.clone() + pub fn model(&self) -> &Model { + &self.view.model } /// Access the underlying window context. @@ -2163,7 +2163,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { pub fn observe_global( &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(); diff --git a/crates/sqlez/src/bindable.rs b/crates/sqlez/src/bindable.rs index 4c874c4585..ebfddbe1da 100644 --- a/crates/sqlez/src/bindable.rs +++ b/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 { + 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 { diff --git a/crates/terminal_view2/src/persistence.rs b/crates/terminal_view2/src/persistence.rs index 38dad88a8e..0da9ed4729 100644 --- a/crates/terminal_view2/src/persistence.rs +++ b/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! { diff --git a/crates/terminal_view2/src/terminal_element.rs b/crates/terminal_view2/src/terminal_element.rs index 50ab14144b..00f1dca17d 100644 --- a/crates/terminal_view2/src/terminal_element.rs +++ b/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, - rects: Vec, - relative_highlighted_ranges: Vec<(RangeInclusive, Hsla)>, - cursor: Option, - background_color: Hsla, - size: TerminalSize, - mode: TermMode, - display_offset: usize, - hyperlink_tooltip: Option>, - 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, - text: Line, -} - -impl LayoutCell { - fn new(point: AlacPoint, text: Line) -> LayoutCell { - LayoutCell { point, text } - } - - fn paint( - &self, - origin: Point, - layout: &LayoutState, - _visible_bounds: Bounds, - _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, - num_of_cells: usize, - color: Hsla, -} - -impl LayoutRect { - fn new(point: AlacPoint, 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, - layout: &LayoutState, - _view: &mut TerminalView, - cx: &mut ViewContext, - ) { - 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, - focused: bool, - cursor_visible: bool, - can_navigate_to_selected_word: bool, -} - -impl TerminalElement { - pub fn new( - terminal: WeakModel, - focused: bool, - cursor_visible: bool, - can_navigate_to_selected_word: bool, - ) -> TerminalElement { - TerminalElement { - terminal, - focused, - cursor_visible, - can_navigate_to_selected_word, - } - } - - //Vec> -> Clip out the parts of the ranges - - fn layout_grid( - grid: &Vec, - text_style: &TextStyle, - terminal_theme: &TerminalStyle, - text_layout_cache: &TextLayoutCache, - font_cache: &FontCache, - hyperlink: Option<(HighlightStyle, &RangeInclusive)>, - ) -> (Vec, Vec) { - let mut cells = vec![]; - let mut rects = vec![]; - - let mut cur_rect: Option = 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)>, - ) -> 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( - connection: WeakModel, - origin: Point, - f: impl Fn(&mut Terminal, Vector2F, E, &mut ModelContext), - ) -> impl Fn(E, &mut TerminalView, &mut EventContext) { - 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, - visible_bounds: Bounds, - mode: TermMode, - cx: &mut ViewContext, - ) { - let connection = self.terminal; - - let mut region = MouseRegion::new::(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 for TerminalElement { - type ElementState = LayoutState; - - fn layout( - &mut self, - view_state: &mut TerminalView, - element_state: &mut Self::ElementState, - cx: &mut ViewContext, - ) -> 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::( - 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, - view_state: &mut TerminalView, - element_state: &mut Self::ElementState, - cx: &mut ViewContext, - ) { - 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 { - todo!() - } - - fn initialize( - &mut self, - view_state: &mut TerminalView, - element_state: Option, - cx: &mut ViewContext, - ) -> Self::ElementState { - todo!() - } - - // todo!() remove? - // fn metadata(&self) -> Option<&dyn std::any::Any> { - // None - // } - - // fn debug( - // &self, - // _: Bounds, - // _: &Self::ElementState, - // _: &Self::PaintState, - // _: &TerminalView, - // _: &gpui::ViewContext, - // ) -> gpui::serde_json::Value { - // json!({ - // "type": "TerminalElement", - // }) - // } - - // fn rect_for_text_range( - // &self, - // _: Range, - // bounds: Bounds, - // _: Bounds, - // layout: &Self::ElementState, - // _: &Self::PaintState, - // _: &TerminalView, - // _: &gpui::ViewContext, - // ) -> Option> { - // // 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 for TerminalElement { - fn render(self) -> AnyElement { - 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, - layout: &LayoutState, - origin: Point, -) -> Option<(f32, Vec)> { - // 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 { - 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, +// rects: Vec, +// relative_highlighted_ranges: Vec<(RangeInclusive, Hsla)>, +// cursor: Option, +// background_color: Hsla, +// size: TerminalSize, +// mode: TermMode, +// display_offset: usize, +// hyperlink_tooltip: Option>, +// 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, +// text: Line, +// } + +// impl LayoutCell { +// fn new(point: AlacPoint, text: Line) -> LayoutCell { +// LayoutCell { point, text } +// } + +// fn paint( +// &self, +// origin: Point, +// layout: &LayoutState, +// _visible_bounds: Bounds, +// _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, +// num_of_cells: usize, +// color: Hsla, +// } + +// impl LayoutRect { +// fn new(point: AlacPoint, 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, +// layout: &LayoutState, +// _view: &mut TerminalView, +// cx: &mut ViewContext, +// ) { +// 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, +// focused: bool, +// cursor_visible: bool, +// can_navigate_to_selected_word: bool, +// } + +// impl TerminalElement { +// pub fn new( +// terminal: WeakModel, +// focused: bool, +// cursor_visible: bool, +// can_navigate_to_selected_word: bool, +// ) -> TerminalElement { +// TerminalElement { +// terminal, +// focused, +// cursor_visible, +// can_navigate_to_selected_word, +// } +// } + +// //Vec> -> Clip out the parts of the ranges + +// fn layout_grid( +// grid: &Vec, +// text_style: &TextStyle, +// terminal_theme: &TerminalStyle, +// text_layout_cache: &TextLayoutCache, +// font_cache: &FontCache, +// hyperlink: Option<(HighlightStyle, &RangeInclusive)>, +// ) -> (Vec, Vec) { +// let mut cells = vec![]; +// let mut rects = vec![]; + +// let mut cur_rect: Option = 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)>, +// ) -> 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( +// connection: WeakModel, +// origin: Point, +// f: impl Fn(&mut Terminal, Vector2F, E, &mut ModelContext), +// ) -> impl Fn(E, &mut TerminalView, &mut EventContext) { +// 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, +// visible_bounds: Bounds, +// mode: TermMode, +// cx: &mut ViewContext, +// ) { +// let connection = self.terminal; + +// let mut region = MouseRegion::new::(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 for TerminalElement { +// type ElementState = LayoutState; + +// fn layout( +// &mut self, +// view_state: &mut TerminalView, +// element_state: &mut Self::ElementState, +// cx: &mut ViewContext, +// ) -> 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::( +// 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, +// view_state: &mut TerminalView, +// element_state: &mut Self::ElementState, +// cx: &mut ViewContext, +// ) { +// 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 { +// todo!() +// } + +// // todo!() remove? +// // fn metadata(&self) -> Option<&dyn std::any::Any> { +// // None +// // } + +// // fn debug( +// // &self, +// // _: Bounds, +// // _: &Self::ElementState, +// // _: &Self::PaintState, +// // _: &TerminalView, +// // _: &gpui::ViewContext, +// // ) -> gpui::serde_json::Value { +// // json!({ +// // "type": "TerminalElement", +// // }) +// // } + +// // fn rect_for_text_range( +// // &self, +// // _: Range, +// // bounds: Bounds, +// // _: Bounds, +// // layout: &Self::ElementState, +// // _: &Self::PaintState, +// // _: &TerminalView, +// // _: &gpui::ViewContext, +// // ) -> Option> { +// // // 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 for TerminalElement { +// fn render(self) -> AnyElement { +// 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, +// layout: &LayoutState, +// origin: Point, +// ) -> Option<(f32, Vec)> { +// // 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 { +// terminal_settings +// .font_size +// .map(|size| theme::adjusted_font_size(size, cx)) +// } diff --git a/crates/terminal_view2/src/terminal_panel.rs b/crates/terminal_view2/src/terminal_panel.rs index 94140450bc..fbb1bd5352 100644 --- a/crates/terminal_view2/src/terminal_panel.rs +++ b/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.register_action(TerminalPanel::new_terminal); workspace.register_action(TerminalPanel::open_terminal); + workspace.register_action(|workspace, _: &ToggleFocus, cx| { + workspace.toggle_panel_focus::(cx); + }); }, ) .detach(); } -#[derive(Debug)] -pub enum Event { - Close, - DockPositionChanged, - ZoomIn, - ZoomOut, - Focus, -} - pub struct TerminalPanel { pane: View, fs: Arc, @@ -54,9 +51,9 @@ pub struct TerminalPanel { impl TerminalPanel { fn new(workspace: &Workspace, cx: &mut ViewContext) -> 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::(window) - .map_or(false, |(_, item)| { - item.handle.act_as::(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::(window) + // .map_or(false, |(_, item)| { + // item.handle.act_as::(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, cx: AsyncAppContext) -> Task>> { - 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::(&panel)?) - } else { - None - }; - 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::>() - }) - }) - } else { - Default::default() - }; - let pane = panel.read(cx).pane.clone(); - (panel, pane, items) - })?; + pub async fn load( + workspace: WeakView, + mut cx: AsyncWindowContext, + ) -> Result> { + 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::(&panel)) + .transpose() + .log_err() + .flatten(); - 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 (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::>() + }) + }) + } 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); } } + } - 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 for TerminalPanel {} impl EventEmitter for TerminalPanel {} impl Render for TerminalPanel { - fn render(&mut self, cx: &mut ViewContext) -> gpui::AnyElement { - ChildView::new(&self.pane, cx).into_any() - } + type Element = Div; - // todo!() - // fn focus_in(&mut self, _: gpui::AnyView, cx: &mut ViewContext) { - // if cx.is_self_focused() { - // cx.focus(&self.pane); - // } - // } + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { + div().child(self.pane.clone()) + } +} + +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>) { - ("Terminal Panel".into(), Some(Box::new(ToggleFocus))) - } - fn icon_label(&self, cx: &WindowContext) -> Option { 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) + // todo!() + // fn icon_tooltip(&self) -> (String, Option>) { + // ("Terminal Panel".into(), Some(Box::new(ToggleFocus))) // } - // fn should_activate_on_event(_: &Self::Event) -> bool { - // false - // } + fn icon(&self, _cx: &WindowContext) -> Option { + Some(Icon::Terminal) + } - // fn should_close_on_event(event: &Event) -> bool { - // matches!(event, Event::Close) - // } - - // fn is_focus_event(event: &Self::Event) -> bool { - // matches!(event, Event::Focus) - // } - - // fn should_zoom_in_on_event(event: &Event) -> bool { - // matches!(event, Event::ZoomIn) - // } - - // fn should_zoom_out_on_event(event: &Event) -> bool { - // matches!(event, Event::ZoomOut) - // } + fn toggle_action(&self) -> Box { + Box::new(ToggleFocus) + } } #[derive(Serialize, Deserialize)] diff --git a/crates/terminal_view2/src/terminal_view.rs b/crates/terminal_view2/src/terminal_view.rs index 1f0836422f..55b7c1af66 100644 --- a/crates/terminal_view2/src/terminal_view.rs +++ b/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.register_action(TerminalView::deploy) + workspace.register_action(TerminalView::deploy); }, ) .detach(); @@ -94,6 +95,12 @@ impl EventEmitter for TerminalView {} impl EventEmitter for TerminalView {} impl EventEmitter 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, FocusableKeyDispatch>; + type Element = Focusable>; fn render(&mut self, cx: &mut ViewContext) -> 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 { diff --git a/crates/workspace2/src/persistence/model.rs b/crates/workspace2/src/persistence/model.rs index 2b8ec94bd4..fde052706b 100644 --- a/crates/workspace2/src/persistence/model.rs +++ b/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 { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 1b8244833a..f28675661d 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/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> = 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(&mut self, cx: &mut ViewContext) -> Option> { - // self.focus_or_unfocus_panel::(cx, |_, _| true)? - // .as_any() - // .clone() - // .downcast() - // } + /// Transfer focus to the panel of the given type. + pub fn focus_panel(&mut self, cx: &mut ViewContext) -> Option> { + let panel = self.focus_or_unfocus_panel::(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, }) }) diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index a1b29f0227..f471ea2306 100644 --- a/crates/zed2/Cargo.toml +++ b/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" } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index abdbe94f2b..ee1a067a29 100644 --- a/crates/zed2/src/main.rs +++ b/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); diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index 8fe5f2447f..84cacccb5a 100644 --- a/crates/zed2/src/zed2.rs +++ b/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, 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, 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, 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);