diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 844d5968c2bf47174cf67846d92ca5183777caa5..57118bcf22793ed78abfdb64aac4b962a294e9e7 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -2,7 +2,7 @@ use crate::{ editor::{buffer_view, BufferView}, settings::Settings, util, watch, - workspace::WorkspaceView, + workspace::Workspace, worktree::{match_paths, PathMatch, Worktree}, }; use gpui::{ @@ -26,7 +26,7 @@ use std::{ pub struct FileFinder { handle: WeakViewHandle, settings: watch::Receiver, - workspace: WeakViewHandle, + workspace: WeakViewHandle, query_buffer: ViewHandle, search_count: usize, latest_search_id: usize, @@ -253,7 +253,7 @@ impl FileFinder { }) } - fn toggle(workspace_view: &mut WorkspaceView, _: &(), ctx: &mut ViewContext) { + fn toggle(workspace_view: &mut Workspace, _: &(), ctx: &mut ViewContext) { workspace_view.toggle_modal(ctx, |ctx, workspace_view| { let workspace = ctx.handle(); let finder = @@ -264,10 +264,10 @@ impl FileFinder { } fn on_event( - workspace_view: &mut WorkspaceView, + workspace_view: &mut Workspace, _: ViewHandle, event: &Event, - ctx: &mut ViewContext, + ctx: &mut ViewContext, ) { match event { Event::Selected(tree_id, path) => { @@ -284,7 +284,7 @@ impl FileFinder { pub fn new( settings: watch::Receiver, - workspace: ViewHandle, + workspace: ViewHandle, ctx: &mut ViewContext, ) -> Self { ctx.observe_view(&workspace, Self::workspace_updated); @@ -310,7 +310,7 @@ impl FileFinder { } } - fn workspace_updated(&mut self, _: ViewHandle, ctx: &mut ViewContext) { + fn workspace_updated(&mut self, _: ViewHandle, ctx: &mut ViewContext) { self.spawn_search(self.query_buffer.read(ctx).text(ctx.as_ref()), ctx); } @@ -453,7 +453,7 @@ impl FileFinder { #[cfg(test)] mod tests { use super::*; - use crate::{editor, settings, test::temp_tree, workspace::WorkspaceView}; + use crate::{editor, settings, test::temp_tree, workspace::Workspace}; use gpui::App; use serde_json::json; use std::fs; @@ -473,7 +473,7 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let (window_id, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings, ctx); + let mut workspace = Workspace::new(0, settings, ctx); workspace.open_path(tmp_dir.path().into(), ctx); workspace }); @@ -542,7 +542,7 @@ mod tests { })); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings.clone(), ctx); + let mut workspace = Workspace::new(0, settings.clone(), ctx); workspace.open_path(tmp_dir.path().into(), ctx); workspace }); @@ -599,7 +599,7 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings.clone(), ctx); + let mut workspace = Workspace::new(0, settings.clone(), ctx); workspace.open_path(file_path, ctx); workspace }); @@ -641,7 +641,7 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings.clone(), ctx); + let mut workspace = Workspace::new(0, settings.clone(), ctx); smol::block_on(workspace.open_paths( &[tmp_dir.path().join("dir1"), tmp_dir.path().join("dir2")], ctx, diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace.rs similarity index 84% rename from zed/src/workspace/workspace_view.rs rename to zed/src/workspace.rs index 2b89ba4d60c7b085e3786b7b54243aa7326e0b87..00cdeef7a5ea09467a53fac30809a89cde12d119 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace.rs @@ -1,32 +1,99 @@ -use super::{pane, Pane, PaneGroup, SplitDirection}; +pub mod pane; +pub mod pane_group; +pub use pane::*; +pub use pane_group::*; + use crate::{ - editor::{Buffer, BufferView}, settings::Settings, + watch::{self, Receiver}, +}; +use gpui::{MutableAppContext, PathPromptOptions}; +use std::path::PathBuf; +pub fn init(app: &mut MutableAppContext) { + app.add_global_action("workspace:open", open); + app.add_global_action("workspace:open_paths", open_paths); + app.add_global_action("app:quit", quit); + app.add_action("workspace:save", Workspace::save_active_item); + app.add_action("workspace:debug_elements", Workspace::debug_elements); + app.add_bindings(vec![ + Binding::new("cmd-s", "workspace:save", None), + Binding::new("cmd-alt-i", "workspace:debug_elements", None), + ]); + pane::init(app); +} +use crate::{ + editor::{Buffer, BufferView}, time::ReplicaId, - watch, worktree::{Worktree, WorktreeHandle}, }; use futures_core::{future::LocalBoxFuture, Future}; use gpui::{ color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, - ClipboardItem, Entity, EntityTask, ModelHandle, MutableAppContext, View, ViewContext, - ViewHandle, + ClipboardItem, Entity, EntityTask, ModelHandle, View, ViewContext, ViewHandle, }; use log::error; use smol::prelude::*; use std::{ collections::{hash_map::Entry, HashMap, HashSet}, - path::{Path, PathBuf}, + path::Path, sync::Arc, }; -pub fn init(app: &mut MutableAppContext) { - app.add_action("workspace:save", WorkspaceView::save_active_item); - app.add_action("workspace:debug_elements", WorkspaceView::debug_elements); - app.add_bindings(vec![ - Binding::new("cmd-s", "workspace:save", None), - Binding::new("cmd-alt-i", "workspace:debug_elements", None), - ]); +pub struct OpenParams { + pub paths: Vec, + pub settings: watch::Receiver, +} + +fn open(settings: &Receiver, ctx: &mut MutableAppContext) { + let settings = settings.clone(); + ctx.prompt_for_paths( + PathPromptOptions { + files: true, + directories: true, + multiple: true, + }, + move |paths, ctx| { + if let Some(paths) = paths { + ctx.dispatch_global_action("workspace:open_paths", OpenParams { paths, settings }); + } + }, + ); +} + +fn open_paths(params: &OpenParams, app: &mut MutableAppContext) { + log::info!("open paths {:?}", params.paths); + + // Open paths in existing workspace if possible + for window_id in app.window_ids().collect::>() { + if let Some(handle) = app.root_view::(window_id) { + if handle.update(app, |view, ctx| { + if view.contains_paths(¶ms.paths, ctx.as_ref()) { + let open_paths = view.open_paths(¶ms.paths, ctx); + ctx.foreground().spawn(open_paths).detach(); + log::info!("open paths on existing workspace"); + true + } else { + false + } + }) { + return; + } + } + } + + log::info!("open new workspace"); + + // Add a new workspace if necessary + app.add_window(|ctx| { + let mut view = Workspace::new(0, params.settings.clone(), ctx); + let open_paths = view.open_paths(¶ms.paths, ctx); + ctx.foreground().spawn(open_paths).detach(); + view + }); +} + +fn quit(_: &(), app: &mut MutableAppContext) { + app.platform().quit(); } pub trait ItemView: View { @@ -129,7 +196,7 @@ pub struct State { pub center: PaneGroup, } -pub struct WorkspaceView { +pub struct Workspace { pub settings: watch::Receiver, modal: Option, center: PaneGroup, @@ -144,7 +211,7 @@ pub struct WorkspaceView { >, } -impl WorkspaceView { +impl Workspace { pub fn new( replica_id: ReplicaId, settings: watch::Receiver, @@ -157,7 +224,7 @@ impl WorkspaceView { }); ctx.focus(&pane); - WorkspaceView { + Workspace { modal: None, center: PaneGroup::new(pane.id()), panes: vec![pane.clone()], @@ -487,11 +554,11 @@ impl WorkspaceView { } } -impl Entity for WorkspaceView { +impl Entity for Workspace { type Event = (); } -impl View for WorkspaceView { +impl View for Workspace { fn ui_name() -> &'static str { "Workspace" } @@ -514,12 +581,12 @@ impl View for WorkspaceView { } #[cfg(test)] -pub trait WorkspaceViewHandle { +pub trait WorkspaceHandle { fn file_entries(&self, app: &AppContext) -> Vec<(usize, Arc)>; } #[cfg(test)] -impl WorkspaceViewHandle for ViewHandle { +impl WorkspaceHandle for ViewHandle { fn file_entries(&self, app: &AppContext) -> Vec<(usize, Arc)> { self.read(app) .worktrees() @@ -536,12 +603,73 @@ impl WorkspaceViewHandle for ViewHandle { #[cfg(test)] mod tests { - use super::{pane, WorkspaceView, WorkspaceViewHandle as _}; + use super::*; use crate::{editor::BufferView, settings, test::temp_tree}; use gpui::App; use serde_json::json; use std::{collections::HashSet, os::unix}; + #[test] + fn test_open_paths_action() { + App::test((), |app| { + let settings = settings::channel(&app.font_cache()).unwrap().1; + + init(app); + + let dir = temp_tree(json!({ + "a": { + "aa": null, + "ab": null, + }, + "b": { + "ba": null, + "bb": null, + }, + "c": { + "ca": null, + "cb": null, + }, + })); + + app.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths: vec![ + dir.path().join("a").to_path_buf(), + dir.path().join("b").to_path_buf(), + ], + settings: settings.clone(), + }, + ); + assert_eq!(app.window_ids().count(), 1); + + app.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths: vec![dir.path().join("a").to_path_buf()], + settings: settings.clone(), + }, + ); + assert_eq!(app.window_ids().count(), 1); + let workspace_view_1 = app + .root_view::(app.window_ids().next().unwrap()) + .unwrap(); + assert_eq!(workspace_view_1.read(app).worktrees().len(), 2); + + app.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths: vec![ + dir.path().join("b").to_path_buf(), + dir.path().join("c").to_path_buf(), + ], + settings: settings.clone(), + }, + ); + assert_eq!(app.window_ids().count(), 2); + }); + } + #[test] fn test_open_entry() { App::test_async((), |mut app| async move { @@ -556,7 +684,7 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings, ctx); + let mut workspace = Workspace::new(0, settings, ctx); smol::block_on(workspace.open_paths(&[dir.path().into()], ctx)); workspace }); @@ -642,7 +770,7 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings, ctx); + let mut workspace = Workspace::new(0, settings, ctx); workspace.open_path(dir1.path().into(), ctx); workspace }); @@ -716,7 +844,7 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings, ctx); + let mut workspace = Workspace::new(0, settings, ctx); workspace.open_path(dir.into(), ctx); workspace }); @@ -777,7 +905,7 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let (window_id, workspace) = app.add_window(|ctx| { - let mut workspace = WorkspaceView::new(0, settings, ctx); + let mut workspace = Workspace::new(0, settings, ctx); workspace.open_path(dir.path().into(), ctx); workspace }); diff --git a/zed/src/workspace/mod.rs b/zed/src/workspace/mod.rs deleted file mode 100644 index f651c31f533251e1c48c3008cee292f43248724f..0000000000000000000000000000000000000000 --- a/zed/src/workspace/mod.rs +++ /dev/null @@ -1,148 +0,0 @@ -pub mod pane; -pub mod pane_group; -pub mod workspace_view; - -pub use pane::*; -pub use pane_group::*; -pub use workspace_view::*; - -use crate::{ - settings::Settings, - watch::{self, Receiver}, -}; -use gpui::{MutableAppContext, PathPromptOptions}; -use std::path::PathBuf; - -pub fn init(app: &mut MutableAppContext) { - app.add_global_action("workspace:open", open); - app.add_global_action("workspace:open_paths", open_paths); - app.add_global_action("app:quit", quit); - pane::init(app); - workspace_view::init(app); -} - -pub struct OpenParams { - pub paths: Vec, - pub settings: watch::Receiver, -} - -fn open(settings: &Receiver, ctx: &mut MutableAppContext) { - let settings = settings.clone(); - ctx.prompt_for_paths( - PathPromptOptions { - files: true, - directories: true, - multiple: true, - }, - move |paths, ctx| { - if let Some(paths) = paths { - ctx.dispatch_global_action("workspace:open_paths", OpenParams { paths, settings }); - } - }, - ); -} - -fn open_paths(params: &OpenParams, app: &mut MutableAppContext) { - log::info!("open paths {:?}", params.paths); - - // Open paths in existing workspace if possible - for window_id in app.window_ids().collect::>() { - if let Some(handle) = app.root_view::(window_id) { - if handle.update(app, |view, ctx| { - if view.contains_paths(¶ms.paths, ctx.as_ref()) { - let open_paths = view.open_paths(¶ms.paths, ctx); - ctx.foreground().spawn(open_paths).detach(); - log::info!("open paths on existing workspace"); - true - } else { - false - } - }) { - return; - } - } - } - - log::info!("open new workspace"); - - // Add a new workspace if necessary - app.add_window(|ctx| { - let mut view = WorkspaceView::new(0, params.settings.clone(), ctx); - let open_paths = view.open_paths(¶ms.paths, ctx); - ctx.foreground().spawn(open_paths).detach(); - view - }); -} - -fn quit(_: &(), app: &mut MutableAppContext) { - app.platform().quit(); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{settings, test::*}; - use gpui::App; - use serde_json::json; - - #[test] - fn test_open_paths_action() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - - init(app); - - let dir = temp_tree(json!({ - "a": { - "aa": null, - "ab": null, - }, - "b": { - "ba": null, - "bb": null, - }, - "c": { - "ca": null, - "cb": null, - }, - })); - - app.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths: vec![ - dir.path().join("a").to_path_buf(), - dir.path().join("b").to_path_buf(), - ], - settings: settings.clone(), - }, - ); - assert_eq!(app.window_ids().count(), 1); - - app.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths: vec![dir.path().join("a").to_path_buf()], - settings: settings.clone(), - }, - ); - assert_eq!(app.window_ids().count(), 1); - let workspace_view_1 = app - .root_view::(app.window_ids().next().unwrap()) - .unwrap(); - assert_eq!(workspace_view_1.read(app).worktrees().len(), 2); - - app.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths: vec![ - dir.path().join("b").to_path_buf(), - dir.path().join("c").to_path_buf(), - ], - settings: settings.clone(), - }, - ); - assert_eq!(app.window_ids().count(), 2); - }); - } -}