diff --git a/gpui/src/app.rs b/gpui/src/app.rs index cc844581ae..2fe6af4212 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -2100,7 +2100,7 @@ impl ModelHandle { } } - fn downgrade(&self) -> WeakModelHandle { + pub fn downgrade(&self) -> WeakModelHandle { WeakModelHandle::new(self.model_id) } @@ -2268,6 +2268,15 @@ impl WeakModelHandle { } } +impl Clone for WeakModelHandle { + fn clone(&self) -> Self { + Self { + model_id: self.model_id, + model_type: PhantomData, + } + } +} + pub struct ViewHandle { window_id: usize, view_id: usize, diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 12888237e3..441e0d291d 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -1,14 +1,28 @@ pub mod pane; pub mod pane_group; +use crate::{ + editor::{Buffer, BufferView}, + settings::Settings, + time::ReplicaId, + watch::{self, Receiver}, + worktree::{FileHandle, 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, PathPromptOptions, View, + ViewContext, ViewHandle, WeakModelHandle, +}; +use log::error; pub use pane::*; pub use pane_group::*; - -use crate::{ - settings::Settings, - watch::{self, Receiver}, - worktree::FileHandle, +use smol::prelude::*; +use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::{hash_map::Entry, HashSet}, + path::Path, + sync::Arc, }; -use std::{collections::HashMap, fmt, path::PathBuf}; pub fn init(app: &mut MutableAppContext) { app.add_global_action("workspace:open", open); @@ -23,24 +37,6 @@ pub fn init(app: &mut MutableAppContext) { ]); pane::init(app); } -use crate::{ - editor::{Buffer, BufferView}, - time::ReplicaId, - 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, PathPromptOptions, View, - ViewContext, ViewHandle, -}; -use log::error; -use smol::prelude::*; -use std::{ - collections::{hash_map::Entry, HashSet}, - path::Path, - sync::Arc, -}; pub struct OpenParams { pub paths: Vec, @@ -137,15 +133,19 @@ pub trait ItemView: View { } pub trait ItemHandle: Send + Sync { - fn id(&self) -> usize; + fn boxed_clone(&self) -> Box; + fn downgrade(&self) -> Box; +} + +pub trait WeakItemHandle: Send + Sync { fn file<'a>(&'a self, ctx: &'a AppContext) -> Option<&'a FileHandle>; fn add_view( &self, window_id: usize, settings: watch::Receiver, app: &mut MutableAppContext, - ) -> Box; - fn boxed_clone(&self) -> Box; + ) -> Option>; + fn alive(&self, ctx: &AppContext) -> bool; } pub trait ItemViewHandle: Send + Sync { @@ -165,25 +165,37 @@ pub trait ItemViewHandle: Send + Sync { } impl ItemHandle for ModelHandle { - fn id(&self) -> usize { - self.id() + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) } + fn downgrade(&self) -> Box { + Box::new(self.downgrade()) + } +} + +impl WeakItemHandle for WeakModelHandle { fn file<'a>(&'a self, ctx: &'a AppContext) -> Option<&'a FileHandle> { - self.read(ctx).file() + self.upgrade(ctx).and_then(|h| h.read(ctx).file()) } fn add_view( &self, window_id: usize, - settings: watch::Receiver, - app: &mut MutableAppContext, - ) -> Box { - Box::new(app.add_view(window_id, |ctx| T::build_view(self.clone(), settings, ctx))) + settings: Receiver, + ctx: &mut MutableAppContext, + ) -> Option> { + if let Some(handle) = self.upgrade(ctx.as_ref()) { + Some(Box::new(ctx.add_view(window_id, |ctx| { + T::build_view(handle, settings, ctx) + }))) + } else { + None + } } - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) + fn alive(&self, ctx: &AppContext) -> bool { + self.upgrade(ctx).is_some() } } @@ -256,12 +268,6 @@ impl Clone for Box { } } -impl fmt::Debug for Box { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ItemHandle {{id: {}}}", self.id()) - } -} - #[derive(Debug)] pub struct State { pub modal: Option, @@ -276,7 +282,7 @@ pub struct Workspace { active_pane: ViewHandle, replica_id: ReplicaId, worktrees: HashSet>, - items: Vec>, + items: Vec>, loading_items: HashMap< (usize, Arc), postage::watch::Receiver, Arc>>>, @@ -427,7 +433,7 @@ impl Workspace { let buffer = ctx.add_model(|ctx| Buffer::new(self.replica_id, "", ctx)); let buffer_view = ctx.add_view(|ctx| BufferView::for_buffer(buffer.clone(), self.settings.clone(), ctx)); - self.items.push(Box::new(buffer)); + self.items.push(ItemHandle::downgrade(&buffer)); self.add_item(Box::new(buffer_view), ctx); } @@ -451,12 +457,25 @@ impl Workspace { // Otherwise, if this file is already open somewhere in the workspace, // then add another view for it. - if let Some(item) = self.items.iter().find(|item| { - item.file(ctx.as_ref()) - .map_or(false, |f| f.entry_id() == entry) - }) { - self.add_item(item.add_view(window_id, settings, ctx.as_mut()), ctx); - return None; + let mut i = 0; + while i < self.items.len() { + let item = &self.items[i]; + if item.alive(ctx.as_ref()) { + if item + .file(ctx.as_ref()) + .map_or(false, |f| f.entry_id() == entry) + { + self.add_item( + item.add_view(window_id, settings.clone(), ctx.as_mut()) + .unwrap(), + ctx, + ); + return None; + } + i += 1; + } else { + self.items.remove(i); + } } let (worktree_id, path) = entry.clone(); @@ -507,8 +526,11 @@ impl Workspace { me.loading_items.remove(&entry); match load_result { Ok(item) => { - me.items.push(item.clone()); - let view = item.add_view(window_id, settings, ctx.as_mut()); + let weak_item = item.downgrade(); + let view = weak_item + .add_view(window_id, settings, ctx.as_mut()) + .unwrap(); + me.items.push(weak_item); me.add_item(view, ctx); } Err(error) => {