Cargo.lock 🔗
@@ -11567,6 +11567,7 @@ dependencies = [
"smol",
"sum_tree",
"tempdir",
+ "terminal_view2",
"text2",
"theme2",
"thiserror",
Kirill Bulatov created
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(-)
@@ -11567,6 +11567,7 @@ dependencies = [
"smol",
"sum_tree",
"tempdir",
+ "terminal_view2",
"text2",
"theme2",
"thiserror",
@@ -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>,
) {
@@ -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();
@@ -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> {
@@ -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! {
@@ -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))
+// }
@@ -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)]
@@ -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 {
@@ -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 {
@@ -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,
})
})
@@ -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" }
@@ -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);
@@ -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);