diff --git a/Cargo.lock b/Cargo.lock index a35dfd20cc..a0088a1695 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6609,6 +6609,36 @@ dependencies = [ "workspace", ] +[[package]] +name = "project_panel2" +version = "0.1.0" +dependencies = [ + "anyhow", + "client2", + "collections", + "context_menu", + "db2", + "editor2", + "futures 0.3.28", + "gpui2", + "language2", + "menu2", + "postage", + "pretty_assertions", + "project2", + "schemars", + "serde", + "serde_derive", + "serde_json", + "settings2", + "smallvec", + "theme2", + "ui2", + "unicase", + "util", + "workspace2", +] + [[package]] name = "project_symbols" version = "0.1.0" @@ -9257,9 +9287,9 @@ dependencies = [ [[package]] name = "tiktoken-rs" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9ae5a3c24361e5f038af22517ba7f8e3af4099e30e78a3d56f86b48238fce9d" +checksum = "a4427b6b1c6b38215b92dd47a83a0ecc6735573d0a5a4c14acc0ac5b33b28adb" dependencies = [ "anyhow", "base64 0.21.4", @@ -11418,6 +11448,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "project2", + "project_panel2", "rand 0.8.5", "regex", "rope2", diff --git a/Cargo.toml b/Cargo.toml index 905750f835..6b29b18127 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,7 @@ members = [ "crates/project", "crates/project2", "crates/project_panel", + "crates/project_panel2", "crates/project_symbols", "crates/recent_projects", "crates/rope", @@ -154,6 +155,7 @@ tempdir = { version = "0.3.7" } thiserror = { version = "1.0.29" } time = { version = "0.3", features = ["serde", "serde-well-known"] } toml = { version = "0.5" } +tiktoken-rs = "0.5.7" tree-sitter = "0.20" unindent = { version = "0.1.7" } pretty_assertions = "1.3.0" diff --git a/assets/settings/default.json b/assets/settings/default.json index 42f3b31286..85f8a8fbc4 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -174,7 +174,8 @@ // // 1. "gpt-3.5-turbo-0613"" // 2. "gpt-4-0613"" - "default_open_ai_model": "gpt-4-0613" + // 3. "gpt-4-1106-preview" + "default_open_ai_model": "gpt-4-1106-preview" }, // Whether the screen sharing icon is shown in the os status bar. "show_call_status_icon": true, @@ -270,9 +271,7 @@ "copilot": { // The set of glob patterns for which copilot should be disabled // in any matching file. - "disabled_globs": [ - ".env" - ] + "disabled_globs": [".env"] }, // Settings specific to journaling "journal": { @@ -381,12 +380,7 @@ // Default directories to search for virtual environments, relative // to the current working directory. We recommend overriding this // in your project's settings, rather than globally. - "directories": [ - ".env", - "env", - ".venv", - "venv" - ], + "directories": [".env", "env", ".venv", "venv"], // Can also be 'csh', 'fish', and `nushell` "activate_script": "default" } diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index fb49a4b515..6516e07cd4 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -29,7 +29,7 @@ postage.workspace = true rand.workspace = true log.workspace = true parse_duration = "2.1.1" -tiktoken-rs = "0.5.0" +tiktoken-rs.workspace = true matrixmultiply = "0.3.7" rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] } bincode = "1.3.3" diff --git a/crates/ai2/Cargo.toml b/crates/ai2/Cargo.toml index aee265db6e..25c9965915 100644 --- a/crates/ai2/Cargo.toml +++ b/crates/ai2/Cargo.toml @@ -29,7 +29,7 @@ postage.workspace = true rand.workspace = true log.workspace = true parse_duration = "2.1.1" -tiktoken-rs = "0.5.0" +tiktoken-rs.workspace = true matrixmultiply = "0.3.7" rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] } bincode = "1.3.3" diff --git a/crates/assistant/Cargo.toml b/crates/assistant/Cargo.toml index fc885f6b36..876e5e0b76 100644 --- a/crates/assistant/Cargo.toml +++ b/crates/assistant/Cargo.toml @@ -40,7 +40,7 @@ schemars.workspace = true serde.workspace = true serde_json.workspace = true smol.workspace = true -tiktoken-rs = "0.5" +tiktoken-rs.workspace = true [dev-dependencies] editor = { path = "../editor", features = ["test-support"] } diff --git a/crates/assistant/src/assistant_settings.rs b/crates/assistant/src/assistant_settings.rs index 05d8d9ffeb..65dd588b3c 100644 --- a/crates/assistant/src/assistant_settings.rs +++ b/crates/assistant/src/assistant_settings.rs @@ -9,6 +9,8 @@ pub enum OpenAIModel { ThreePointFiveTurbo, #[serde(rename = "gpt-4-0613")] Four, + #[serde(rename = "gpt-4-1106-preview")] + FourTurbo, } impl OpenAIModel { @@ -16,6 +18,7 @@ impl OpenAIModel { match self { OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo-0613", OpenAIModel::Four => "gpt-4-0613", + OpenAIModel::FourTurbo => "gpt-4-1106-preview", } } @@ -23,13 +26,15 @@ impl OpenAIModel { match self { OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo", OpenAIModel::Four => "gpt-4", + OpenAIModel::FourTurbo => "gpt-4-turbo", } } pub fn cycle(&self) -> Self { match self { OpenAIModel::ThreePointFiveTurbo => OpenAIModel::Four, - OpenAIModel::Four => OpenAIModel::ThreePointFiveTurbo, + OpenAIModel::Four => OpenAIModel::FourTurbo, + OpenAIModel::FourTurbo => OpenAIModel::ThreePointFiveTurbo, } } } diff --git a/crates/editor2/src/display_map/block_map.rs b/crates/editor2/src/display_map/block_map.rs index aa5ff0e3d2..2f65903f08 100644 --- a/crates/editor2/src/display_map/block_map.rs +++ b/crates/editor2/src/display_map/block_map.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{Anchor, Editor, ExcerptId, ExcerptRange, ToPoint as _}; use collections::{Bound, HashMap, HashSet}; -use gpui::{AnyElement, ViewContext}; +use gpui::{AnyElement, Pixels, ViewContext}; use language::{BufferSnapshot, Chunk, Patch, Point}; use parking_lot::Mutex; use std::{ @@ -82,12 +82,11 @@ pub enum BlockStyle { pub struct BlockContext<'a, 'b> { pub view_context: &'b mut ViewContext<'a, Editor>, - pub anchor_x: f32, - pub scroll_x: f32, - pub gutter_width: f32, - pub gutter_padding: f32, - pub em_width: f32, - pub line_height: f32, + pub anchor_x: Pixels, + pub gutter_width: Pixels, + pub gutter_padding: Pixels, + pub em_width: Pixels, + pub line_height: Pixels, pub block_id: usize, } diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index fe98dd8679..ebe78d95b3 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -22,7 +22,7 @@ mod editor_tests; pub mod test; use ::git::diff::DiffHunk; use aho_corasick::AhoCorasick; -use anyhow::{Context as _, Result}; +use anyhow::{anyhow, Context as _, Result}; use blink_manager::BlinkManager; use client::{ClickhouseEvent, Client, Collaborator, ParticipantIndex, TelemetrySettings}; use clock::ReplicaId; @@ -43,8 +43,8 @@ use gpui::{ AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render, - StatelessInteractive, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, - ViewContext, VisualContext, WeakView, WindowContext, + StatefulInteractive, StatelessInteractive, Styled, Subscription, Task, TextStyle, + UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -69,7 +69,7 @@ pub use multi_buffer::{ }; use ordered_float::OrderedFloat; use parking_lot::{Mutex, RwLock}; -use project::{FormatTrigger, Location, Project, ProjectTransaction}; +use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction}; use rand::prelude::*; use rpc::proto::*; use scroll::{ @@ -97,7 +97,7 @@ use text::{OffsetUtf16, Rope}; use theme::{ ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings, }; -use ui::{IconButton, StyledExt}; +use ui::{v_stack, HighlightedLabel, IconButton, StyledExt, TextTooltip}; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{ item::ItemEvent, searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, @@ -7588,53 +7588,47 @@ impl Editor { }) } - // pub fn find_all_references( - // workspace: &mut Workspace, - // _: &FindAllReferences, - // cx: &mut ViewContext, - // ) -> Option>> { - // let active_item = workspace.active_item(cx)?; - // let editor_handle = active_item.act_as::(cx)?; + pub fn find_all_references( + &mut self, + _: &FindAllReferences, + cx: &mut ViewContext, + ) -> Option>> { + let buffer = self.buffer.read(cx); + let head = self.selections.newest::(cx).head(); + let (buffer, head) = buffer.text_anchor_for_position(head, cx)?; + let replica_id = self.replica_id(cx); - // let editor = editor_handle.read(cx); - // let buffer = editor.buffer.read(cx); - // let head = editor.selections.newest::(cx).head(); - // let (buffer, head) = buffer.text_anchor_for_position(head, cx)?; - // let replica_id = editor.replica_id(cx); + let workspace = self.workspace()?; + let project = workspace.read(cx).project().clone(); + let references = project.update(cx, |project, cx| project.references(&buffer, head, cx)); + Some(cx.spawn(|_, mut cx| async move { + let locations = references.await?; + if locations.is_empty() { + return Ok(()); + } - // let project = workspace.project().clone(); - // let references = project.update(cx, |project, cx| project.references(&buffer, head, cx)); - // Some(cx.spawn_labeled( - // "Finding All References...", - // |workspace, mut cx| async move { - // let locations = references.await?; - // if locations.is_empty() { - // return Ok(()); - // } + workspace.update(&mut cx, |workspace, cx| { + let title = locations + .first() + .as_ref() + .map(|location| { + let buffer = location.buffer.read(cx); + format!( + "References to `{}`", + buffer + .text_for_range(location.range.clone()) + .collect::() + ) + }) + .unwrap(); + Self::open_locations_in_multibuffer( + workspace, locations, replica_id, title, false, cx, + ); + })?; - // workspace.update(&mut cx, |workspace, cx| { - // let title = locations - // .first() - // .as_ref() - // .map(|location| { - // let buffer = location.buffer.read(cx); - // format!( - // "References to `{}`", - // buffer - // .text_for_range(location.range.clone()) - // .collect::() - // ) - // }) - // .unwrap(); - // Self::open_locations_in_multibuffer( - // workspace, locations, replica_id, title, false, cx, - // ); - // })?; - - // Ok(()) - // }, - // )) - // } + Ok(()) + })) + } /// Opens a multibuffer with the given project locations in it pub fn open_locations_in_multibuffer( @@ -7685,7 +7679,7 @@ impl Editor { editor.update(cx, |editor, cx| { editor.highlight_background::( ranges_to_highlight, - |theme| todo!("theme.editor.highlighted_line_background"), + |theme| theme.editor_highlighted_line_background, cx, ); }); @@ -8869,46 +8863,50 @@ impl Editor { // }); // } - // fn jump( - // workspace: &mut Workspace, - // path: ProjectPath, - // position: Point, - // anchor: language::Anchor, - // cx: &mut ViewContext, - // ) { - // let editor = workspace.open_path(path, None, true, cx); - // cx.spawn(|_, mut cx| async move { - // let editor = editor - // .await? - // .downcast::() - // .ok_or_else(|| anyhow!("opened item was not an editor"))? - // .downgrade(); - // editor.update(&mut cx, |editor, cx| { - // let buffer = editor - // .buffer() - // .read(cx) - // .as_singleton() - // .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?; - // let buffer = buffer.read(cx); - // let cursor = if buffer.can_resolve(&anchor) { - // language::ToPoint::to_point(&anchor, buffer) - // } else { - // buffer.clip_point(position, Bias::Left) - // }; + fn jump( + &mut self, + path: ProjectPath, + position: Point, + anchor: language::Anchor, + cx: &mut ViewContext, + ) { + let workspace = self.workspace(); + cx.spawn(|_, mut cx| async move { + let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?; + let editor = workspace.update(&mut cx, |workspace, cx| { + workspace.open_path(path, None, true, cx) + })?; + let editor = editor + .await? + .downcast::() + .ok_or_else(|| anyhow!("opened item was not an editor"))? + .downgrade(); + editor.update(&mut cx, |editor, cx| { + let buffer = editor + .buffer() + .read(cx) + .as_singleton() + .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?; + let buffer = buffer.read(cx); + let cursor = if buffer.can_resolve(&anchor) { + language::ToPoint::to_point(&anchor, buffer) + } else { + buffer.clip_point(position, Bias::Left) + }; - // let nav_history = editor.nav_history.take(); - // editor.change_selections(Some(Autoscroll::newest()), cx, |s| { - // s.select_ranges([cursor..cursor]); - // }); - // editor.nav_history = nav_history; + let nav_history = editor.nav_history.take(); + editor.change_selections(Some(Autoscroll::newest()), cx, |s| { + s.select_ranges([cursor..cursor]); + }); + editor.nav_history = nav_history; - // anyhow::Ok(()) - // })??; + anyhow::Ok(()) + })??; - // anyhow::Ok(()) - // }) - // .detach_and_log_err(cx); - // } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } fn marked_text_ranges(&self, cx: &AppContext) -> Option>> { let snapshot = self.buffer.read(cx).read(cx); @@ -9973,43 +9971,22 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend } let message = diagnostic.message; Arc::new(move |cx: &mut BlockContext| { - todo!() - // let message = message.clone(); - // let settings = ThemeSettings::get_global(cx); - // let tooltip_style = settings.theme.tooltip.clone(); - // let theme = &settings.theme.editor; - // let style = diagnostic_style(diagnostic.severity, is_valid, theme); - // let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round(); - // let anchor_x = cx.anchor_x; - // enum BlockContextToolip {} - // MouseEventHandler::new::(cx.block_id, cx, |_, _| { - // Flex::column() - // .with_children(highlighted_lines.iter().map(|(line, highlights)| { - // Label::new( - // line.clone(), - // style.message.clone().with_font_size(font_size), - // ) - // .with_highlights(highlights.clone()) - // .contained() - // .with_margin_left(anchor_x) - // })) - // .aligned() - // .left() - // .into_any() - // }) - // .with_cursor_style(CursorStyle::PointingHand) - // .on_click(MouseButton::Left, move |_, _, cx| { - // cx.write_to_clipboard(ClipboardItem::new(message.clone())); - // }) - // // We really need to rethink this ID system... - // .with_tooltip::( - // cx.block_id, - // "Copy diagnostic message", - // None, - // tooltip_style, - // cx, - // ) - // .into_any() + let message = message.clone(); + v_stack() + .id(cx.block_id) + .size_full() + .bg(gpui::red()) + .children(highlighted_lines.iter().map(|(line, highlights)| { + div() + .child(HighlightedLabel::new(line.clone(), highlights.clone())) + .ml(cx.anchor_x) + })) + .cursor_pointer() + .on_click(move |_, _, cx| { + cx.write_to_clipboard(ClipboardItem::new(message.clone())); + }) + .tooltip(|_, cx| cx.build_view(|cx| TextTooltip::new("Copy diagnostic message"))) + .render() }) } diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 65f6edb18d..638ed33891 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -1,5 +1,8 @@ use crate::{ - display_map::{BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint}, + display_map::{ + BlockContext, BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint, + TransformBlock, + }, editor_settings::ShowScrollbar, git::{diff_hunk_to_display, DisplayDiffHunk}, hover_popover::hover_at, @@ -15,17 +18,19 @@ use crate::{ use anyhow::Result; use collections::{BTreeMap, HashMap}; use gpui::{ - black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, - BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase, Edges, Element, - ElementId, ElementInputHandler, Entity, FocusHandle, GlobalElementId, Hsla, InputHandler, - KeyContext, KeyDownEvent, KeyMatch, Line, LineLayout, Modifiers, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ShapedGlyph, Size, Style, TextRun, - TextStyle, TextSystem, ViewContext, WindowContext, WrappedLineLayout, + point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, BorrowWindow, + Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, ElementId, + ElementInputHandler, Entity, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, ParentElement, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun, TextStyle, + ViewContext, WindowContext, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; use multi_buffer::Anchor; -use project::project_settings::{GitGutterSetting, ProjectSettings}; +use project::{ + project_settings::{GitGutterSetting, ProjectSettings}, + ProjectPath, +}; use settings::Settings; use smallvec::SmallVec; use std::{ @@ -39,6 +44,7 @@ use std::{ }; use sum_tree::Bias; use theme::{ActiveTheme, PlayerColor}; +use ui::{h_stack, IconButton}; use util::ResultExt; use workspace::item::Item; @@ -1171,30 +1177,31 @@ impl EditorElement { } } - // fn paint_blocks( - // &mut self, - // bounds: Bounds, - // visible_bounds: Bounds, - // layout: &mut LayoutState, - // editor: &mut Editor, - // cx: &mut ViewContext, - // ) { - // let scroll_position = layout.position_map.snapshot.scroll_position(); - // let scroll_left = scroll_position.x * layout.position_map.em_width; - // let scroll_top = scroll_position.y * layout.position_map.line_height; + fn paint_blocks( + &mut self, + bounds: Bounds, + layout: &mut LayoutState, + editor: &mut Editor, + cx: &mut ViewContext, + ) { + let scroll_position = layout.position_map.snapshot.scroll_position(); + let scroll_left = scroll_position.x * layout.position_map.em_width; + let scroll_top = scroll_position.y * layout.position_map.line_height; - // for block in &mut layout.blocks { - // let mut origin = bounds.origin - // + point( - // 0., - // block.row as f32 * layout.position_map.line_height - scroll_top, - // ); - // if !matches!(block.style, BlockStyle::Sticky) { - // origin += point(-scroll_left, 0.); - // } - // block.element.paint(origin, visible_bounds, editor, cx); - // } - // } + for block in &mut layout.blocks { + let mut origin = bounds.origin + + point( + Pixels::ZERO, + block.row as f32 * layout.position_map.line_height - scroll_top, + ); + if !matches!(block.style, BlockStyle::Sticky) { + origin += point(-scroll_left, Pixels::ZERO); + } + block + .element + .draw(origin, block.available_space, editor, cx); + } + } fn column_pixels(&self, column: usize, cx: &ViewContext) -> Pixels { let style = &self.style; @@ -1741,22 +1748,22 @@ impl EditorElement { .unwrap() .width; let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width; - // todo!("blocks") - // let (scroll_width, blocks) = self.layout_blocks( - // start_row..end_row, - // &snapshot, - // size.x, - // scroll_width, - // gutter_padding, - // gutter_width, - // em_width, - // gutter_width + gutter_margin, - // line_height, - // &style, - // &line_layouts, - // editor, - // cx, - // ); + + let (scroll_width, blocks) = self.layout_blocks( + start_row..end_row, + &snapshot, + bounds.size.width, + scroll_width, + gutter_padding, + gutter_width, + em_width, + gutter_width + gutter_margin, + line_height, + &style, + &line_layouts, + editor, + cx, + ); let scroll_max = point( f32::from((scroll_width - text_size.width) / em_width).max(0.0), @@ -1937,7 +1944,7 @@ impl EditorElement { fold_ranges, line_number_layouts, display_hunks, - // blocks, + blocks, selections, context_menu, code_actions_indicator, @@ -1948,226 +1955,177 @@ impl EditorElement { } } - // #[allow(clippy::too_many_arguments)] - // fn layout_blocks( - // &mut self, - // rows: Range, - // snapshot: &EditorSnapshot, - // editor_width: f32, - // scroll_width: f32, - // gutter_padding: f32, - // gutter_width: f32, - // em_width: f32, - // text_x: f32, - // line_height: f32, - // style: &EditorStyle, - // line_layouts: &[LineWithInvisibles], - // editor: &mut Editor, - // cx: &mut ViewContext, - // ) -> (f32, Vec) { - // let mut block_id = 0; - // let scroll_x = snapshot.scroll_anchor.offset.x; - // let (fixed_blocks, non_fixed_blocks) = snapshot - // .blocks_in_range(rows.clone()) - // .partition::, _>(|(_, block)| match block { - // TransformBlock::ExcerptHeader { .. } => false, - // TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed, - // }); - // let mut render_block = |block: &TransformBlock, width: f32, block_id: usize| { - // let mut element = match block { - // TransformBlock::Custom(block) => { - // let align_to = block - // .position() - // .to_point(&snapshot.buffer_snapshot) - // .to_display_point(snapshot); - // let anchor_x = text_x - // + if rows.contains(&align_to.row()) { - // line_layouts[(align_to.row() - rows.start) as usize] - // .line - // .x_for_index(align_to.column() as usize) - // } else { - // layout_line(align_to.row(), snapshot, style, cx.text_layout_cache()) - // .x_for_index(align_to.column() as usize) - // }; + #[allow(clippy::too_many_arguments)] + fn layout_blocks( + &mut self, + rows: Range, + snapshot: &EditorSnapshot, + editor_width: Pixels, + scroll_width: Pixels, + gutter_padding: Pixels, + gutter_width: Pixels, + em_width: Pixels, + text_x: Pixels, + line_height: Pixels, + style: &EditorStyle, + line_layouts: &[LineWithInvisibles], + editor: &mut Editor, + cx: &mut ViewContext, + ) -> (Pixels, Vec) { + let mut block_id = 0; + let scroll_x = snapshot.scroll_anchor.offset.x; + let (fixed_blocks, non_fixed_blocks) = snapshot + .blocks_in_range(rows.clone()) + .partition::, _>(|(_, block)| match block { + TransformBlock::ExcerptHeader { .. } => false, + TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed, + }); + let mut render_block = |block: &TransformBlock, + available_space: Size, + block_id: usize, + editor: &mut Editor, + cx: &mut ViewContext| { + let mut element = match block { + TransformBlock::Custom(block) => { + let align_to = block + .position() + .to_point(&snapshot.buffer_snapshot) + .to_display_point(snapshot); + let anchor_x = text_x + + if rows.contains(&align_to.row()) { + line_layouts[(align_to.row() - rows.start) as usize] + .line + .x_for_index(align_to.column() as usize) + } else { + layout_line(align_to.row(), snapshot, style, cx) + .unwrap() + .x_for_index(align_to.column() as usize) + }; - // block.render(&mut BlockContext { - // view_context: cx, - // anchor_x, - // gutter_padding, - // line_height, - // scroll_x, - // gutter_width, - // em_width, - // block_id, - // }) - // } - // TransformBlock::ExcerptHeader { - // id, - // buffer, - // range, - // starts_new_buffer, - // .. - // } => { - // let tooltip_style = theme::current(cx).tooltip.clone(); - // let include_root = editor - // .project - // .as_ref() - // .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) - // .unwrap_or_default(); - // let jump_icon = project::File::from_dyn(buffer.file()).map(|file| { - // let jump_path = ProjectPath { - // worktree_id: file.worktree_id(cx), - // path: file.path.clone(), - // }; - // let jump_anchor = range - // .primary - // .as_ref() - // .map_or(range.context.start, |primary| primary.start); - // let jump_position = language::ToPoint::to_point(&jump_anchor, buffer); + block.render(&mut BlockContext { + view_context: cx, + anchor_x, + gutter_padding, + line_height, + // scroll_x, + gutter_width, + em_width, + block_id, + }) + } + TransformBlock::ExcerptHeader { + id, + buffer, + range, + starts_new_buffer, + .. + } => { + let include_root = editor + .project + .as_ref() + .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) + .unwrap_or_default(); + let jump_icon = project::File::from_dyn(buffer.file()).map(|file| { + let jump_path = ProjectPath { + worktree_id: file.worktree_id(cx), + path: file.path.clone(), + }; + let jump_anchor = range + .primary + .as_ref() + .map_or(range.context.start, |primary| primary.start); + let jump_position = language::ToPoint::to_point(&jump_anchor, buffer); - // enum JumpIcon {} - // MouseEventHandler::new::((*id).into(), cx, |state, _| { - // let style = style.jump_icon.style_for(state); - // Svg::new("icons/arrow_up_right.svg") - // .with_color(style.color) - // .constrained() - // .with_width(style.icon_width) - // .aligned() - // .contained() - // .with_style(style.container) - // .constrained() - // .with_width(style.button_width) - // .with_height(style.button_width) - // }) - // .with_cursor_style(CursorStyle::PointingHand) - // .on_click(MouseButton::Left, move |_, editor, cx| { - // if let Some(workspace) = editor - // .workspace - // .as_ref() - // .and_then(|(workspace, _)| workspace.upgrade(cx)) - // { - // workspace.update(cx, |workspace, cx| { - // Editor::jump( - // workspace, - // jump_path.clone(), - // jump_position, - // jump_anchor, - // cx, - // ); - // }); - // } - // }) - // .with_tooltip::( - // (*id).into(), - // "Jump to Buffer".to_string(), - // Some(Box::new(crate::OpenExcerpts)), - // tooltip_style.clone(), - // cx, - // ) - // .aligned() - // .flex_float() - // }); + // todo!("avoid ElementId collision risk here") + let icon_button_id: usize = id.clone().into(); + IconButton::new(icon_button_id, ui::Icon::ArrowUpRight) + .on_click(move |editor: &mut Editor, cx| { + editor.jump(jump_path.clone(), jump_position, jump_anchor, cx); + }) + .tooltip("Jump to Buffer") // todo!(pass an action as well to show key binding) + }); - // if *starts_new_buffer { - // let editor_font_size = style.text.font_size; - // let style = &style.diagnostic_path_header; - // let font_size = (style.text_scale_factor * editor_font_size).round(); + let element = if *starts_new_buffer { + let path = buffer.resolve_file_path(cx, include_root); + let mut filename = None; + let mut parent_path = None; + // Can't use .and_then() because `.file_name()` and `.parent()` return references :( + if let Some(path) = path { + filename = path.file_name().map(|f| f.to_string_lossy().to_string()); + parent_path = + path.parent().map(|p| p.to_string_lossy().to_string() + "/"); + } - // let path = buffer.resolve_file_path(cx, include_root); - // let mut filename = None; - // let mut parent_path = None; - // // Can't use .and_then() because `.file_name()` and `.parent()` return references :( - // if let Some(path) = path { - // filename = path.file_name().map(|f| f.to_string_lossy.to_string()); - // parent_path = - // path.parent().map(|p| p.to_string_lossy.to_string() + "/"); - // } + h_stack() + .size_full() + .bg(gpui::red()) + .child(filename.unwrap_or_else(|| "untitled".to_string())) + .children(parent_path) + .children(jump_icon) // .p_x(gutter_padding) + } else { + let text_style = style.text.clone(); + h_stack() + .size_full() + .bg(gpui::red()) + .child("⋯") + .children(jump_icon) // .p_x(gutter_padding) + }; + element.render() + } + }; - // Flex::row() - // .with_child( - // Label::new( - // filename.unwrap_or_else(|| "untitled".to_string()), - // style.filename.text.clone().with_font_size(font_size), - // ) - // .contained() - // .with_style(style.filename.container) - // .aligned(), - // ) - // .with_children(parent_path.map(|path| { - // Label::new(path, style.path.text.clone().with_font_size(font_size)) - // .contained() - // .with_style(style.path.container) - // .aligned() - // })) - // .with_children(jump_icon) - // .contained() - // .with_style(style.container) - // .with_padding_left(gutter_padding) - // .with_padding_right(gutter_padding) - // .expanded() - // .into_any_named("path header block") - // } else { - // let text_style = style.text.clone(); - // Flex::row() - // .with_child(Label::new("⋯", text_style)) - // .with_children(jump_icon) - // .contained() - // .with_padding_left(gutter_padding) - // .with_padding_right(gutter_padding) - // .expanded() - // .into_any_named("collapsed context") - // } - // } - // }; + let size = element.measure(available_space, editor, cx); + (element, size) + }; - // element.layout( - // SizeConstraint { - // min: gpui::Point::::zero(), - // max: point(width, block.height() as f32 * line_height), - // }, - // editor, - // cx, - // ); - // element - // }; - - // let mut fixed_block_max_width = 0f32; - // let mut blocks = Vec::new(); - // for (row, block) in fixed_blocks { - // let element = render_block(block, f32::INFINITY, block_id); - // block_id += 1; - // fixed_block_max_width = fixed_block_max_width.max(element.size().x + em_width); - // blocks.push(BlockLayout { - // row, - // element, - // style: BlockStyle::Fixed, - // }); - // } - // for (row, block) in non_fixed_blocks { - // let style = match block { - // TransformBlock::Custom(block) => block.style(), - // TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky, - // }; - // let width = match style { - // BlockStyle::Sticky => editor_width, - // BlockStyle::Flex => editor_width - // .max(fixed_block_max_width) - // .max(gutter_width + scroll_width), - // BlockStyle::Fixed => unreachable!(), - // }; - // let element = render_block(block, width, block_id); - // block_id += 1; - // blocks.push(BlockLayout { - // row, - // element, - // style, - // }); - // } - // ( - // scroll_width.max(fixed_block_max_width - gutter_width), - // blocks, - // ) - // } + let mut fixed_block_max_width = Pixels::ZERO; + let mut blocks = Vec::new(); + for (row, block) in fixed_blocks { + let available_space = size( + AvailableSpace::MinContent, + AvailableSpace::Definite(block.height() as f32 * line_height), + ); + let (element, element_size) = + render_block(block, available_space, block_id, editor, cx); + block_id += 1; + fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width); + blocks.push(BlockLayout { + row, + element, + available_space, + style: BlockStyle::Fixed, + }); + } + for (row, block) in non_fixed_blocks { + let style = match block { + TransformBlock::Custom(block) => block.style(), + TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky, + }; + let width = match style { + BlockStyle::Sticky => editor_width, + BlockStyle::Flex => editor_width + .max(fixed_block_max_width) + .max(gutter_width + scroll_width), + BlockStyle::Fixed => unreachable!(), + }; + let available_space = size( + AvailableSpace::Definite(width), + AvailableSpace::Definite(block.height() as f32 * line_height), + ); + let (element, _) = render_block(block, available_space, block_id, editor, cx); + block_id += 1; + blocks.push(BlockLayout { + row, + element, + available_space, + style, + }); + } + ( + scroll_width.max(fixed_block_max_width - gutter_width), + blocks, + ) + } fn paint_mouse_listeners( &mut self, @@ -2613,7 +2571,11 @@ impl Element for EditorElement { }); // on_action(cx, Editor::rename); todo!() // on_action(cx, Editor::confirm_rename); todo!() - // on_action(cx, Editor::find_all_references); todo!() + register_action(cx, |editor, action, cx| { + editor + .find_all_references(action, cx) + .map(|task| task.detach_and_log_err(cx)); + }); register_action(cx, Editor::next_copilot_suggestion); register_action(cx, Editor::previous_copilot_suggestion); register_action(cx, Editor::copilot_suggest); @@ -2670,11 +2632,18 @@ impl Element for EditorElement { &layout.position_map, cx, ); + self.paint_background(gutter_bounds, text_bounds, &layout, cx); if layout.gutter_size.width > Pixels::ZERO { self.paint_gutter(gutter_bounds, &mut layout, editor, cx); } + self.paint_text(text_bounds, &mut layout, editor, cx); + + if !layout.blocks.is_empty() { + self.paint_blocks(bounds, &mut layout, editor, cx); + } + let input_handler = ElementInputHandler::new(bounds, cx); cx.handle_input(&editor.focus_handle, input_handler); }); @@ -3295,7 +3264,7 @@ pub struct LayoutState { highlighted_rows: Option>, line_number_layouts: Vec>, display_hunks: Vec, - // blocks: Vec, + blocks: Vec, highlighted_ranges: Vec<(Range, Hsla)>, fold_ranges: Vec<(BufferRow, Range, Hsla)>, selections: Vec<(PlayerColor, Vec)>, @@ -3398,6 +3367,7 @@ impl PositionMap { struct BlockLayout { row: u32, element: AnyElement, + available_space: Size, style: BlockStyle, } diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index 06e93e275d..16487cf18a 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -68,8 +68,12 @@ where A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static, { fn qualified_name() -> SharedString { + let name = type_name::(); + let mut separator_matches = name.rmatch_indices("::"); + separator_matches.next().unwrap(); + let name_start_ix = separator_matches.next().map_or(0, |(ix, _)| ix + 2); // todo!() remove the 2 replacement when migration is done - type_name::().replace("2::", "::").into() + name[name_start_ix..].replace("2::", "::").into() } fn build(params: Option) -> Result> diff --git a/crates/gpui2/src/color.rs b/crates/gpui2/src/color.rs index 5f6308ec4f..44a0e917be 100644 --- a/crates/gpui2/src/color.rs +++ b/crates/gpui2/src/color.rs @@ -293,7 +293,16 @@ pub fn blue() -> Hsla { pub fn green() -> Hsla { Hsla { - h: 0.3, + h: 0.33, + s: 1., + l: 0.5, + a: 1., + } +} + +pub fn yellow() -> Hsla { + Hsla { + h: 0.16, s: 1., l: 0.5, a: 1., diff --git a/crates/gpui2/src/text_system/line_layout.rs b/crates/gpui2/src/text_system/line_layout.rs index db7140b040..7e9176caca 100644 --- a/crates/gpui2/src/text_system/line_layout.rs +++ b/crates/gpui2/src/text_system/line_layout.rs @@ -54,9 +54,9 @@ impl LineLayout { pub fn closest_index_for_x(&self, x: Pixels) -> usize { let mut prev_index = 0; let mut prev_x = px(0.); + for run in self.runs.iter() { for glyph in run.glyphs.iter() { - glyph.index; if glyph.position.x >= x { if glyph.position.x - x < x - prev_x { return glyph.index; @@ -68,7 +68,8 @@ impl LineLayout { prev_x = glyph.position.x; } } - prev_index + 1 + + self.len } pub fn x_for_index(&self, index: usize) -> Pixels { diff --git a/crates/project_panel2/Cargo.toml b/crates/project_panel2/Cargo.toml new file mode 100644 index 0000000000..bd6bc59a65 --- /dev/null +++ b/crates/project_panel2/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "project_panel2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/project_panel.rs" +doctest = false + +[dependencies] +context_menu = { path = "../context_menu" } +collections = { path = "../collections" } +db = { path = "../db2", package = "db2" } +editor = { path = "../editor2", package = "editor2" } +gpui = { path = "../gpui2", package = "gpui2" } +menu = { path = "../menu2", package = "menu2" } +project = { path = "../project2", package = "project2" } +settings = { path = "../settings2", package = "settings2" } +theme = { path = "../theme2", package = "theme2" } +ui = { path = "../ui2", package = "ui2" } +util = { path = "../util" } +workspace = { path = "../workspace2", package = "workspace2" } +anyhow.workspace = true +postage.workspace = true +futures.workspace = true +serde.workspace = true +serde_derive.workspace = true +serde_json.workspace = true +schemars.workspace = true +smallvec.workspace = true +pretty_assertions.workspace = true +unicase = "2.6" + +[dev-dependencies] +client = { path = "../client2", package = "client2", features = ["test-support"] } +language = { path = "../language2", package = "language2", features = ["test-support"] } +editor = { path = "../editor2", package = "editor2", features = ["test-support"] } +gpui = { path = "../gpui2", package = "gpui2", features = ["test-support"] } +workspace = { path = "../workspace2", package = "workspace2", features = ["test-support"] } +serde_json.workspace = true diff --git a/crates/project_panel2/src/file_associations.rs b/crates/project_panel2/src/file_associations.rs new file mode 100644 index 0000000000..9e9a865f3e --- /dev/null +++ b/crates/project_panel2/src/file_associations.rs @@ -0,0 +1,96 @@ +use std::{path::Path, str, sync::Arc}; + +use collections::HashMap; + +use gpui::{AppContext, AssetSource}; +use serde_derive::Deserialize; +use util::{maybe, paths::PathExt}; + +#[derive(Deserialize, Debug)] +struct TypeConfig { + icon: Arc, +} + +#[derive(Deserialize, Debug)] +pub struct FileAssociations { + suffixes: HashMap, + types: HashMap, +} + +const COLLAPSED_DIRECTORY_TYPE: &'static str = "collapsed_folder"; +const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_folder"; +const COLLAPSED_CHEVRON_TYPE: &'static str = "collapsed_chevron"; +const EXPANDED_CHEVRON_TYPE: &'static str = "expanded_chevron"; +pub const FILE_TYPES_ASSET: &'static str = "icons/file_icons/file_types.json"; + +pub fn init(assets: impl AssetSource, cx: &mut AppContext) { + cx.set_global(FileAssociations::new(assets)) +} + +impl FileAssociations { + pub fn new(assets: impl AssetSource) -> Self { + assets + .load("icons/file_icons/file_types.json") + .and_then(|file| { + serde_json::from_str::(str::from_utf8(&file).unwrap()) + .map_err(Into::into) + }) + .unwrap_or_else(|_| FileAssociations { + suffixes: HashMap::default(), + types: HashMap::default(), + }) + } + + pub fn get_icon(path: &Path, cx: &AppContext) -> Arc { + maybe!({ + let this = cx.has_global::().then(|| cx.global::())?; + + // FIXME: Associate a type with the languages and have the file's langauge + // override these associations + maybe!({ + let suffix = path.icon_suffix()?; + + this.suffixes + .get(suffix) + .and_then(|type_str| this.types.get(type_str)) + .map(|type_config| type_config.icon.clone()) + }) + .or_else(|| this.types.get("default").map(|config| config.icon.clone())) + }) + .unwrap_or_else(|| Arc::from("".to_string())) + } + + pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Arc { + maybe!({ + let this = cx.has_global::().then(|| cx.global::())?; + + let key = if expanded { + EXPANDED_DIRECTORY_TYPE + } else { + COLLAPSED_DIRECTORY_TYPE + }; + + this.types + .get(key) + .map(|type_config| type_config.icon.clone()) + }) + .unwrap_or_else(|| Arc::from("".to_string())) + } + + pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Arc { + maybe!({ + let this = cx.has_global::().then(|| cx.global::())?; + + let key = if expanded { + EXPANDED_CHEVRON_TYPE + } else { + COLLAPSED_CHEVRON_TYPE + }; + + this.types + .get(key) + .map(|type_config| type_config.icon.clone()) + }) + .unwrap_or_else(|| Arc::from("".to_string())) + } +} diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index e3e04f5254..1feead1a19 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/crates/project_panel2/src/project_panel.rs @@ -8,10 +8,10 @@ use file_associations::FileAssociations; use anyhow::{anyhow, Result}; use gpui::{ - actions, div, px, svg, uniform_list, Action, AppContext, AssetSource, AsyncAppContext, - AsyncWindowContext, ClipboardItem, Div, Element, Entity, EventEmitter, FocusEnabled, - FocusHandle, Model, ParentElement as _, Pixels, Point, PromptLevel, Render, - StatefulInteractive, StatefulInteractivity, Styled, Task, UniformListScrollHandle, View, + actions, div, px, rems, svg, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext, + ClipboardItem, Component, Div, EventEmitter, FocusHandle, FocusableKeyDispatch, Model, + MouseButton, ParentElement as _, Pixels, Point, PromptLevel, Render, StatefulInteractive, + StatefulInteractivity, StatelessInteractive, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext as _, WeakView, WindowContext, }; use menu::{Confirm, SelectNext, SelectPrev}; @@ -31,9 +31,9 @@ use std::{ sync::Arc, }; use theme::ActiveTheme as _; -use ui::{h_stack, v_stack}; +use ui::{h_stack, v_stack, Label}; use unicase::UniCase; -use util::TryFutureExt; +use util::{maybe, TryFutureExt}; use workspace::{ dock::{DockPosition, PanelEvent}, Workspace, @@ -54,8 +54,8 @@ pub struct ProjectPanel { edit_state: Option, filename_editor: View, clipboard_entry: Option, - dragged_entry_destination: Option>, - workspace: WeakView, + _dragged_entry_destination: Option>, + _workspace: WeakView, has_focus: bool, width: Option, pending_serialization: Task>, @@ -131,31 +131,6 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(assets: impl AssetSource, cx: &mut AppContext) { init_settings(cx); file_associations::init(assets, cx); - - // cx.add_action(ProjectPanel::expand_selected_entry); - // cx.add_action(ProjectPanel::collapse_selected_entry); - // cx.add_action(ProjectPanel::collapse_all_entries); - // cx.add_action(ProjectPanel::select_prev); - // cx.add_action(ProjectPanel::select_next); - // cx.add_action(ProjectPanel::new_file); - // cx.add_action(ProjectPanel::new_directory); - // cx.add_action(ProjectPanel::rename); - // cx.add_async_action(ProjectPanel::delete); - // cx.add_async_action(ProjectPanel::confirm); - // cx.add_async_action(ProjectPanel::open_file); - // cx.add_action(ProjectPanel::cancel); - // cx.add_action(ProjectPanel::cut); - // cx.add_action(ProjectPanel::copy); - // cx.add_action(ProjectPanel::copy_path); - // cx.add_action(ProjectPanel::copy_relative_path); - // cx.add_action(ProjectPanel::reveal_in_finder); - // cx.add_action(ProjectPanel::open_in_terminal); - // cx.add_action(ProjectPanel::new_search_in_directory); - // cx.add_action( - // |this: &mut ProjectPanel, action: &Paste, cx: &mut ViewContext| { - // this.paste(action, cx); - // }, - // ); } #[derive(Debug)] @@ -244,7 +219,6 @@ impl ProjectPanel { // }) // .detach(); - let view_id = cx.view().entity_id(); let mut this = Self { project: project.clone(), fs: workspace.app_state().fs.clone(), @@ -258,8 +232,8 @@ impl ProjectPanel { filename_editor, clipboard_entry: None, // context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)), - dragged_entry_destination: None, - workspace: workspace.weak_handle(), + _dragged_entry_destination: None, + _workspace: workspace.weak_handle(), has_focus: false, width: None, pending_serialization: Task::ready(None), @@ -311,19 +285,19 @@ impl ProjectPanel { } } &Event::SplitEntry { entry_id } => { - // if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) { - // if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) { - // workspace - // .split_path( - // ProjectPath { - // worktree_id: worktree.read(cx).id(), - // path: entry.path.clone(), - // }, - // cx, - // ) - // .detach_and_log_err(cx); - // } - // } + if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) { + if let Some(_entry) = worktree.read(cx).entry_for_id(entry_id) { + // workspace + // .split_path( + // ProjectPath { + // worktree_id: worktree.read(cx).id(), + // path: entry.path.clone(), + // }, + // cx, + // ) + // .detach_and_log_err(cx); + } + } } _ => {} } @@ -391,79 +365,80 @@ impl ProjectPanel { fn deploy_context_menu( &mut self, - position: Point, - entry_id: ProjectEntryId, - cx: &mut ViewContext, + _position: Point, + _entry_id: ProjectEntryId, + _cx: &mut ViewContext, ) { - // let project = self.project.read(cx); + todo!() + // let project = self.project.read(cx); - // let worktree_id = if let Some(id) = project.worktree_id_for_entry(entry_id, cx) { - // id - // } else { - // return; - // }; + // let worktree_id = if let Some(id) = project.worktree_id_for_entry(entry_id, cx) { + // id + // } else { + // return; + // }; - // self.selection = Some(Selection { - // worktree_id, - // entry_id, - // }); + // self.selection = Some(Selection { + // worktree_id, + // entry_id, + // }); - // let mut menu_entries = Vec::new(); - // if let Some((worktree, entry)) = self.selected_entry(cx) { - // let is_root = Some(entry) == worktree.root_entry(); - // if !project.is_remote() { - // menu_entries.push(ContextMenuItem::action( - // "Add Folder to Project", - // workspace::AddFolderToProject, - // )); - // if is_root { - // let project = self.project.clone(); - // menu_entries.push(ContextMenuItem::handler("Remove from Project", move |cx| { - // project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); - // })); + // let mut menu_entries = Vec::new(); + // if let Some((worktree, entry)) = self.selected_entry(cx) { + // let is_root = Some(entry) == worktree.root_entry(); + // if !project.is_remote() { + // menu_entries.push(ContextMenuItem::action( + // "Add Folder to Project", + // workspace::AddFolderToProject, + // )); + // if is_root { + // let project = self.project.clone(); + // menu_entries.push(ContextMenuItem::handler("Remove from Project", move |cx| { + // project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); + // })); + // } // } - // } - // menu_entries.push(ContextMenuItem::action("New File", NewFile)); - // menu_entries.push(ContextMenuItem::action("New Folder", NewDirectory)); - // menu_entries.push(ContextMenuItem::Separator); - // menu_entries.push(ContextMenuItem::action("Cut", Cut)); - // menu_entries.push(ContextMenuItem::action("Copy", Copy)); - // if let Some(clipboard_entry) = self.clipboard_entry { - // if clipboard_entry.worktree_id() == worktree.id() { - // menu_entries.push(ContextMenuItem::action("Paste", Paste)); - // } - // } - // menu_entries.push(ContextMenuItem::Separator); - // menu_entries.push(ContextMenuItem::action("Copy Path", CopyPath)); - // menu_entries.push(ContextMenuItem::action( - // "Copy Relative Path", - // CopyRelativePath, - // )); - - // if entry.is_dir() { + // menu_entries.push(ContextMenuItem::action("New File", NewFile)); + // menu_entries.push(ContextMenuItem::action("New Folder", NewDirectory)); // menu_entries.push(ContextMenuItem::Separator); - // } - // menu_entries.push(ContextMenuItem::action("Reveal in Finder", RevealInFinder)); - // if entry.is_dir() { - // menu_entries.push(ContextMenuItem::action("Open in Terminal", OpenInTerminal)); + // menu_entries.push(ContextMenuItem::action("Cut", Cut)); + // menu_entries.push(ContextMenuItem::action("Copy", Copy)); + // if let Some(clipboard_entry) = self.clipboard_entry { + // if clipboard_entry.worktree_id() == worktree.id() { + // menu_entries.push(ContextMenuItem::action("Paste", Paste)); + // } + // } + // menu_entries.push(ContextMenuItem::Separator); + // menu_entries.push(ContextMenuItem::action("Copy Path", CopyPath)); // menu_entries.push(ContextMenuItem::action( - // "Search Inside", - // NewSearchInDirectory, + // "Copy Relative Path", + // CopyRelativePath, // )); + + // if entry.is_dir() { + // menu_entries.push(ContextMenuItem::Separator); + // } + // menu_entries.push(ContextMenuItem::action("Reveal in Finder", RevealInFinder)); + // if entry.is_dir() { + // menu_entries.push(ContextMenuItem::action("Open in Terminal", OpenInTerminal)); + // menu_entries.push(ContextMenuItem::action( + // "Search Inside", + // NewSearchInDirectory, + // )); + // } + + // menu_entries.push(ContextMenuItem::Separator); + // menu_entries.push(ContextMenuItem::action("Rename", Rename)); + // if !is_root { + // menu_entries.push(ContextMenuItem::action("Delete", Delete)); + // } // } - // menu_entries.push(ContextMenuItem::Separator); - // menu_entries.push(ContextMenuItem::action("Rename", Rename)); - // if !is_root { - // menu_entries.push(ContextMenuItem::action("Delete", Delete)); - // } - // } + // // self.context_menu.update(cx, |menu, cx| { + // // menu.show(position, AnchorCorner::TopLeft, menu_entries, cx); + // // }); - // // self.context_menu.update(cx, |menu, cx| { - // // menu.show(position, AnchorCorner::TopLeft, menu_entries, cx); - // // }); - - // cx.notify(); + // cx.notify(); } fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext) { @@ -579,22 +554,18 @@ impl ProjectPanel { } } - fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) -> Option>> { + fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { if let Some(task) = self.confirm_edit(cx) { - return Some(task); + task.detach_and_log_err(cx); } - - None } - fn open_file(&mut self, _: &Open, cx: &mut ViewContext) -> Option>> { + fn open_file(&mut self, _: &Open, cx: &mut ViewContext) { if let Some((_, entry)) = self.selected_entry(cx) { if entry.is_file() { self.open_entry(entry.id, true, cx); } } - - None } fn confirm_edit(&mut self, cx: &mut ViewContext) -> Option>> { @@ -800,27 +771,32 @@ impl ProjectPanel { } } - fn delete(&mut self, _: &Delete, cx: &mut ViewContext) -> Option>> { - let Selection { entry_id, .. } = self.selection?; - let path = self.project.read(cx).path_for_entry(entry_id, cx)?.path; - let file_name = path.file_name()?; + fn delete(&mut self, _: &Delete, cx: &mut ViewContext) { + maybe!({ + let Selection { entry_id, .. } = self.selection?; + let path = self.project.read(cx).path_for_entry(entry_id, cx)?.path; + let file_name = path.file_name()?; - let mut answer = cx.prompt( - PromptLevel::Info, - &format!("Delete {file_name:?}?"), - &["Delete", "Cancel"], - ); - Some(cx.spawn(|this, mut cx| async move { - if answer.await != Ok(0) { - return Ok(()); - } - this.update(&mut cx, |this, cx| { - this.project - .update(cx, |project, cx| project.delete_entry(entry_id, cx)) - .ok_or_else(|| anyhow!("no such entry")) - })?? - .await - })) + let answer = cx.prompt( + PromptLevel::Info, + &format!("Delete {file_name:?}?"), + &["Delete", "Cancel"], + ); + + cx.spawn(|this, mut cx| async move { + if answer.await != Ok(0) { + return Ok(()); + } + this.update(&mut cx, |this, cx| { + this.project + .update(cx, |project, cx| project.delete_entry(entry_id, cx)) + .ok_or_else(|| anyhow!("no such entry")) + })?? + .await + }) + .detach_and_log_err(cx); + Some(()) + }); } fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { @@ -897,8 +873,9 @@ impl ProjectPanel { } } - fn paste(&mut self, _: &Paste, cx: &mut ViewContext) -> Option<()> { - if let Some((worktree, entry)) = self.selected_entry(cx) { + fn paste(&mut self, _: &Paste, cx: &mut ViewContext) { + maybe!({ + let (worktree, entry) = self.selected_entry(cx)?; let clipboard_entry = self.clipboard_entry?; if clipboard_entry.worktree_id() != worktree.id() { return None; @@ -942,15 +919,16 @@ impl ProjectPanel { if let Some(task) = self.project.update(cx, |project, cx| { project.rename_entry(clipboard_entry.entry_id(), new_path, cx) }) { - task.detach_and_log_err(cx) + task.detach_and_log_err(cx); } } else if let Some(task) = self.project.update(cx, |project, cx| { project.copy_entry(clipboard_entry.entry_id(), new_path, cx) }) { - task.detach_and_log_err(cx) + task.detach_and_log_err(cx); } - } - None + + Some(()) + }); } fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext) { @@ -977,7 +955,7 @@ impl ProjectPanel { } } - fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext) { + fn open_in_terminal(&mut self, _: &OpenInTerminal, _cx: &mut ViewContext) { todo!() // if let Some((worktree, entry)) = self.selected_entry(cx) { // let window = cx.window(); @@ -1012,36 +990,37 @@ impl ProjectPanel { } } - fn move_entry( - &mut self, - entry_to_move: ProjectEntryId, - destination: ProjectEntryId, - destination_is_file: bool, - cx: &mut ViewContext, - ) { - let destination_worktree = self.project.update(cx, |project, cx| { - let entry_path = project.path_for_entry(entry_to_move, cx)?; - let destination_entry_path = project.path_for_entry(destination, cx)?.path.clone(); + // todo!() + // fn move_entry( + // &mut self, + // entry_to_move: ProjectEntryId, + // destination: ProjectEntryId, + // destination_is_file: bool, + // cx: &mut ViewContext, + // ) { + // let destination_worktree = self.project.update(cx, |project, cx| { + // let entry_path = project.path_for_entry(entry_to_move, cx)?; + // let destination_entry_path = project.path_for_entry(destination, cx)?.path.clone(); - let mut destination_path = destination_entry_path.as_ref(); - if destination_is_file { - destination_path = destination_path.parent()?; - } + // let mut destination_path = destination_entry_path.as_ref(); + // if destination_is_file { + // destination_path = destination_path.parent()?; + // } - let mut new_path = destination_path.to_path_buf(); - new_path.push(entry_path.path.file_name()?); - if new_path != entry_path.path.as_ref() { - let task = project.rename_entry(entry_to_move, new_path, cx)?; - cx.foreground_executor().spawn(task).detach_and_log_err(cx); - } + // let mut new_path = destination_path.to_path_buf(); + // new_path.push(entry_path.path.file_name()?); + // if new_path != entry_path.path.as_ref() { + // let task = project.rename_entry(entry_to_move, new_path, cx)?; + // cx.foreground_executor().spawn(task).detach_and_log_err(cx); + // } - Some(project.worktree_id_for_entry(destination, cx)?) - }); + // Some(project.worktree_id_for_entry(destination, cx)?) + // }); - if let Some(destination_worktree) = destination_worktree { - self.expand_entry(destination_worktree, destination, cx); - } - } + // if let Some(destination_worktree) = destination_worktree { + // self.expand_entry(destination_worktree, destination, cx); + // } + // } fn index_for_selection(&self, selection: Selection) -> Option<(usize, usize, usize)> { let mut entry_index = 0; @@ -1366,23 +1345,32 @@ impl ProjectPanel { .git_status .as_ref() .map(|status| match status { - GitFileStatus::Added => theme.styles.status.created, - GitFileStatus::Modified => theme.styles.status.modified, - GitFileStatus::Conflict => theme.styles.status.conflict, + GitFileStatus::Added => theme.status().created, + GitFileStatus::Modified => theme.status().modified, + GitFileStatus::Conflict => theme.status().conflict, }) - .unwrap_or(theme.styles.status.info); + .unwrap_or(theme.status().info); h_stack() .child(if let Some(icon) = &details.icon { - div().child(svg().path(icon.to_string())) + div().child( + // todo!() Marshall: Can we use our `IconElement` component here? + svg() + .size(rems(0.9375)) + .flex_none() + .path(icon.to_string()) + .text_color(cx.theme().colors().icon), + ) } else { div() }) .child( if let (Some(editor), true) = (editor, show_editor) { - div().child(editor.clone()) + div().w_full().child(editor.clone()) } else { - div().child(details.filename.clone()) + div() + .text_color(filename_text_color) + .child(Label::new(details.filename.clone())) } .ml_1(), ) @@ -1390,11 +1378,10 @@ impl ProjectPanel { } fn render_entry( + &self, entry_id: ProjectEntryId, details: EntryDetails, - editor: &View, // dragged_entry_destination: &mut Option>, - // theme: &theme::ProjectPanel, cx: &mut ViewContext, ) -> Div> { let kind = details.kind; @@ -1402,9 +1389,18 @@ impl ProjectPanel { const INDENT_SIZE: Pixels = px(16.0); let padding = INDENT_SIZE + details.depth as f32 * px(settings.indent_size); let show_editor = details.is_editing && !details.is_processing; + let is_selected = self + .selection + .map_or(false, |selection| selection.entry_id == entry_id); - Self::render_entry_visual_element(&details, Some(editor), padding, cx) + Self::render_entry_visual_element(&details, Some(&self.filename_editor), padding, cx) .id(entry_id.to_proto() as usize) + .w_full() + .cursor_pointer() + .when(is_selected, |this| { + this.bg(cx.theme().colors().element_selected) + }) + .hover(|style| style.bg(cx.theme().colors().element_hover)) .on_click(move |this, event, cx| { if !show_editor { if kind.is_dir() { @@ -1418,38 +1414,51 @@ impl ProjectPanel { } } }) - // .on_down(MouseButton::Right, move |event, this, cx| { - // this.deploy_context_menu(event.position, entry_id, cx); - // }) - // .on_up(MouseButton::Left, move |_, this, cx| { - // if let Some((_, dragged_entry)) = cx - // .global::>() - // .currently_dragged::(cx.window()) - // { + .on_mouse_down(MouseButton::Right, move |this, event, cx| { + this.deploy_context_menu(event.position, entry_id, cx); + }) + // .on_drop::(|this, event, cx| { // this.move_entry( // *dragged_entry, // entry_id, // matches!(details.kind, EntryKind::File(_)), // cx, // ); - // } // }) } } impl Render for ProjectPanel { - type Element = Div, FocusEnabled>; - - fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { - enum ProjectPanel {} - let theme = cx.theme(); - let last_worktree_root_id = self.last_worktree_root_id; + type Element = Div, FocusableKeyDispatch>; + fn render(&mut self, _cx: &mut gpui::ViewContext) -> Self::Element { let has_worktree = self.visible_entries.len() != 0; if has_worktree { div() .id("project-panel") + .size_full() + .context("ProjectPanel") + .on_action(Self::select_next) + .on_action(Self::select_prev) + .on_action(Self::expand_selected_entry) + .on_action(Self::collapse_selected_entry) + .on_action(Self::collapse_all_entries) + .on_action(Self::new_file) + .on_action(Self::new_directory) + .on_action(Self::rename) + .on_action(Self::delete) + .on_action(Self::confirm) + .on_action(Self::open_file) + .on_action(Self::cancel) + .on_action(Self::cut) + .on_action(Self::copy) + .on_action(Self::copy_path) + .on_action(Self::copy_relative_path) + .on_action(Self::paste) + .on_action(Self::reveal_in_finder) + .on_action(Self::open_in_terminal) + .on_action(Self::new_search_in_directory) .track_focus(&self.focus_handle) .child( uniform_list( @@ -1461,17 +1470,12 @@ impl Render for ProjectPanel { |this: &mut Self, range, cx| { let mut items = SmallVec::new(); this.for_each_visible_entry(range, cx, |id, details, cx| { - items.push(Self::render_entry( - id, - details, - &this.filename_editor, - // &mut dragged_entry_destination, - cx, - )); + items.push(this.render_entry(id, details, cx)); }); items }, ) + .size_full() .track_scroll(self.list.clone()), ) } else { diff --git a/crates/project_panel2/src/project_panel_settings.rs b/crates/project_panel2/src/project_panel_settings.rs new file mode 100644 index 0000000000..5b0e0194a5 --- /dev/null +++ b/crates/project_panel2/src/project_panel_settings.rs @@ -0,0 +1,45 @@ +use anyhow; +use schemars::JsonSchema; +use serde_derive::{Deserialize, Serialize}; +use settings::Settings; + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ProjectPanelDockPosition { + Left, + Right, +} + +#[derive(Deserialize, Debug)] +pub struct ProjectPanelSettings { + pub default_width: f32, + pub dock: ProjectPanelDockPosition, + pub file_icons: bool, + pub folder_icons: bool, + pub git_status: bool, + pub indent_size: f32, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct ProjectPanelSettingsContent { + pub default_width: Option, + pub dock: Option, + pub file_icons: Option, + pub folder_icons: Option, + pub git_status: Option, + pub indent_size: Option, +} + +impl Settings for ProjectPanelSettings { + const KEY: Option<&'static str> = Some("project_panel"); + + type FileContent = ProjectPanelSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &mut gpui::AppContext, + ) -> anyhow::Result { + Self::load_via_json_merge(default_value, user_values) + } +} diff --git a/crates/semantic_index/Cargo.toml b/crates/semantic_index/Cargo.toml index 875440ef3f..0308927944 100644 --- a/crates/semantic_index/Cargo.toml +++ b/crates/semantic_index/Cargo.toml @@ -33,7 +33,7 @@ lazy_static.workspace = true serde.workspace = true serde_json.workspace = true async-trait.workspace = true -tiktoken-rs = "0.5.0" +tiktoken-rs.workspace = true parking_lot.workspace = true rand.workspace = true schemars.workspace = true diff --git a/crates/settings2/src/keymap_file.rs b/crates/settings2/src/keymap_file.rs index 9f279864ee..2b57af0fdb 100644 --- a/crates/settings2/src/keymap_file.rs +++ b/crates/settings2/src/keymap_file.rs @@ -9,7 +9,7 @@ use schemars::{ }; use serde::Deserialize; use serde_json::Value; -use util::{asset_str, ResultExt}; +use util::asset_str; #[derive(Debug, Deserialize, Default, Clone, JsonSchema)] #[serde(transparent)] @@ -86,7 +86,9 @@ impl KeymapFile { "invalid binding value for keystroke {keystroke}, context {context:?}" ) }) - .log_err() + // todo!() + .ok() + // .log_err() .map(|action| KeyBinding::load(&keystroke, action, context.as_deref())) }) .collect::>>()?; diff --git a/crates/ui2/src/components/button.rs b/crates/ui2/src/components/button.rs index 5787616832..1418a977f1 100644 --- a/crates/ui2/src/components/button.rs +++ b/crates/ui2/src/components/button.rs @@ -87,6 +87,7 @@ pub struct Button { label: SharedString, variant: ButtonVariant, width: Option, + color: Option, } impl Button { @@ -99,6 +100,7 @@ impl Button { label: label.into(), variant: Default::default(), width: Default::default(), + color: None, } } @@ -139,25 +141,24 @@ impl Button { self } - fn label_color(&self) -> LabelColor { + pub fn color(mut self, color: Option) -> Self { + self.color = color; + self + } + + pub fn label_color(&self, color: Option) -> LabelColor { if self.disabled { LabelColor::Disabled + } else if let Some(color) = color { + color } else { Default::default() } } - fn icon_color(&self) -> IconColor { - if self.disabled { - IconColor::Disabled - } else { - Default::default() - } - } - - fn render_label(&self) -> Label { + fn render_label(&self, color: LabelColor) -> Label { Label::new(self.label.clone()) - .color(self.label_color()) + .color(color) .line_height_style(LineHeightStyle::UILabel) } @@ -166,7 +167,11 @@ impl Button { } pub fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let icon_color = self.icon_color(); + let (icon_color, label_color) = match (self.disabled, self.color) { + (true, _) => (IconColor::Disabled, LabelColor::Disabled), + (_, None) => (IconColor::Default, LabelColor::Default), + (_, Some(color)) => (IconColor::from(color), color), + }; let mut button = h_stack() .id(SharedString::from(format!("{}", self.label))) @@ -182,16 +187,16 @@ impl Button { (Some(_), Some(IconPosition::Left)) => { button = button .gap_1() - .child(self.render_label()) + .child(self.render_label(label_color)) .children(self.render_icon(icon_color)) } (Some(_), Some(IconPosition::Right)) => { button = button .gap_1() .children(self.render_icon(icon_color)) - .child(self.render_label()) + .child(self.render_label(label_color)) } - (_, _) => button = button.child(self.render_label()), + (_, _) => button = button.child(self.render_label(label_color)), } if let Some(width) = self.width { diff --git a/crates/ui2/src/components/icon.rs b/crates/ui2/src/components/icon.rs index 907f3f9187..75c8129608 100644 --- a/crates/ui2/src/components/icon.rs +++ b/crates/ui2/src/components/icon.rs @@ -1,7 +1,7 @@ use gpui::{rems, svg, Hsla}; use strum::EnumIter; -use crate::prelude::*; +use crate::{prelude::*, LabelColor}; #[derive(Default, PartialEq, Copy, Clone)] pub enum IconSize { @@ -14,15 +14,20 @@ pub enum IconSize { pub enum IconColor { #[default] Default, - Muted, - Disabled, - Placeholder, Accent, + Created, + Deleted, + Disabled, Error, - Warning, - Success, + Hidden, Info, + Modified, + Muted, + Placeholder, + Player(u32), Selected, + Success, + Warning, } impl IconColor { @@ -38,6 +43,33 @@ impl IconColor { IconColor::Success => cx.theme().status().success, IconColor::Info => cx.theme().status().info, IconColor::Selected => cx.theme().colors().icon_accent, + IconColor::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor, + IconColor::Created => cx.theme().status().created, + IconColor::Modified => cx.theme().status().modified, + IconColor::Deleted => cx.theme().status().deleted, + IconColor::Hidden => cx.theme().status().hidden, + } + } +} + +impl From for IconColor { + fn from(label: LabelColor) -> Self { + match label { + LabelColor::Default => IconColor::Default, + LabelColor::Muted => IconColor::Muted, + LabelColor::Disabled => IconColor::Disabled, + LabelColor::Placeholder => IconColor::Placeholder, + LabelColor::Accent => IconColor::Accent, + LabelColor::Error => IconColor::Error, + LabelColor::Warning => IconColor::Warning, + LabelColor::Success => IconColor::Success, + LabelColor::Info => IconColor::Info, + LabelColor::Selected => IconColor::Selected, + LabelColor::Player(i) => IconColor::Player(i), + LabelColor::Created => IconColor::Created, + LabelColor::Modified => IconColor::Modified, + LabelColor::Deleted => IconColor::Deleted, + LabelColor::Hidden => IconColor::Hidden, } } } diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index 91653ea8cd..b20cd31036 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -1,10 +1,7 @@ +use crate::{h_stack, prelude::*, ClickHandler, Icon, IconColor, IconElement, TextTooltip}; +use gpui::{MouseButton, VisualContext}; use std::sync::Arc; -use gpui::{rems, MouseButton}; - -use crate::{h_stack, prelude::*}; -use crate::{ClickHandler, Icon, IconColor, IconElement}; - struct IconButtonHandlers { click: Option>, } @@ -22,6 +19,7 @@ pub struct IconButton { color: IconColor, variant: ButtonVariant, state: InteractionState, + tooltip: Option, handlers: IconButtonHandlers, } @@ -33,6 +31,7 @@ impl IconButton { color: IconColor::default(), variant: ButtonVariant::default(), state: InteractionState::default(), + tooltip: None, handlers: IconButtonHandlers::default(), } } @@ -57,6 +56,11 @@ impl IconButton { self } + pub fn tooltip(mut self, tooltip: impl Into) -> Self { + self.tooltip = Some(tooltip.into()); + self + } + pub fn on_click( mut self, handler: impl 'static + Fn(&mut V, &mut ViewContext) + Send + Sync, @@ -88,9 +92,7 @@ impl IconButton { .id(self.id.clone()) .justify_center() .rounded_md() - // todo!("Where do these numbers come from?") - .py(rems(0.21875)) - .px(rems(0.375)) + .p_1() .bg(bg_color) .hover(|style| style.bg(bg_hover_color)) .active(|style| style.bg(bg_active_color)) @@ -103,6 +105,11 @@ impl IconButton { }); } + if let Some(tooltip) = self.tooltip.clone() { + button = + button.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(tooltip.clone()))); + } + button } } diff --git a/crates/ui2/src/components/label.rs b/crates/ui2/src/components/label.rs index 6b915af1b9..4b9cea8dc2 100644 --- a/crates/ui2/src/components/label.rs +++ b/crates/ui2/src/components/label.rs @@ -7,28 +7,40 @@ use crate::styled_ext::StyledExt; pub enum LabelColor { #[default] Default, - Muted, + Accent, Created, - Modified, Deleted, Disabled, + Error, Hidden, + Info, + Modified, + Muted, Placeholder, - Accent, + Player(u32), + Selected, + Success, + Warning, } impl LabelColor { pub fn hsla(&self, cx: &WindowContext) -> Hsla { match self { - Self::Default => cx.theme().colors().text, - Self::Muted => cx.theme().colors().text_muted, - Self::Created => cx.theme().status().created, - Self::Modified => cx.theme().status().modified, - Self::Deleted => cx.theme().status().deleted, - Self::Disabled => cx.theme().colors().text_disabled, - Self::Hidden => cx.theme().status().hidden, - Self::Placeholder => cx.theme().colors().text_placeholder, - Self::Accent => cx.theme().colors().text_accent, + LabelColor::Default => cx.theme().colors().text, + LabelColor::Muted => cx.theme().colors().text_muted, + LabelColor::Created => cx.theme().status().created, + LabelColor::Modified => cx.theme().status().modified, + LabelColor::Deleted => cx.theme().status().deleted, + LabelColor::Disabled => cx.theme().colors().text_disabled, + LabelColor::Hidden => cx.theme().status().hidden, + LabelColor::Info => cx.theme().status().info, + LabelColor::Placeholder => cx.theme().colors().text_placeholder, + LabelColor::Accent => cx.theme().colors().text_accent, + LabelColor::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor, + LabelColor::Error => cx.theme().status().error, + LabelColor::Selected => cx.theme().colors().text_accent, + LabelColor::Success => cx.theme().status().success, + LabelColor::Warning => cx.theme().status().warning, } } } diff --git a/crates/ui2/src/components/tooltip.rs b/crates/ui2/src/components/tooltip.rs index e6c0e3f44d..ee3e9708c0 100644 --- a/crates/ui2/src/components/tooltip.rs +++ b/crates/ui2/src/components/tooltip.rs @@ -9,8 +9,10 @@ pub struct TextTooltip { } impl TextTooltip { - pub fn new(str: SharedString) -> Self { - Self { title: str } + pub fn new(title: impl Into) -> Self { + Self { + title: title.into(), + } } } diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index f21eb84ae2..9a614bc92e 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -1,7 +1,7 @@ use crate::{status_bar::StatusItemView, Axis, Workspace}; use gpui::{ - div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, ParentElement, Render, - Subscription, View, ViewContext, WeakView, WindowContext, + div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, FocusHandle, + ParentElement, Render, Styled, Subscription, View, ViewContext, WeakView, WindowContext, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -34,6 +34,7 @@ pub trait Panel: Render + EventEmitter { fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext) {} fn set_active(&mut self, _active: bool, _cx: &mut ViewContext) {} fn has_focus(&self, cx: &WindowContext) -> bool; + fn focus_handle(&self, cx: &WindowContext) -> FocusHandle; } pub trait PanelHandle: Send + Sync { @@ -51,6 +52,7 @@ pub trait PanelHandle: Send + Sync { fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option>); fn icon_label(&self, cx: &WindowContext) -> Option; fn has_focus(&self, cx: &WindowContext) -> bool; + fn focus_handle(&self, cx: &WindowContext) -> FocusHandle; fn to_any(&self) -> AnyView; } @@ -117,6 +119,10 @@ where fn to_any(&self) -> AnyView { self.clone().into() } + + fn focus_handle(&self, cx: &WindowContext) -> FocusHandle { + self.read(cx).focus_handle(cx).clone() + } } impl From<&dyn PanelHandle> for AnyView { @@ -422,7 +428,11 @@ impl Render for Dock { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - todo!() + if let Some(entry) = self.visible_entry() { + div().size_full().child(entry.panel.to_any()) + } else { + div() + } } } @@ -728,5 +738,9 @@ pub mod test { fn has_focus(&self, _cx: &WindowContext) -> bool { self.has_focus } + + fn focus_handle(&self, cx: &WindowContext) -> FocusHandle { + unimplemented!() + } } } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index d0613e13ab..e3ea4863c9 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -733,21 +733,21 @@ impl Pane { // self.activate_item(index, activate_pane, activate_pane, cx); // } - // pub fn close_active_item( - // &mut self, - // action: &CloseActiveItem, - // cx: &mut ViewContext, - // ) -> Option>> { - // if self.items.is_empty() { - // return None; - // } - // let active_item_id = self.items[self.active_item_index].id(); - // Some(self.close_item_by_id( - // active_item_id, - // action.save_intent.unwrap_or(SaveIntent::Close), - // cx, - // )) - // } + pub fn close_active_item( + &mut self, + action: &CloseActiveItem, + cx: &mut ViewContext, + ) -> Option>> { + if self.items.is_empty() { + return None; + } + let active_item_id = self.items[self.active_item_index].id(); + Some(self.close_item_by_id( + active_item_id, + action.save_intent.unwrap_or(SaveIntent::Close), + cx, + )) + } pub fn close_item_by_id( &mut self, @@ -1919,7 +1919,12 @@ impl Render for Pane { fn render(&mut self, cx: &mut ViewContext) -> Self::Element { v_stack() + .context("Pane") .size_full() + .on_action(|pane: &mut Self, action, cx| { + pane.close_active_item(action, cx) + .map(|task| task.detach_and_log_err(cx)); + }) .child(self.render_tab_bar(cx)) .child(div() /* todo!(toolbar) */) .child(if let Some(item) = self.active_item() { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 1a5f3329b8..2a8980895c 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -29,7 +29,7 @@ use client2::{ Client, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; -use dock::{Dock, DockPosition, PanelButtons}; +use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle as _}; use futures::{ channel::{mpsc, oneshot}, future::try_join_all, @@ -69,7 +69,7 @@ use std::{ }; use theme2::ActiveTheme; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use ui::{h_stack, Label}; +use ui::{h_stack, Button, ButtonVariant, Label, LabelColor}; use util::ResultExt; use uuid::Uuid; pub use workspace_settings::{AutosaveSetting, WorkspaceSettings}; @@ -248,102 +248,6 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { // } // } // }); - // cx.add_async_action(Workspace::open); - - // cx.add_async_action(Workspace::follow_next_collaborator); - // cx.add_async_action(Workspace::close); - // cx.add_async_action(Workspace::close_inactive_items_and_panes); - // cx.add_async_action(Workspace::close_all_items_and_panes); - // cx.add_global_action(Workspace::close_global); - // cx.add_global_action(restart); - // cx.add_async_action(Workspace::save_all); - // cx.add_action(Workspace::add_folder_to_project); - // cx.add_action( - // |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext| { - // let pane = workspace.active_pane().clone(); - // workspace.unfollow(&pane, cx); - // }, - // ); - // cx.add_action( - // |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext| { - // workspace - // .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx) - // .detach_and_log_err(cx); - // }, - // ); - // cx.add_action( - // |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext| { - // workspace - // .save_active_item(SaveIntent::SaveAs, cx) - // .detach_and_log_err(cx); - // }, - // ); - // cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { - // workspace.activate_previous_pane(cx) - // }); - // cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| { - // workspace.activate_next_pane(cx) - // }); - - // cx.add_action( - // |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| { - // workspace.activate_pane_in_direction(action.0, cx) - // }, - // ); - - // cx.add_action( - // |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| { - // workspace.swap_pane_in_direction(action.0, cx) - // }, - // ); - - // cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| { - // workspace.toggle_dock(DockPosition::Left, cx); - // }); - // cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| { - // workspace.toggle_dock(DockPosition::Right, cx); - // }); - // cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| { - // workspace.toggle_dock(DockPosition::Bottom, cx); - // }); - // cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| { - // workspace.close_all_docks(cx); - // }); - // cx.add_action(Workspace::activate_pane_at_index); - // cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| { - // workspace.reopen_closed_item(cx).detach(); - // }); - // cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| { - // workspace - // .go_back(workspace.active_pane().downgrade(), cx) - // .detach(); - // }); - // cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| { - // workspace - // .go_forward(workspace.active_pane().downgrade(), cx) - // .detach(); - // }); - - // cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| { - // cx.spawn(|workspace, mut cx| async move { - // let err = install_cli::install_cli(&cx) - // .await - // .context("Failed to create CLI symlink"); - - // workspace.update(&mut cx, |workspace, cx| { - // if matches!(err, Err(_)) { - // err.notify_err(workspace, cx); - // } else { - // workspace.show_notification(1, cx, |cx| { - // cx.build_view(|_| { - // MessageNotification::new("Successfully installed the `zed` binary") - // }) - // }); - // } - // }) - // }) - // .detach(); - // }); } type ProjectItemBuilders = @@ -942,108 +846,15 @@ impl Workspace { &self.right_dock } - // pub fn add_panel(&mut self, panel: View, cx: &mut ViewContext) - // where - // T::Event: std::fmt::Debug, - // { - // self.add_panel_with_extra_event_handler(panel, cx, |_, _, _, _| {}) - // } + pub fn add_panel(&mut self, panel: View, cx: &mut ViewContext) { + let dock = match panel.position(cx) { + DockPosition::Left => &self.left_dock, + DockPosition::Bottom => &self.bottom_dock, + DockPosition::Right => &self.right_dock, + }; - // pub fn add_panel_with_extra_event_handler( - // &mut self, - // panel: View, - // cx: &mut ViewContext, - // handler: F, - // ) where - // T::Event: std::fmt::Debug, - // F: Fn(&mut Self, &View, &T::Event, &mut ViewContext) + 'static, - // { - // let dock = match panel.position(cx) { - // DockPosition::Left => &self.left_dock, - // DockPosition::Bottom => &self.bottom_dock, - // DockPosition::Right => &self.right_dock, - // }; - - // self.subscriptions.push(cx.subscribe(&panel, { - // let mut dock = dock.clone(); - // let mut prev_position = panel.position(cx); - // move |this, panel, event, cx| { - // if T::should_change_position_on_event(event) { - // THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION - // See: Dock::add_panel - // - // let new_position = panel.read(cx).position(cx); - // let mut was_visible = false; - // dock.update(cx, |dock, cx| { - // prev_position = new_position; - - // was_visible = dock.is_open() - // && dock - // .visible_panel() - // .map_or(false, |active_panel| active_panel.id() == panel.id()); - // dock.remove_panel(&panel, cx); - // }); - - // if panel.is_zoomed(cx) { - // this.zoomed_position = Some(new_position); - // } - - // dock = match panel.read(cx).position(cx) { - // DockPosition::Left => &this.left_dock, - // DockPosition::Bottom => &this.bottom_dock, - // DockPosition::Right => &this.right_dock, - // } - // .clone(); - // dock.update(cx, |dock, cx| { - // dock.add_panel(panel.clone(), cx); - // if was_visible { - // dock.set_open(true, cx); - // dock.activate_panel(dock.panels_len() - 1, cx); - // } - // }); - // } else if T::should_zoom_in_on_event(event) { - // THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION - // See: Dock::add_panel - // - // dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx)); - // if !panel.has_focus(cx) { - // cx.focus(&panel); - // } - // this.zoomed = Some(panel.downgrade().into_any()); - // this.zoomed_position = Some(panel.read(cx).position(cx)); - // } else if T::should_zoom_out_on_event(event) { - // THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION - // See: Dock::add_panel - // - // dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx)); - // if this.zoomed_position == Some(prev_position) { - // this.zoomed = None; - // this.zoomed_position = None; - // } - // cx.notify(); - // } else if T::is_focus_event(event) { - // THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION - // See: Dock::add_panel - // - // let position = panel.read(cx).position(cx); - // this.dismiss_zoomed_items_to_reveal(Some(position), cx); - // if panel.is_zoomed(cx) { - // this.zoomed = Some(panel.downgrade().into_any()); - // this.zoomed_position = Some(position); - // } else { - // this.zoomed = None; - // this.zoomed_position = None; - // } - // this.update_active_view_for_followers(cx); - // cx.notify(); - // } else { - // handler(this, &panel, event, cx) - // } - // } - // })); - - // dock.update(cx, |dock, cx| dock.add_panel(panel, cx)); - // } + dock.update(cx, |dock, cx| dock.add_panel(panel, cx)); + } pub fn status_bar(&self) -> &View { &self.status_bar @@ -1735,42 +1546,43 @@ impl Workspace { // } // } - // pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext) { - // let dock = match dock_side { - // DockPosition::Left => &self.left_dock, - // DockPosition::Bottom => &self.bottom_dock, - // DockPosition::Right => &self.right_dock, - // }; - // let mut focus_center = false; - // let mut reveal_dock = false; - // dock.update(cx, |dock, cx| { - // let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side); - // let was_visible = dock.is_open() && !other_is_zoomed; - // dock.set_open(!was_visible, cx); + pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext) { + let dock = match dock_side { + DockPosition::Left => &self.left_dock, + DockPosition::Bottom => &self.bottom_dock, + DockPosition::Right => &self.right_dock, + }; + let mut focus_center = false; + let mut reveal_dock = false; + dock.update(cx, |dock, cx| { + let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side); + let was_visible = dock.is_open() && !other_is_zoomed; + dock.set_open(!was_visible, cx); - // if let Some(active_panel) = dock.active_panel() { - // if was_visible { - // if active_panel.has_focus(cx) { - // focus_center = true; - // } - // } else { - // cx.focus(active_panel.as_any()); - // reveal_dock = true; - // } - // } - // }); + if let Some(active_panel) = dock.active_panel() { + if was_visible { + if active_panel.has_focus(cx) { + focus_center = true; + } + } else { + let focus_handle = &active_panel.focus_handle(cx); + cx.focus(focus_handle); + reveal_dock = true; + } + } + }); - // if reveal_dock { - // self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx); - // } + if reveal_dock { + self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx); + } - // if focus_center { - // cx.focus_self(); - // } + if focus_center { + cx.focus(&self.focus_handle); + } - // cx.notify(); - // self.serialize_workspace(cx); - // } + cx.notify(); + self.serialize_workspace(cx); + } pub fn close_all_docks(&mut self, cx: &mut ViewContext) { let docks = [&self.left_dock, &self.bottom_dock, &self.right_dock]; @@ -2644,19 +2456,35 @@ impl Workspace { h_stack() .id("titlebar") .justify_between() - .w_full() - .h(rems(1.75)) - .bg(cx.theme().colors().title_bar_background) .when( !matches!(cx.window_bounds(), WindowBounds::Fullscreen), |s| s.pl_20(), ) + .w_full() + .h(rems(1.75)) + .bg(cx.theme().colors().title_bar_background) .on_click(|_, event, cx| { if event.up.click_count == 2 { cx.zoom_window(); } }) - .child(h_stack().child(Label::new("Left side titlebar item"))) // self.titlebar_item + .child( + h_stack() + // TODO - Add player menu + .child( + Button::new("player") + .variant(ButtonVariant::Ghost) + .color(Some(LabelColor::Player(0))), + ) + // TODO - Add project menu + .child(Button::new("project_name").variant(ButtonVariant::Ghost)) + // TODO - Add git menu + .child( + Button::new("branch_name") + .variant(ButtonVariant::Ghost) + .color(Some(LabelColor::Muted)), + ), + ) // self.titlebar_item .child(h_stack().child(Label::new("Right side titlebar item"))) } @@ -3451,6 +3279,107 @@ impl Workspace { }) } + fn actions(div: Div) -> Div { + div + // cx.add_async_action(Workspace::open); + // cx.add_async_action(Workspace::follow_next_collaborator); + // cx.add_async_action(Workspace::close); + // cx.add_async_action(Workspace::close_inactive_items_and_panes); + // cx.add_async_action(Workspace::close_all_items_and_panes); + // cx.add_global_action(Workspace::close_global); + // cx.add_global_action(restart); + // cx.add_async_action(Workspace::save_all); + // cx.add_action(Workspace::add_folder_to_project); + // cx.add_action( + // |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext| { + // let pane = workspace.active_pane().clone(); + // workspace.unfollow(&pane, cx); + // }, + // ); + // cx.add_action( + // |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext| { + // workspace + // .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx) + // .detach_and_log_err(cx); + // }, + // ); + // cx.add_action( + // |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext| { + // workspace + // .save_active_item(SaveIntent::SaveAs, cx) + // .detach_and_log_err(cx); + // }, + // ); + // cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { + // workspace.activate_previous_pane(cx) + // }); + // cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| { + // workspace.activate_next_pane(cx) + // }); + // cx.add_action( + // |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| { + // workspace.activate_pane_in_direction(action.0, cx) + // }, + // ); + // cx.add_action( + // |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| { + // workspace.swap_pane_in_direction(action.0, cx) + // }, + // ); + .on_action(|this, e: &ToggleLeftDock, cx| { + println!("TOGGLING DOCK"); + this.toggle_dock(DockPosition::Left, cx); + }) + // cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| { + // workspace.toggle_dock(DockPosition::Right, cx); + // }); + // cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| { + // workspace.toggle_dock(DockPosition::Bottom, cx); + // }); + // cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| { + // workspace.close_all_docks(cx); + // }); + // cx.add_action(Workspace::activate_pane_at_index); + // cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| { + // workspace.reopen_closed_item(cx).detach(); + // }); + // cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| { + // workspace + // .go_back(workspace.active_pane().downgrade(), cx) + // .detach(); + // }); + // cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| { + // workspace + // .go_forward(workspace.active_pane().downgrade(), cx) + // .detach(); + // }); + + // cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| { + // cx.spawn(|workspace, mut cx| async move { + // let err = install_cli::install_cli(&cx) + // .await + // .context("Failed to create CLI symlink"); + + // workspace.update(&mut cx, |workspace, cx| { + // if matches!(err, Err(_)) { + // err.notify_err(workspace, cx); + // } else { + // workspace.show_notification(1, cx, |cx| { + // cx.build_view(|_| { + // MessageNotification::new("Successfully installed the `zed` binary") + // }) + // }); + // } + // }) + // }) + // .detach(); + // }); + } + + // todo!() + // #[cfg(any(test, feature = "test-support"))] + // pub fn test_new(project: ModelHandle, cx: &mut ViewContext) -> Self { + // use node_runtime::FakeNodeRuntime; #[cfg(any(test, feature = "test-support"))] pub fn test_new(project: Model, cx: &mut ViewContext) -> Self { use gpui::Context; @@ -3776,83 +3705,31 @@ impl Render for Workspace { .border_b() .border_color(cx.theme().colors().border) .child(self.modal_layer.clone()) - // .children( - // Some( - // Panel::new("project-panel-outer", cx) - // .side(PanelSide::Left) - // .child(ProjectPanel::new("project-panel-inner")), - // ) - // .filter(|_| self.is_project_panel_open()), - // ) - // .children( - // Some( - // Panel::new("collab-panel-outer", cx) - // .child(CollabPanel::new("collab-panel-inner")) - // .side(PanelSide::Left), - // ) - // .filter(|_| self.is_collab_panel_open()), - // ) - // .child(NotificationToast::new( - // "maxbrunsfeld has requested to add you as a contact.".into(), - // )) .child( - div().flex().flex_col().flex_1().h_full().child( - div().flex().flex_1().child(self.center.render( - &self.project, - &self.follower_states, - self.active_call(), - &self.active_pane, - self.zoomed.as_ref(), - &self.app_state, - cx, - )), - ), // .children( - // Some( - // Panel::new("terminal-panel", cx) - // .child(Terminal::new()) - // .allowed_sides(PanelAllowedSides::BottomOnly) - // .side(PanelSide::Bottom), - // ) - // .filter(|_| self.is_terminal_open()), - // ), - ), // .children( - // Some( - // Panel::new("chat-panel-outer", cx) - // .side(PanelSide::Right) - // .child(ChatPanel::new("chat-panel-inner").messages(vec![ - // ChatMessage::new( - // "osiewicz".to_string(), - // "is this thing on?".to_string(), - // DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") - // .unwrap() - // .naive_local(), - // ), - // ChatMessage::new( - // "maxdeviant".to_string(), - // "Reading you loud and clear!".to_string(), - // DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") - // .unwrap() - // .naive_local(), - // ), - // ])), - // ) - // .filter(|_| self.is_chat_panel_open()), - // ) - // .children( - // Some( - // Panel::new("notifications-panel-outer", cx) - // .side(PanelSide::Right) - // .child(NotificationsPanel::new("notifications-panel-inner")), - // ) - // .filter(|_| self.is_notifications_panel_open()), - // ) - // .children( - // Some( - // Panel::new("assistant-panel-outer", cx) - // .child(AssistantPanel::new("assistant-panel-inner")), - // ) - // .filter(|_| self.is_assistant_panel_open()), - // ), + div() + .flex() + .flex_row() + .flex_1() + .h_full() + .child(div().flex().flex_1().child(self.left_dock.clone())) + .child( + div() + .flex() + .flex_col() + .flex_1() + .child(self.center.render( + &self.project, + &self.follower_states, + self.active_call(), + &self.active_pane, + self.zoomed.as_ref(), + &self.app_state, + cx, + )) + .child(div().flex().flex_1().child(self.bottom_dock.clone())), + ) + .child(div().flex().flex_1().child(self.right_dock.clone())), + ), ) .child(self.status_bar.clone()) // .when(self.debug.show_toast, |this| { diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 610bd90f69..a21d113cad 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -55,7 +55,7 @@ node_runtime = { path = "../node_runtime" } # outline = { path = "../outline" } # plugin_runtime = { path = "../plugin_runtime",optional = true } project = { package = "project2", path = "../project2" } -# project_panel = { path = "../project_panel" } +project_panel = { package = "project_panel2", path = "../project_panel2" } # project_symbols = { path = "../project_symbols" } # quick_action_bar = { path = "../quick_action_bar" } # recent_projects = { path = "../recent_projects" } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index b3797bed2a..20fc18e6ed 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -193,7 +193,7 @@ fn main() { // file_finder::init(cx); // outline::init(cx); // project_symbols::init(cx); - // project_panel::init(Assets, cx); + project_panel::init(Assets, cx); // channel::init(&client, user_store.clone(), cx); // diagnostics::init(cx); // search::init(cx); diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index 37c317fb61..8f4a9c6ddf 100644 --- a/crates/zed2/src/zed2.rs +++ b/crates/zed2/src/zed2.rs @@ -17,6 +17,7 @@ pub use only_instance::*; pub use open_listener::*; use anyhow::{anyhow, Context as _, Result}; +use project_panel::ProjectPanel; use settings::{initial_local_settings_content, Settings}; use std::{borrow::Cow, ops::Deref, sync::Arc}; use util::{ @@ -27,8 +28,9 @@ use util::{ }; use uuid::Uuid; use workspace::{ - create_and_open_local_file, notifications::simple_message_notification::MessageNotification, - open_new, AppState, NewFile, NewWindow, Workspace, WorkspaceSettings, + create_and_open_local_file, dock::PanelHandle, + notifications::simple_message_notification::MessageNotification, open_new, AppState, NewFile, + NewWindow, Workspace, WorkspaceSettings, }; use zed_actions::{OpenBrowser, OpenZedURL}; @@ -380,49 +382,38 @@ pub fn initialize_workspace( // } // false // }); - // })?; + })?; - // let project_panel = ProjectPanel::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()); - // let chat_panel = - // collab_ui::chat_panel::ChatPanel::load(workspace_handle.clone(), cx.clone()); - // let notification_panel = collab_ui::notification_panel::NotificationPanel::load( - // workspace_handle.clone(), - // cx.clone(), - // ); - // let ( - // project_panel, + let project_panel = ProjectPanel::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()); + // let chat_panel = + // collab_ui::chat_panel::ChatPanel::load(workspace_handle.clone(), cx.clone()); + // let notification_panel = collab_ui::notification_panel::NotificationPanel::load( + // workspace_handle.clone(), + // cx.clone(), + // ); + let ( + project_panel, // terminal_panel, // assistant_panel, // channels_panel, // chat_panel, // notification_panel, - // ) = futures::try_join!( - // project_panel, + ) = futures::try_join!( + project_panel, // terminal_panel, // assistant_panel, // channels_panel, // chat_panel, // notification_panel, - // )?; - // workspace_handle.update(&mut cx, |workspace, cx| { - // let project_panel_position = project_panel.position(cx); - // workspace.add_panel_with_extra_event_handler( - // project_panel, - // cx, - // |workspace, _, event, cx| match event { - // project_panel::Event::NewSearchInDirectory { dir_entry } => { - // search::ProjectSearchView::new_search_in_directory(workspace, dir_entry, cx) - // } - // project_panel::Event::ActivatePanel => { - // workspace.focus_panel::(cx); - // } - // _ => {} - // }, - // ); + )?; + + 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(assistant_panel, cx); // workspace.add_panel(channels_panel, cx); @@ -440,9 +431,9 @@ pub fn initialize_workspace( // .map_or(false, |entry| entry.is_dir()) // }) // { - // workspace.toggle_dock(project_panel_position, cx); + workspace.toggle_dock(project_panel_position, cx); // } - // cx.focus_self(); + // cx.focus_self(); })?; Ok(()) })