diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 43aa1f54a11e4b16b7831c87e51d2f8c2325ef71..8112fa7af41cc67360e2594d2f7048fd5cfa9aff 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -107,12 +107,6 @@ pub struct Pane { active_toolbar_visible: bool, } -pub(crate) struct FollowerState { - pub(crate) leader_id: PeerId, - pub(crate) current_view_id: Option, - pub(crate) items_by_leader_view_id: HashMap>, -} - pub trait Toolbar: View { fn active_item_changed( &mut self, @@ -266,7 +260,17 @@ impl Pane { if let Some((project_entry_id, build_item)) = task.log_err() { pane.update(&mut cx, |pane, cx| { pane.nav_history.borrow_mut().set_mode(mode); - let item = pane.open_item(project_entry_id, cx, build_item); + }); + let item = workspace.update(&mut cx, |workspace, cx| { + Self::open_item( + workspace, + pane.clone(), + project_entry_id, + cx, + build_item, + ) + }); + pane.update(&mut cx, |pane, cx| { pane.nav_history .borrow_mut() .set_mode(NavigationMode::Normal); @@ -289,52 +293,50 @@ impl Pane { } pub(crate) fn open_item( - &mut self, + workspace: &mut Workspace, + pane: ViewHandle, project_entry_id: ProjectEntryId, - cx: &mut ViewContext, + cx: &mut ViewContext, build_item: impl FnOnce(&mut MutableAppContext) -> Box, ) -> Box { - for (ix, item) in self.items.iter().enumerate() { - if item.project_entry_id(cx) == Some(project_entry_id) { - let item = item.boxed_clone(); - self.activate_item(ix, cx); - return item; + let existing_item = pane.update(cx, |pane, cx| { + for (ix, item) in pane.items.iter().enumerate() { + if item.project_entry_id(cx) == Some(project_entry_id) { + let item = item.boxed_clone(); + pane.activate_item(ix, cx); + return Some(item); + } } + None + }); + if let Some(existing_item) = existing_item { + existing_item + } else { + let item = build_item(cx); + Self::add_item(workspace, pane, item.boxed_clone(), cx); + item } - - let item = build_item(cx); - self.add_item(item.boxed_clone(), cx); - item } - pub(crate) fn add_item(&mut self, mut item: Box, cx: &mut ViewContext) { + pub(crate) fn add_item( + workspace: &mut Workspace, + pane: ViewHandle, + mut item: Box, + cx: &mut ViewContext, + ) { // Prevent adding the same item to the pane more than once. - if self.items.iter().any(|i| i.id() == item.id()) { + if pane.read(cx).items.iter().any(|i| i.id() == item.id()) { return; } - item.set_nav_history(self.nav_history.clone(), cx); - item.added_to_pane(cx); - let item_idx = cmp::min(self.active_item_index + 1, self.items.len()); - self.items.insert(item_idx, item); - self.activate_item(item_idx, cx); - cx.notify(); - } - - pub(crate) fn set_follow_state( - &mut self, - follower_state: FollowerState, - cx: &mut ViewContext, - ) -> Result<()> { - if let Some(current_view_id) = follower_state.current_view_id { - let item = follower_state - .items_by_leader_view_id - .get(¤t_view_id) - .ok_or_else(|| anyhow!("invalid current view id"))? - .clone(); - self.add_item(item, cx); - } - Ok(()) + item.set_nav_history(pane.read(cx).nav_history.clone(), cx); + item.added_to_pane(workspace, pane.clone(), cx); + pane.update(cx, |pane, cx| { + let item_idx = cmp::min(pane.active_item_index + 1, pane.items.len()); + pane.items.insert(item_idx, item); + pane.activate_item(item_idx, cx); + cx.notify(); + }); } pub fn items(&self) -> impl Iterator> { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 6f013d910f3525986b49de5c3b36a7cb8ef7868e..5955f81e62d41eb7cfd93e554d4649e5f56f2ab8 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -284,7 +284,12 @@ pub trait ItemHandle: 'static { fn boxed_clone(&self) -> Box; fn set_nav_history(&self, nav_history: Rc>, cx: &mut MutableAppContext); fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option>; - fn added_to_pane(&mut self, cx: &mut ViewContext); + fn added_to_pane( + &self, + workspace: &mut Workspace, + pane: ViewHandle, + cx: &mut ViewContext, + ); fn deactivated(&self, cx: &mut MutableAppContext); fn navigate(&self, data: Box, cx: &mut MutableAppContext); fn id(&self) -> usize; @@ -350,22 +355,37 @@ impl ItemHandle for ViewHandle { }) } - fn added_to_pane(&mut self, cx: &mut ViewContext) { - cx.subscribe(self, |pane, item, event, cx| { + fn added_to_pane( + &self, + workspace: &mut Workspace, + pane: ViewHandle, + cx: &mut ViewContext, + ) { + let pane = pane.downgrade(); + cx.subscribe(self, move |workspace, item, event, cx| { + let pane = if let Some(pane) = pane.upgrade(cx) { + pane + } else { + log::error!("unexpected item event after pane was dropped"); + return; + }; + if T::should_close_item_on_event(event) { - pane.close_item(item.id(), cx); + pane.update(cx, |pane, cx| pane.close_item(item.id(), cx)); return; } if T::should_activate_item_on_event(event) { - if let Some(ix) = pane.index_for_item(&item) { - pane.activate_item(ix, cx); - pane.activate(cx); - } + pane.update(cx, |pane, cx| { + if let Some(ix) = pane.index_for_item(&item) { + pane.activate_item(ix, cx); + pane.activate(cx); + } + }); } if T::should_update_tab_on_event(event) { - cx.notify() + pane.update(cx, |_, cx| cx.notify()); } if let Some(message) = item @@ -533,6 +553,7 @@ pub struct Workspace { status_bar: ViewHandle, project: ModelHandle, leader_state: LeaderState, + follower_states_by_leader: HashMap, _observe_current_user: Task<()>, } @@ -542,6 +563,11 @@ struct LeaderState { subscriptions: Vec, } +struct FollowerState { + current_view_id: Option, + items_by_leader_view_id: HashMap>, +} + impl Workspace { pub fn new(params: &WorkspaceParams, cx: &mut ViewContext) -> Self { cx.observe(¶ms.project, |_, project, cx| { @@ -614,6 +640,7 @@ impl Workspace { right_sidebar: Sidebar::new(Side::Right), project: params.project.clone(), leader_state: Default::default(), + follower_states_by_leader: Default::default(), _observe_current_user, }; this.project_remote_id_changed(this.project.read(cx).remote_id(), cx); @@ -910,8 +937,8 @@ impl Workspace { } pub fn add_item(&mut self, item: Box, cx: &mut ViewContext) { - self.active_pane() - .update(cx, |pane, cx| pane.add_item(item, cx)) + let pane = self.active_pane().clone(); + Pane::add_item(self, pane, item, cx); } pub fn open_path( @@ -926,10 +953,14 @@ impl Workspace { let pane = pane .upgrade(&cx) .ok_or_else(|| anyhow!("pane was closed"))?; - this.update(&mut cx, |_, cx| { - pane.update(cx, |pane, cx| { - Ok(pane.open_item(project_entry_id, cx, build_item)) - }) + this.update(&mut cx, |this, cx| { + Ok(Pane::open_item( + this, + pane, + project_entry_id, + cx, + build_item, + )) }) }) } @@ -1057,9 +1088,7 @@ impl Workspace { self.activate_pane(new_pane.clone(), cx); if let Some(item) = pane.read(cx).active_item() { if let Some(clone) = item.clone_on_split(cx.as_mut()) { - new_pane.update(cx, |new_pane, cx| { - new_pane.add_item(clone, cx); - }); + Pane::add_item(self, new_pane.clone(), clone, cx); } } self.center.split(&pane, &new_pane, direction).unwrap(); @@ -1149,21 +1178,32 @@ impl Workspace { } let items = futures::future::try_join_all(item_tasks).await?; - let mut items_by_leader_view_id = HashMap::default(); - for (view, item) in response.views.into_iter().zip(items) { - items_by_leader_view_id.insert(view.id as usize, item); - } - - pane.update(&mut cx, |pane, cx| { - pane.set_follow_state( - FollowerState { - leader_id, - current_view_id: response.current_view_id.map(|id| id as usize), - items_by_leader_view_id, - }, - cx, + let follower_state = FollowerState { + current_view_id: response.current_view_id.map(|id| id as usize), + items_by_leader_view_id: response + .views + .iter() + .map(|v| v.id as usize) + .zip(items) + .collect(), + }; + let current_item = if let Some(current_view_id) = follower_state.current_view_id + { + Some( + follower_state + .items_by_leader_view_id + .get(¤t_view_id) + .ok_or_else(|| anyhow!("invalid current view id"))? + .clone(), ) - })?; + } else { + None + }; + this.update(&mut cx, |this, cx| { + if let Some(item) = current_item { + Pane::add_item(this, pane, item, cx); + } + }); } Ok(()) })