Extract from Workspace a Project model that stores a window's worktrees

This commit is contained in:
Max Brunsfeld 2021-09-23 16:27:55 -07:00
parent 39fbf7d4d1
commit d561f50ab1
5 changed files with 125 additions and 82 deletions

View File

@ -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);

View File

@ -385,7 +385,7 @@ impl FileFinder {
.workspace
.upgrade(&cx)?
.read(cx)
.worktrees()
.worktrees(cx)
.iter()
.map(|tree| tree.read(cx).snapshot())
.collect::<Vec<_>>();

View File

@ -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;

88
zed/src/project.rs Normal file
View File

@ -0,0 +1,88 @@
use super::worktree::Worktree;
use anyhow::Result;
use gpui::{Entity, ModelContext, ModelHandle, Task};
pub struct Project {
worktrees: Vec<ModelHandle<Worktree>>,
}
pub enum Event {}
impl Project {
pub fn new() -> Self {
Self {
worktrees: Default::default(),
}
}
pub fn worktrees(&self) -> &[ModelHandle<Worktree>] {
&self.worktrees
}
pub fn worktree_for_id(&self, id: usize) -> Option<ModelHandle<Worktree>> {
self.worktrees
.iter()
.find(|worktree| worktree.id() == id)
.cloned()
}
pub fn add_worktree(&mut self, worktree: ModelHandle<Worktree>) {
self.worktrees.push(worktree);
}
pub fn share_worktree(
&self,
remote_id: u64,
cx: &mut ModelContext<Self>,
) -> Option<Task<Result<u64>>> {
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<Self>) {
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>) {
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;
}

View File

@ -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<ViewHandle<Pane>>,
active_pane: ViewHandle<Pane>,
worktrees: HashSet<ModelHandle<Worktree>>,
project: ModelHandle<Project>,
items: Vec<Box<dyn WeakItemHandle>>,
loading_items: HashMap<
(usize, Arc<Path>),
@ -360,6 +361,8 @@ pub struct Workspace {
impl Workspace {
pub fn new(app_state: &AppState, cx: &mut ViewContext<Self>) -> 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<ModelHandle<Worktree>> {
&self.worktrees
pub fn worktrees<'a>(&self, cx: &'a AppContext) -> &'a [ModelHandle<Worktree>] {
&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<Output = ()> + '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<Self>) {
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<Self>) {
@ -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<Self>) {
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<Self>) -> ViewHandle<Pane> {
@ -1186,7 +1140,7 @@ pub trait WorkspaceHandle {
impl WorkspaceHandle for ViewHandle<Workspace> {
fn file_entries(&self, cx: &AppContext) -> Vec<(usize, Arc<Path>)> {
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::<Workspace>(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::<HashSet<_>>();
@ -1526,7 +1480,7 @@ mod tests {
let tree = cx.read(|cx| {
workspace
.read(cx)
.worktrees()
.worktrees(cx)
.iter()
.next()
.unwrap()