Keep weak handles to workspace items

This commit is contained in:
Max Brunsfeld 2021-05-07 09:52:15 -07:00
parent e6323f0d02
commit 3c3cf3b7c5
2 changed files with 84 additions and 53 deletions

View File

@ -2100,7 +2100,7 @@ impl<T: Entity> ModelHandle<T> {
} }
} }
fn downgrade(&self) -> WeakModelHandle<T> { pub fn downgrade(&self) -> WeakModelHandle<T> {
WeakModelHandle::new(self.model_id) WeakModelHandle::new(self.model_id)
} }
@ -2268,6 +2268,15 @@ impl<T: Entity> WeakModelHandle<T> {
} }
} }
impl<T> Clone for WeakModelHandle<T> {
fn clone(&self) -> Self {
Self {
model_id: self.model_id,
model_type: PhantomData,
}
}
}
pub struct ViewHandle<T> { pub struct ViewHandle<T> {
window_id: usize, window_id: usize,
view_id: usize, view_id: usize,

View File

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