diff --git a/server/src/rpc.rs b/server/src/rpc.rs index a9ffdad8997d8e2338223b0afdfdd05e3981fd1b..ea0a6c2968be6e39346c0fb834c72a53d30edcd2 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -1157,13 +1157,13 @@ mod tests { ); }); workspace_b - .condition(&cx_b, |workspace, _| workspace.worktrees().len() == 1) + .condition(&cx_b, |workspace, cx| workspace.worktrees(cx).len() == 1) .await; let local_worktree_id_b = workspace_b.read_with(&cx_b, |workspace, cx| { let active_pane = workspace.active_pane().read(cx); assert!(active_pane.active_item().is_none()); - workspace.worktrees().iter().next().unwrap().id() + workspace.worktrees(cx).first().unwrap().id() }); workspace_b .update(&mut cx_b, |worktree, cx| { @@ -1180,7 +1180,7 @@ mod tests { tree.as_local_mut().unwrap().unshare(cx); }); workspace_b - .condition(&cx_b, |workspace, _| workspace.worktrees().len() == 0) + .condition(&cx_b, |workspace, cx| workspace.worktrees(cx).len() == 0) .await; workspace_b.read_with(&cx_b, |workspace, cx| { let active_pane = workspace.active_pane().read(cx); diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 2749bbe59d7e3dd9d4132b1d3654a01fb91fe50a..caf560c16a54cd6ceadf2f4e035d25d3b26566a2 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -385,7 +385,7 @@ impl FileFinder { .workspace .upgrade(&cx)? .read(cx) - .worktrees() + .worktrees(cx) .iter() .map(|tree| tree.read(cx).snapshot()) .collect::>(); diff --git a/zed/src/lib.rs b/zed/src/lib.rs index b7fa3f83d06b00f730945cccd40f2f7a7869a077..bd09e386ee087add09233abf9d61014b6481f846 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -9,6 +9,7 @@ pub mod http; pub mod language; pub mod menus; pub mod people_panel; +pub mod project; pub mod project_browser; pub mod rpc; pub mod settings; diff --git a/zed/src/project.rs b/zed/src/project.rs new file mode 100644 index 0000000000000000000000000000000000000000..f05e4e4a8d94b0c4a0a2a7d0d408789ddc9a0039 --- /dev/null +++ b/zed/src/project.rs @@ -0,0 +1,88 @@ +use super::worktree::Worktree; +use anyhow::Result; +use gpui::{Entity, ModelContext, ModelHandle, Task}; + +pub struct Project { + worktrees: Vec>, +} + +pub enum Event {} + +impl Project { + pub fn new() -> Self { + Self { + worktrees: Default::default(), + } + } + + pub fn worktrees(&self) -> &[ModelHandle] { + &self.worktrees + } + + pub fn worktree_for_id(&self, id: usize) -> Option> { + self.worktrees + .iter() + .find(|worktree| worktree.id() == id) + .cloned() + } + + pub fn add_worktree(&mut self, worktree: ModelHandle) { + self.worktrees.push(worktree); + } + + pub fn share_worktree( + &self, + remote_id: u64, + cx: &mut ModelContext, + ) -> Option>> { + for worktree in &self.worktrees { + let task = worktree.update(cx, |worktree, cx| { + worktree.as_local_mut().and_then(|worktree| { + if worktree.remote_id() == Some(remote_id) { + Some(worktree.share(cx)) + } else { + None + } + }) + }); + if task.is_some() { + return task; + } + } + None + } + + pub fn unshare_worktree(&mut self, remote_id: u64, cx: &mut ModelContext) { + for worktree in &self.worktrees { + if worktree.update(cx, |worktree, cx| { + if let Some(worktree) = worktree.as_local_mut() { + if worktree.remote_id() == Some(remote_id) { + worktree.unshare(cx); + return true; + } + } + false + }) { + break; + } + } + } + + pub fn close_remote_worktree(&mut self, id: u64, cx: &mut ModelContext) { + self.worktrees.retain(|worktree| { + worktree.update(cx, |worktree, cx| { + if let Some(worktree) = worktree.as_remote_mut() { + if worktree.remote_id() == id { + worktree.close_all_buffers(cx); + return false; + } + } + true + }) + }); + } +} + +impl Entity for Project { + type Event = Event; +} diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 456b9e7042ab648be9e895d71f518718facba654..8d46a11309d97de050045c96dcfe07abba066a88 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -8,6 +8,7 @@ use crate::{ fs::Fs, language::LanguageRegistry, people_panel::{JoinWorktree, LeaveWorktree, PeoplePanel, ShareWorktree, UnshareWorktree}, + project::Project, project_browser::ProjectBrowser, rpc, settings::Settings, @@ -34,7 +35,7 @@ pub use pane_group::*; use postage::{prelude::Stream, watch}; use sidebar::{Side, Sidebar, ToggleSidebarItem}; use std::{ - collections::{hash_map::Entry, HashMap, HashSet}, + collections::{hash_map::Entry, HashMap}, future::Future, path::{Path, PathBuf}, sync::Arc, @@ -349,7 +350,7 @@ pub struct Workspace { right_sidebar: Sidebar, panes: Vec>, active_pane: ViewHandle, - worktrees: HashSet>, + project: ModelHandle, items: Vec>, loading_items: HashMap< (usize, Arc), @@ -360,6 +361,8 @@ pub struct Workspace { impl Workspace { pub fn new(app_state: &AppState, cx: &mut ViewContext) -> Self { + let project = cx.add_model(|_| Project::new()); + let pane = cx.add_view(|_| Pane::new(app_state.settings.clone())); let pane_id = pane.id(); cx.subscribe(&pane, move |me, _, event, cx| { @@ -424,15 +427,15 @@ impl Workspace { fs: app_state.fs.clone(), left_sidebar, right_sidebar, - worktrees: Default::default(), + project, items: Default::default(), loading_items: Default::default(), _observe_current_user, } } - pub fn worktrees(&self) -> &HashSet> { - &self.worktrees + pub fn worktrees<'a>(&self, cx: &'a AppContext) -> &'a [ModelHandle] { + &self.project.read(cx).worktrees() } pub fn contains_paths(&self, paths: &[PathBuf], cx: &AppContext) -> bool { @@ -440,7 +443,7 @@ impl Workspace { } pub fn contains_path(&self, path: &Path, cx: &AppContext) -> bool { - for worktree in &self.worktrees { + for worktree in self.worktrees(cx) { let worktree = worktree.read(cx).as_local(); if worktree.map_or(false, |w| w.contains_abs_path(path)) { return true; @@ -451,7 +454,7 @@ impl Workspace { pub fn worktree_scans_complete(&self, cx: &AppContext) -> impl Future + 'static { let futures = self - .worktrees + .worktrees(cx) .iter() .filter_map(|worktree| worktree.read(cx).as_local()) .map(|worktree| worktree.scan_complete()) @@ -511,7 +514,7 @@ impl Workspace { cx.spawn(|this, mut cx| async move { let mut entry_id = None; this.read_with(&cx, |this, cx| { - for tree in this.worktrees.iter() { + for tree in this.worktrees(cx) { if let Some(relative_path) = tree .read(cx) .as_local() @@ -559,7 +562,9 @@ impl Workspace { let worktree = Worktree::open_local(rpc, path, fs, languages, &mut cx).await?; this.update(&mut cx, |this, cx| { cx.observe(&worktree, |_, _, cx| cx.notify()).detach(); - this.worktrees.insert(worktree.clone()); + this.project.update(cx, |project, _| { + project.add_worktree(worktree.clone()); + }); cx.notify(); }); Ok(worktree) @@ -616,7 +621,7 @@ impl Workspace { let (worktree_id, path) = entry.clone(); - let worktree = match self.worktrees.get(&worktree_id).cloned() { + let worktree = match self.project.read(cx).worktree_for_id(worktree_id) { Some(worktree) => worktree, None => { log::error!("worktree {} does not exist", worktree_id); @@ -731,7 +736,7 @@ impl Workspace { if let Some(item) = self.active_item(cx) { let handle = cx.handle(); if item.entry_id(cx.as_ref()).is_none() { - let worktree = self.worktrees.iter().next(); + let worktree = self.worktrees(cx).first(); let start_abs_path = worktree .and_then(|w| w.read(cx).as_local()) .map_or(Path::new(""), |w| w.abs_path()) @@ -830,22 +835,8 @@ impl Workspace { rpc.authenticate_and_connect(&cx).await?; let task = this.update(&mut cx, |this, cx| { - for worktree in &this.worktrees { - let task = worktree.update(cx, |worktree, cx| { - worktree.as_local_mut().and_then(|worktree| { - if worktree.remote_id() == Some(remote_id) { - Some(worktree.share(cx)) - } else { - None - } - }) - }); - - if task.is_some() { - return task; - } - } - None + this.project + .update(cx, |project, cx| project.share_worktree(remote_id, cx)) }); if let Some(share_task) = task { @@ -861,19 +852,8 @@ impl Workspace { fn unshare_worktree(&mut self, action: &UnshareWorktree, cx: &mut ViewContext) { let remote_id = action.0; - for worktree in &self.worktrees { - if worktree.update(cx, |worktree, cx| { - if let Some(worktree) = worktree.as_local_mut() { - if worktree.remote_id() == Some(remote_id) { - worktree.unshare(cx); - return true; - } - } - false - }) { - break; - } - } + self.project + .update(cx, |project, cx| project.unshare_worktree(remote_id, cx)); } fn join_worktree(&mut self, action: &JoinWorktree, cx: &mut ViewContext) { @@ -890,23 +870,15 @@ impl Workspace { cx.observe(&worktree, |_, _, cx| cx.notify()).detach(); cx.subscribe(&worktree, move |this, _, event, cx| match event { worktree::Event::Closed => { - this.worktrees.retain(|worktree| { - worktree.update(cx, |worktree, cx| { - if let Some(worktree) = worktree.as_remote_mut() { - if worktree.remote_id() == worktree_id { - worktree.close_all_buffers(cx); - return false; - } - } - true - }) + this.project.update(cx, |project, cx| { + project.close_remote_worktree(worktree_id, cx); }); - cx.notify(); } }) .detach(); - this.worktrees.insert(worktree); + this.project + .update(cx, |project, _| project.add_worktree(worktree)); cx.notify(); }); @@ -919,27 +891,9 @@ impl Workspace { fn leave_worktree(&mut self, action: &LeaveWorktree, cx: &mut ViewContext) { let remote_id = action.0; - cx.spawn(|this, mut cx| { - async move { - this.update(&mut cx, |this, cx| { - this.worktrees.retain(|worktree| { - worktree.update(cx, |worktree, cx| { - if let Some(worktree) = worktree.as_remote_mut() { - if worktree.remote_id() == remote_id { - worktree.close_all_buffers(cx); - return false; - } - } - true - }) - }) - }); - - Ok(()) - } - .log_err() - }) - .detach(); + self.project.update(cx, |project, cx| { + project.close_remote_worktree(remote_id, cx); + }); } fn add_pane(&mut self, cx: &mut ViewContext) -> ViewHandle { @@ -1186,7 +1140,7 @@ pub trait WorkspaceHandle { impl WorkspaceHandle for ViewHandle { fn file_entries(&self, cx: &AppContext) -> Vec<(usize, Arc)> { self.read(cx) - .worktrees() + .worktrees(cx) .iter() .flat_map(|tree| { let tree_id = tree.id(); @@ -1254,8 +1208,8 @@ mod tests { .await; assert_eq!(cx.window_ids().len(), 1); let workspace_1 = cx.root_view::(cx.window_ids()[0]).unwrap(); - workspace_1.read_with(&cx, |workspace, _| { - assert_eq!(workspace.worktrees().len(), 2) + workspace_1.read_with(&cx, |workspace, cx| { + assert_eq!(workspace.worktrees(cx).len(), 2) }); cx.update(|cx| { @@ -1433,7 +1387,7 @@ mod tests { cx.read(|cx| { let worktree_roots = workspace .read(cx) - .worktrees() + .worktrees(cx) .iter() .map(|w| w.read(cx).as_local().unwrap().abs_path()) .collect::>(); @@ -1526,7 +1480,7 @@ mod tests { let tree = cx.read(|cx| { workspace .read(cx) - .worktrees() + .worktrees(cx) .iter() .next() .unwrap()