Ensure we drop the last pane item
Previously, we weren't updating the toolbar's active item when emptying out a pane. This enhances an integration test to ensure that we don't hold references to any editors or buffers once we close everything.
This commit is contained in:
parent
41a27e6925
commit
4f4364d510
3 changed files with 78 additions and 12 deletions
|
@ -597,6 +597,15 @@ impl TestAppContext {
|
||||||
pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
|
pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
|
||||||
self.cx.borrow().leak_detector()
|
self.cx.borrow().leak_detector()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub fn assert_dropped(&self, handle: impl WeakHandle) {
|
||||||
|
self.cx
|
||||||
|
.borrow()
|
||||||
|
.leak_detector()
|
||||||
|
.lock()
|
||||||
|
.assert_dropped(handle.id())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncAppContext {
|
impl AsyncAppContext {
|
||||||
|
@ -3302,6 +3311,10 @@ pub trait Handle<T> {
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait WeakHandle {
|
||||||
|
fn id(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||||
pub enum EntityLocation {
|
pub enum EntityLocation {
|
||||||
Model(usize),
|
Model(usize),
|
||||||
|
@ -3576,6 +3589,12 @@ pub struct WeakModelHandle<T> {
|
||||||
model_type: PhantomData<T>,
|
model_type: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> WeakHandle for WeakModelHandle<T> {
|
||||||
|
fn id(&self) -> usize {
|
||||||
|
self.model_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<T> Send for WeakModelHandle<T> {}
|
unsafe impl<T> Send for WeakModelHandle<T> {}
|
||||||
unsafe impl<T> Sync for WeakModelHandle<T> {}
|
unsafe impl<T> Sync for WeakModelHandle<T> {}
|
||||||
|
|
||||||
|
@ -4145,6 +4164,12 @@ pub struct WeakViewHandle<T> {
|
||||||
view_type: PhantomData<T>,
|
view_type: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> WeakHandle for WeakViewHandle<T> {
|
||||||
|
fn id(&self) -> usize {
|
||||||
|
self.view_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: View> WeakViewHandle<T> {
|
impl<T: View> WeakViewHandle<T> {
|
||||||
fn new(window_id: usize, view_id: usize) -> Self {
|
fn new(window_id: usize, view_id: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -4471,11 +4496,36 @@ impl LeakDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn assert_dropped(&mut self, entity_id: usize) {
|
||||||
|
if let Some((type_name, backtraces)) = self.handle_backtraces.get_mut(&entity_id) {
|
||||||
|
for trace in backtraces.values_mut() {
|
||||||
|
if let Some(trace) = trace {
|
||||||
|
trace.resolve();
|
||||||
|
eprintln!("{:?}", crate::util::CwdBacktrace(trace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hint = if *LEAK_BACKTRACE {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
" – set LEAK_BACKTRACE=1 for more information"
|
||||||
|
};
|
||||||
|
|
||||||
|
panic!(
|
||||||
|
"{} handles to {} {} still exist{}",
|
||||||
|
backtraces.len(),
|
||||||
|
type_name.unwrap_or("entity"),
|
||||||
|
entity_id,
|
||||||
|
hint
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn detect(&mut self) {
|
pub fn detect(&mut self) {
|
||||||
let mut found_leaks = false;
|
let mut found_leaks = false;
|
||||||
for (id, (type_name, backtraces)) in self.handle_backtraces.iter_mut() {
|
for (id, (type_name, backtraces)) in self.handle_backtraces.iter_mut() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"leaked {} handles to {:?} {}",
|
"leaked {} handles to {} {}",
|
||||||
backtraces.len(),
|
backtraces.len(),
|
||||||
type_name.unwrap_or("entity"),
|
type_name.unwrap_or("entity"),
|
||||||
id
|
id
|
||||||
|
|
|
@ -580,6 +580,7 @@ impl Pane {
|
||||||
let item = pane.items.remove(item_ix);
|
let item = pane.items.remove(item_ix);
|
||||||
if pane.items.is_empty() {
|
if pane.items.is_empty() {
|
||||||
item.deactivated(cx);
|
item.deactivated(cx);
|
||||||
|
pane.update_toolbar(cx);
|
||||||
cx.emit(Event::Remove);
|
cx.emit(Event::Remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -725,32 +725,47 @@ mod tests {
|
||||||
.update(cx, |w, cx| w.open_path(file1.clone(), cx))
|
.update(cx, |w, cx| w.open_path(file1.clone(), cx))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx.read(|cx| {
|
|
||||||
assert_eq!(
|
let (editor_1, buffer) = pane_1.update(cx, |pane_1, cx| {
|
||||||
pane_1.read(cx).active_item().unwrap().project_path(cx),
|
let editor = pane_1.active_item().unwrap().downcast::<Editor>().unwrap();
|
||||||
Some(file1.clone())
|
assert_eq!(editor.project_path(cx), Some(file1.clone()));
|
||||||
);
|
let buffer = editor.update(cx, |editor, cx| {
|
||||||
|
editor.insert("dirt", cx);
|
||||||
|
editor.buffer().downgrade()
|
||||||
|
});
|
||||||
|
(editor.downgrade(), buffer)
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.dispatch_action(window_id, pane::Split(SplitDirection::Right));
|
cx.dispatch_action(window_id, pane::Split(SplitDirection::Right));
|
||||||
cx.update(|cx| {
|
let editor_2 = cx.update(|cx| {
|
||||||
let pane_2 = workspace.read(cx).active_pane().clone();
|
let pane_2 = workspace.read(cx).active_pane().clone();
|
||||||
assert_ne!(pane_1, pane_2);
|
assert_ne!(pane_1, pane_2);
|
||||||
|
|
||||||
let pane2_item = pane_2.read(cx).active_item().unwrap();
|
let pane2_item = pane_2.read(cx).active_item().unwrap();
|
||||||
assert_eq!(pane2_item.project_path(cx.as_ref()), Some(file1.clone()));
|
assert_eq!(pane2_item.project_path(cx.as_ref()), Some(file1.clone()));
|
||||||
|
|
||||||
cx.dispatch_action(
|
pane2_item.downcast::<Editor>().unwrap().downgrade()
|
||||||
window_id,
|
|
||||||
vec![workspace.id(), pane_2.id()],
|
|
||||||
&workspace::CloseActiveItem,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
cx.dispatch_action(window_id, workspace::CloseActiveItem);
|
||||||
|
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
workspace.read_with(cx, |workspace, _| {
|
workspace.read_with(cx, |workspace, _| {
|
||||||
assert_eq!(workspace.panes().len(), 1);
|
assert_eq!(workspace.panes().len(), 1);
|
||||||
assert_eq!(workspace.active_pane(), &pane_1);
|
assert_eq!(workspace.active_pane(), &pane_1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cx.dispatch_action(window_id, workspace::CloseActiveItem);
|
||||||
|
cx.foreground().run_until_parked();
|
||||||
|
cx.simulate_prompt_answer(window_id, 1);
|
||||||
|
cx.foreground().run_until_parked();
|
||||||
|
|
||||||
|
workspace.read_with(cx, |workspace, cx| {
|
||||||
|
assert!(workspace.active_item(cx).is_none());
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.assert_dropped(editor_1);
|
||||||
|
cx.assert_dropped(editor_2);
|
||||||
|
cx.assert_dropped(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue