Don't allow strong view handles to be read/updated with an AsyncAppContext
This avoids an invitation to hold strong view handles across async await points, which is a common source of leaks. Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
689e878bd8
commit
6317e885c7
14 changed files with 129 additions and 163 deletions
|
@ -61,7 +61,7 @@ fn join_project(action: &JoinProject, app_state: Arc<AppState>, cx: &mut AppCont
|
||||||
});
|
});
|
||||||
|
|
||||||
let workspace = if let Some(existing_workspace) = existing_workspace {
|
let workspace = if let Some(existing_workspace) = existing_workspace {
|
||||||
existing_workspace
|
existing_workspace.downgrade()
|
||||||
} else {
|
} else {
|
||||||
let active_call = cx.read(ActiveCall::global);
|
let active_call = cx.read(ActiveCall::global);
|
||||||
let room = active_call
|
let room = active_call
|
||||||
|
@ -93,7 +93,7 @@ fn join_project(action: &JoinProject, app_state: Arc<AppState>, cx: &mut AppCont
|
||||||
workspace
|
workspace
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
workspace
|
workspace.downgrade()
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.activate_window(workspace.window_id());
|
cx.activate_window(workspace.window_id());
|
||||||
|
|
|
@ -2715,14 +2715,15 @@ impl Editor {
|
||||||
let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
|
let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
|
||||||
project.apply_code_action(buffer, action, true, cx)
|
project.apply_code_action(buffer, action, true, cx)
|
||||||
});
|
});
|
||||||
|
let editor = editor.downgrade();
|
||||||
Some(cx.spawn(|workspace, cx| async move {
|
Some(cx.spawn(|workspace, cx| async move {
|
||||||
let project_transaction = apply_code_actions.await?;
|
let project_transaction = apply_code_actions.await?;
|
||||||
Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
|
Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn open_project_transaction(
|
async fn open_project_transaction(
|
||||||
this: ViewHandle<Editor>,
|
this: &WeakViewHandle<Editor>,
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
transaction: ProjectTransaction,
|
transaction: ProjectTransaction,
|
||||||
title: String,
|
title: String,
|
||||||
|
@ -5943,10 +5944,11 @@ impl Editor {
|
||||||
project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
|
project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let editor = editor.downgrade();
|
||||||
Some(cx.spawn(|workspace, mut cx| async move {
|
Some(cx.spawn(|workspace, mut cx| async move {
|
||||||
let project_transaction = rename.await?;
|
let project_transaction = rename.await?;
|
||||||
Self::open_project_transaction(
|
Self::open_project_transaction(
|
||||||
editor.clone(),
|
&editor,
|
||||||
workspace,
|
workspace,
|
||||||
project_transaction,
|
project_transaction,
|
||||||
format!("Rename: {} → {}", old_name, new_name),
|
format!("Rename: {} → {}", old_name, new_name),
|
||||||
|
@ -6761,7 +6763,8 @@ impl Editor {
|
||||||
let editor = editor
|
let editor = editor
|
||||||
.await?
|
.await?
|
||||||
.downcast::<Editor>()
|
.downcast::<Editor>()
|
||||||
.ok_or_else(|| anyhow!("opened item was not an editor"))?;
|
.ok_or_else(|| anyhow!("opened item was not an editor"))?
|
||||||
|
.downgrade();
|
||||||
editor.update(&mut cx, |editor, cx| {
|
editor.update(&mut cx, |editor, cx| {
|
||||||
let buffer = editor
|
let buffer = editor
|
||||||
.buffer()
|
.buffer()
|
||||||
|
@ -6783,6 +6786,7 @@ impl Editor {
|
||||||
|
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})??;
|
})??;
|
||||||
|
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
|
|
|
@ -67,6 +67,7 @@ impl FollowableItem for Editor {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let pane = pane.downgrade();
|
||||||
Some(cx.spawn(|mut cx| async move {
|
Some(cx.spawn(|mut cx| async move {
|
||||||
let mut buffers = futures::future::try_join_all(buffers).await?;
|
let mut buffers = futures::future::try_join_all(buffers).await?;
|
||||||
let editor = pane.read_with(&cx, |pane, cx| {
|
let editor = pane.read_with(&cx, |pane, cx| {
|
||||||
|
@ -127,7 +128,7 @@ impl FollowableItem for Editor {
|
||||||
};
|
};
|
||||||
|
|
||||||
update_editor_from_message(
|
update_editor_from_message(
|
||||||
editor.clone(),
|
editor.downgrade(),
|
||||||
project,
|
project,
|
||||||
proto::update_view::Editor {
|
proto::update_view::Editor {
|
||||||
selections: state.selections,
|
selections: state.selections,
|
||||||
|
@ -286,9 +287,6 @@ impl FollowableItem for Editor {
|
||||||
let update_view::Variant::Editor(message) = message;
|
let update_view::Variant::Editor(message) = message;
|
||||||
let project = project.clone();
|
let project = project.clone();
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let this = this
|
|
||||||
.upgrade(&cx)
|
|
||||||
.ok_or_else(|| anyhow!("editor was dropped"))?;
|
|
||||||
update_editor_from_message(this, project, message, &mut cx).await
|
update_editor_from_message(this, project, message, &mut cx).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -304,7 +302,7 @@ impl FollowableItem for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_editor_from_message(
|
async fn update_editor_from_message(
|
||||||
this: ViewHandle<Editor>,
|
this: WeakViewHandle<Editor>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
message: proto::update_view::Editor,
|
message: proto::update_view::Editor,
|
||||||
cx: &mut AsyncAppContext,
|
cx: &mut AsyncAppContext,
|
||||||
|
|
|
@ -124,11 +124,10 @@ impl FeedbackEditor {
|
||||||
&["Yes, Submit!", "No"],
|
&["Yes, Submit!", "No"],
|
||||||
);
|
);
|
||||||
|
|
||||||
let this = cx.handle();
|
|
||||||
let client = cx.global::<Arc<Client>>().clone();
|
let client = cx.global::<Arc<Client>>().clone();
|
||||||
let specs = self.system_specs.clone();
|
let specs = self.system_specs.clone();
|
||||||
|
|
||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let answer = answer.recv().await;
|
let answer = answer.recv().await;
|
||||||
|
|
||||||
if answer == Some(0) {
|
if answer == Some(0) {
|
||||||
|
|
|
@ -127,18 +127,8 @@ pub trait BorrowAppContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BorrowWindowContext {
|
pub trait BorrowWindowContext {
|
||||||
type ReturnValue<T>;
|
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T;
|
||||||
|
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T;
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(
|
|
||||||
&self,
|
|
||||||
window_id: usize,
|
|
||||||
f: F,
|
|
||||||
) -> Self::ReturnValue<T>;
|
|
||||||
fn update<T, F: FnOnce(&mut WindowContext) -> T>(
|
|
||||||
&mut self,
|
|
||||||
window_id: usize,
|
|
||||||
f: F,
|
|
||||||
) -> Self::ReturnValue<T>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -381,28 +371,6 @@ impl BorrowAppContext for AsyncAppContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorrowWindowContext for AsyncAppContext {
|
|
||||||
type ReturnValue<T> = Result<T>;
|
|
||||||
|
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> Result<T> {
|
|
||||||
self.0
|
|
||||||
.borrow()
|
|
||||||
.read_window(window_id, f)
|
|
||||||
.ok_or_else(|| anyhow!("window was closed"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update<T, F: FnOnce(&mut WindowContext) -> T>(
|
|
||||||
&mut self,
|
|
||||||
window_id: usize,
|
|
||||||
f: F,
|
|
||||||
) -> Result<T> {
|
|
||||||
self.0
|
|
||||||
.borrow_mut()
|
|
||||||
.update_window(window_id, f)
|
|
||||||
.ok_or_else(|| anyhow!("window was closed"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize);
|
type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize);
|
||||||
type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext);
|
type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext);
|
||||||
|
|
||||||
|
@ -3330,8 +3298,6 @@ impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
|
impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
|
||||||
type ReturnValue<T> = T;
|
|
||||||
|
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||||
BorrowWindowContext::read_with(&*self.window_context, window_id, f)
|
BorrowWindowContext::read_with(&*self.window_context, window_id, f)
|
||||||
}
|
}
|
||||||
|
@ -3384,8 +3350,6 @@ impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
|
impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
|
||||||
type ReturnValue<T> = T;
|
|
||||||
|
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||||
BorrowWindowContext::read_with(&*self.view_context, window_id, f)
|
BorrowWindowContext::read_with(&*self.view_context, window_id, f)
|
||||||
}
|
}
|
||||||
|
@ -3722,7 +3686,7 @@ impl<T: View> ViewHandle<T> {
|
||||||
cx.read_view(self)
|
cx.read_view(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::ReturnValue<S>
|
pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> S
|
||||||
where
|
where
|
||||||
C: BorrowWindowContext,
|
C: BorrowWindowContext,
|
||||||
F: FnOnce(&T, &ViewContext<T>) -> S,
|
F: FnOnce(&T, &ViewContext<T>) -> S,
|
||||||
|
@ -3733,7 +3697,7 @@ impl<T: View> ViewHandle<T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::ReturnValue<S>
|
pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> S
|
||||||
where
|
where
|
||||||
C: BorrowWindowContext,
|
C: BorrowWindowContext,
|
||||||
F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
|
F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
|
||||||
|
|
|
@ -390,8 +390,6 @@ impl BorrowAppContext for TestAppContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorrowWindowContext for TestAppContext {
|
impl BorrowWindowContext for TestAppContext {
|
||||||
type ReturnValue<T> = T;
|
|
||||||
|
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||||
self.cx
|
self.cx
|
||||||
.borrow()
|
.borrow()
|
||||||
|
|
|
@ -142,8 +142,6 @@ impl BorrowAppContext for WindowContext<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorrowWindowContext for WindowContext<'_> {
|
impl BorrowWindowContext for WindowContext<'_> {
|
||||||
type ReturnValue<T> = T;
|
|
||||||
|
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||||
if self.window_id == window_id {
|
if self.window_id == window_id {
|
||||||
f(self)
|
f(self)
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Some(Some(Ok(item))) = opened.first() {
|
if let Some(Some(Ok(item))) = opened.first() {
|
||||||
if let Some(editor) = item.downcast::<Editor>() {
|
if let Some(editor) = item.downcast::<Editor>().map(|editor| editor.downgrade()) {
|
||||||
editor.update(&mut cx, |editor, cx| {
|
editor.update(&mut cx, |editor, cx| {
|
||||||
let len = editor.buffer().read(cx).len(cx);
|
let len = editor.buffer().read(cx).len(cx);
|
||||||
editor.change_selections(Some(Autoscroll::center()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::center()), cx, |s| {
|
||||||
|
|
|
@ -811,8 +811,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorrowWindowContext for DockTestContext<'_> {
|
impl BorrowWindowContext for DockTestContext<'_> {
|
||||||
type ReturnValue<T> = T;
|
|
||||||
|
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||||
BorrowWindowContext::read_with(self.cx, window_id, f)
|
BorrowWindowContext::read_with(self.cx, window_id, f)
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,26 +214,10 @@ pub fn init(cx: &mut AppContext) {
|
||||||
Pane::reopen_closed_item(workspace, cx).detach();
|
Pane::reopen_closed_item(workspace, cx).detach();
|
||||||
});
|
});
|
||||||
cx.add_action(|workspace: &mut Workspace, action: &GoBack, cx| {
|
cx.add_action(|workspace: &mut Workspace, action: &GoBack, cx| {
|
||||||
Pane::go_back(
|
Pane::go_back(workspace, action.pane.clone(), cx).detach();
|
||||||
workspace,
|
|
||||||
action
|
|
||||||
.pane
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|weak_handle| weak_handle.upgrade(cx)),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.detach();
|
|
||||||
});
|
});
|
||||||
cx.add_action(|workspace: &mut Workspace, action: &GoForward, cx| {
|
cx.add_action(|workspace: &mut Workspace, action: &GoForward, cx| {
|
||||||
Pane::go_forward(
|
Pane::go_forward(workspace, action.pane.clone(), cx).detach();
|
||||||
workspace,
|
|
||||||
action
|
|
||||||
.pane
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|weak_handle| weak_handle.upgrade(cx)),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.detach();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,12 +376,12 @@ impl Pane {
|
||||||
|
|
||||||
pub fn go_back(
|
pub fn go_back(
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
pane: Option<ViewHandle<Pane>>,
|
pane: Option<WeakViewHandle<Pane>>,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
Self::navigate_history(
|
Self::navigate_history(
|
||||||
workspace,
|
workspace,
|
||||||
pane.unwrap_or_else(|| workspace.active_pane().clone()),
|
pane.unwrap_or_else(|| workspace.active_pane().downgrade()),
|
||||||
NavigationMode::GoingBack,
|
NavigationMode::GoingBack,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -405,12 +389,12 @@ impl Pane {
|
||||||
|
|
||||||
pub fn go_forward(
|
pub fn go_forward(
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
pane: Option<ViewHandle<Pane>>,
|
pane: Option<WeakViewHandle<Pane>>,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
Self::navigate_history(
|
Self::navigate_history(
|
||||||
workspace,
|
workspace,
|
||||||
pane.unwrap_or_else(|| workspace.active_pane().clone()),
|
pane.unwrap_or_else(|| workspace.active_pane().downgrade()),
|
||||||
NavigationMode::GoingForward,
|
NavigationMode::GoingForward,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -422,7 +406,7 @@ impl Pane {
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
Self::navigate_history(
|
Self::navigate_history(
|
||||||
workspace,
|
workspace,
|
||||||
workspace.active_pane().clone(),
|
workspace.active_pane().downgrade(),
|
||||||
NavigationMode::ReopeningClosedItem,
|
NavigationMode::ReopeningClosedItem,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -450,62 +434,62 @@ impl Pane {
|
||||||
|
|
||||||
fn navigate_history(
|
fn navigate_history(
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
pane: ViewHandle<Pane>,
|
pane: WeakViewHandle<Pane>,
|
||||||
mode: NavigationMode,
|
mode: NavigationMode,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
cx.focus(&pane);
|
let to_load = if let Some(pane) = pane.upgrade(cx) {
|
||||||
|
cx.focus(&pane);
|
||||||
|
|
||||||
let to_load = pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
loop {
|
loop {
|
||||||
// Retrieve the weak item handle from the history.
|
// Retrieve the weak item handle from the history.
|
||||||
let entry = pane.nav_history.borrow_mut().pop(mode, cx)?;
|
let entry = pane.nav_history.borrow_mut().pop(mode, cx)?;
|
||||||
|
|
||||||
// If the item is still present in this pane, then activate it.
|
// If the item is still present in this pane, then activate it.
|
||||||
if let Some(index) = entry
|
if let Some(index) = entry
|
||||||
.item
|
.item
|
||||||
.upgrade(cx)
|
.upgrade(cx)
|
||||||
.and_then(|v| pane.index_for_item(v.as_ref()))
|
.and_then(|v| pane.index_for_item(v.as_ref()))
|
||||||
{
|
{
|
||||||
let prev_active_item_index = pane.active_item_index;
|
let prev_active_item_index = pane.active_item_index;
|
||||||
pane.nav_history.borrow_mut().set_mode(mode);
|
pane.nav_history.borrow_mut().set_mode(mode);
|
||||||
pane.activate_item(index, true, true, cx);
|
pane.activate_item(index, true, true, cx);
|
||||||
pane.nav_history
|
pane.nav_history
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set_mode(NavigationMode::Normal);
|
.set_mode(NavigationMode::Normal);
|
||||||
|
|
||||||
let mut navigated = prev_active_item_index != pane.active_item_index;
|
let mut navigated = prev_active_item_index != pane.active_item_index;
|
||||||
if let Some(data) = entry.data {
|
if let Some(data) = entry.data {
|
||||||
navigated |= pane.active_item()?.navigate(data, cx);
|
navigated |= pane.active_item()?.navigate(data, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if navigated {
|
||||||
|
break None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// If the item is no longer present in this pane, then retrieve its
|
||||||
if navigated {
|
// project path in order to reopen it.
|
||||||
break None;
|
else {
|
||||||
|
break pane
|
||||||
|
.nav_history
|
||||||
|
.borrow()
|
||||||
|
.paths_by_item
|
||||||
|
.get(&entry.item.id())
|
||||||
|
.cloned()
|
||||||
|
.map(|project_path| (project_path, entry));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the item is no longer present in this pane, then retrieve its
|
})
|
||||||
// project path in order to reopen it.
|
} else {
|
||||||
else {
|
None
|
||||||
break pane
|
};
|
||||||
.nav_history
|
|
||||||
.borrow()
|
|
||||||
.paths_by_item
|
|
||||||
.get(&entry.item.id())
|
|
||||||
.cloned()
|
|
||||||
.map(|project_path| (project_path, entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some((project_path, entry)) = to_load {
|
if let Some((project_path, entry)) = to_load {
|
||||||
// If the item was no longer present, then load it again from its previous path.
|
// If the item was no longer present, then load it again from its previous path.
|
||||||
let pane = pane.downgrade();
|
|
||||||
let task = workspace.load_path(project_path, cx);
|
let task = workspace.load_path(project_path, cx);
|
||||||
cx.spawn(|workspace, mut cx| async move {
|
cx.spawn(|workspace, mut cx| async move {
|
||||||
let task = task.await;
|
let task = task.await;
|
||||||
let pane = pane
|
|
||||||
.upgrade(&cx)
|
|
||||||
.ok_or_else(|| anyhow!("pane was dropped"))?;
|
|
||||||
let mut navigated = false;
|
let mut navigated = false;
|
||||||
if let Some((project_entry_id, build_item)) = task.log_err() {
|
if let Some((project_entry_id, build_item)) = task.log_err() {
|
||||||
let prev_active_item_id = pane.update(&mut cx, |pane, _| {
|
let prev_active_item_id = pane.update(&mut cx, |pane, _| {
|
||||||
|
@ -514,15 +498,18 @@ impl Pane {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let item = workspace.update(&mut cx, |workspace, cx| {
|
let item = workspace.update(&mut cx, |workspace, cx| {
|
||||||
Self::open_item(
|
let pane = pane
|
||||||
|
.upgrade(cx)
|
||||||
|
.ok_or_else(|| anyhow!("pane was dropped"))?;
|
||||||
|
anyhow::Ok(Self::open_item(
|
||||||
workspace,
|
workspace,
|
||||||
pane.clone(),
|
pane.clone(),
|
||||||
project_entry_id,
|
project_entry_id,
|
||||||
true,
|
true,
|
||||||
cx,
|
cx,
|
||||||
build_item,
|
build_item,
|
||||||
)
|
))
|
||||||
})?;
|
})??;
|
||||||
|
|
||||||
pane.update(&mut cx, |pane, cx| {
|
pane.update(&mut cx, |pane, cx| {
|
||||||
navigated |= Some(item.id()) != prev_active_item_id;
|
navigated |= Some(item.id()) != prev_active_item_id;
|
||||||
|
@ -973,6 +960,7 @@ impl Pane {
|
||||||
// of what content they would be saving.
|
// of what content they would be saving.
|
||||||
items_to_close.sort_by_key(|item| !item.is_singleton(cx));
|
items_to_close.sort_by_key(|item| !item.is_singleton(cx));
|
||||||
|
|
||||||
|
let pane = pane.downgrade();
|
||||||
cx.spawn(|workspace, mut cx| async move {
|
cx.spawn(|workspace, mut cx| async move {
|
||||||
let mut saved_project_items_ids = HashSet::default();
|
let mut saved_project_items_ids = HashSet::default();
|
||||||
for item in items_to_close.clone() {
|
for item in items_to_close.clone() {
|
||||||
|
@ -1084,7 +1072,7 @@ impl Pane {
|
||||||
|
|
||||||
pub async fn save_item(
|
pub async fn save_item(
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
pane: &ViewHandle<Pane>,
|
pane: &WeakViewHandle<Pane>,
|
||||||
item_ix: usize,
|
item_ix: usize,
|
||||||
item: &dyn ItemHandle,
|
item: &dyn ItemHandle,
|
||||||
should_prompt_for_save: bool,
|
should_prompt_for_save: bool,
|
||||||
|
|
|
@ -1,28 +1,24 @@
|
||||||
use std::{
|
use crate::{
|
||||||
path::{Path, PathBuf},
|
dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId,
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
use anyhow::{Context, Result};
|
|
||||||
|
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
use gpui::{
|
|
||||||
platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle,
|
|
||||||
};
|
|
||||||
|
|
||||||
use db::sqlez::{
|
use db::sqlez::{
|
||||||
bindable::{Bind, Column, StaticColumnCount},
|
bindable::{Bind, Column, StaticColumnCount},
|
||||||
statement::Statement,
|
statement::Statement,
|
||||||
};
|
};
|
||||||
|
use gpui::{
|
||||||
|
platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle,
|
||||||
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use settings::DockAnchor;
|
use settings::DockAnchor;
|
||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
|
||||||
dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
|
pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
|
||||||
|
|
||||||
|
@ -134,7 +130,7 @@ impl SerializedPaneGroup {
|
||||||
}
|
}
|
||||||
SerializedPaneGroup::Pane(serialized_pane) => {
|
SerializedPaneGroup::Pane(serialized_pane) => {
|
||||||
let pane = workspace
|
let pane = workspace
|
||||||
.update(cx, |workspace, cx| workspace.add_pane(cx))
|
.update(cx, |workspace, cx| workspace.add_pane(cx).downgrade())
|
||||||
.log_err()?;
|
.log_err()?;
|
||||||
let active = serialized_pane.active;
|
let active = serialized_pane.active;
|
||||||
serialized_pane
|
serialized_pane
|
||||||
|
@ -146,8 +142,10 @@ impl SerializedPaneGroup {
|
||||||
.read_with(cx, |pane, _| pane.items_len() != 0)
|
.read_with(cx, |pane, _| pane.items_len() != 0)
|
||||||
.log_err()?
|
.log_err()?
|
||||||
{
|
{
|
||||||
|
let pane = pane.upgrade(cx)?;
|
||||||
Some((Member::Pane(pane.clone()), active.then(|| pane)))
|
Some((Member::Pane(pane.clone()), active.then(|| pane)))
|
||||||
} else {
|
} else {
|
||||||
|
let pane = pane.upgrade(cx)?;
|
||||||
workspace
|
workspace
|
||||||
.update(cx, |workspace, cx| workspace.remove_pane(pane, cx))
|
.update(cx, |workspace, cx| workspace.remove_pane(pane, cx))
|
||||||
.log_err()?;
|
.log_err()?;
|
||||||
|
@ -172,7 +170,7 @@ impl SerializedPane {
|
||||||
pub async fn deserialize_to(
|
pub async fn deserialize_to(
|
||||||
&self,
|
&self,
|
||||||
project: &ModelHandle<Project>,
|
project: &ModelHandle<Project>,
|
||||||
pane_handle: &ViewHandle<Pane>,
|
pane_handle: &WeakViewHandle<Pane>,
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
workspace: &WeakViewHandle<Workspace>,
|
workspace: &WeakViewHandle<Workspace>,
|
||||||
cx: &mut AsyncAppContext,
|
cx: &mut AsyncAppContext,
|
||||||
|
@ -196,8 +194,12 @@ impl SerializedPane {
|
||||||
|
|
||||||
if let Some(item_handle) = item_handle {
|
if let Some(item_handle) = item_handle {
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
let pane_handle = pane_handle
|
||||||
|
.upgrade(cx)
|
||||||
|
.ok_or_else(|| anyhow!("pane was dropped"))?;
|
||||||
Pane::add_item(workspace, &pane_handle, item_handle, false, false, None, cx);
|
Pane::add_item(workspace, &pane_handle, item_handle, false, false, None, cx);
|
||||||
})?;
|
anyhow::Ok(())
|
||||||
|
})??;
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.active {
|
if item.active {
|
||||||
|
|
|
@ -864,7 +864,7 @@ impl Workspace {
|
||||||
requesting_window_id: Option<usize>,
|
requesting_window_id: Option<usize>,
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
) -> Task<(
|
) -> Task<(
|
||||||
ViewHandle<Workspace>,
|
WeakViewHandle<Workspace>,
|
||||||
Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
|
Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
|
||||||
)> {
|
)> {
|
||||||
let project_handle = Project::local(
|
let project_handle = Project::local(
|
||||||
|
@ -982,6 +982,7 @@ impl Workspace {
|
||||||
.1
|
.1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let workspace = workspace.downgrade();
|
||||||
notify_if_database_failed(&workspace, &mut cx);
|
notify_if_database_failed(&workspace, &mut cx);
|
||||||
|
|
||||||
// Call open path for each of the project paths
|
// Call open path for each of the project paths
|
||||||
|
@ -1206,7 +1207,7 @@ impl Workspace {
|
||||||
.flat_map(|pane| {
|
.flat_map(|pane| {
|
||||||
pane.read(cx).items().filter_map(|item| {
|
pane.read(cx).items().filter_map(|item| {
|
||||||
if item.is_dirty(cx) {
|
if item.is_dirty(cx) {
|
||||||
Some((pane.clone(), item.boxed_clone()))
|
Some((pane.downgrade(), item.boxed_clone()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -2688,7 +2689,7 @@ impl Workspace {
|
||||||
workspace.read_with(&cx, |workspace, _| {
|
workspace.read_with(&cx, |workspace, _| {
|
||||||
(
|
(
|
||||||
workspace.project().clone(),
|
workspace.project().clone(),
|
||||||
workspace.dock_pane().clone(),
|
workspace.dock_pane().downgrade(),
|
||||||
workspace.last_active_center_pane.clone(),
|
workspace.last_active_center_pane.clone(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
@ -2769,7 +2770,7 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify_if_database_failed(workspace: &ViewHandle<Workspace>, cx: &mut AsyncAppContext) {
|
fn notify_if_database_failed(workspace: &WeakViewHandle<Workspace>, cx: &mut AsyncAppContext) {
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) {
|
if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) {
|
||||||
workspace.show_notification_once(0, cx, |cx| {
|
workspace.show_notification_once(0, cx, |cx| {
|
||||||
|
@ -2980,7 +2981,7 @@ pub struct WorkspaceCreated(WeakViewHandle<Workspace>);
|
||||||
pub fn activate_workspace_for_project(
|
pub fn activate_workspace_for_project(
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
|
predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
|
||||||
) -> Option<ViewHandle<Workspace>> {
|
) -> Option<WeakViewHandle<Workspace>> {
|
||||||
for window_id in cx.window_ids().collect::<Vec<_>>() {
|
for window_id in cx.window_ids().collect::<Vec<_>>() {
|
||||||
let handle = cx
|
let handle = cx
|
||||||
.update_window(window_id, |cx| {
|
.update_window(window_id, |cx| {
|
||||||
|
@ -2995,8 +2996,8 @@ pub fn activate_workspace_for_project(
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
if handle.is_some() {
|
if let Some(handle) = handle {
|
||||||
return handle;
|
return Some(handle.downgrade());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -3014,7 +3015,7 @@ pub fn open_paths(
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
) -> Task<
|
) -> Task<
|
||||||
Result<(
|
Result<(
|
||||||
ViewHandle<Workspace>,
|
WeakViewHandle<Workspace>,
|
||||||
Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
|
Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
|
||||||
)>,
|
)>,
|
||||||
> {
|
> {
|
||||||
|
@ -3700,7 +3701,7 @@ mod tests {
|
||||||
|
|
||||||
workspace
|
workspace
|
||||||
.update(cx, |workspace, cx| {
|
.update(cx, |workspace, cx| {
|
||||||
Pane::go_back(workspace, Some(pane.clone()), cx)
|
Pane::go_back(workspace, Some(pane.downgrade()), cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -674,13 +674,15 @@ async fn handle_cli_connection(
|
||||||
let wait = async move {
|
let wait = async move {
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
let (done_tx, done_rx) = oneshot::channel();
|
let (done_tx, done_rx) = oneshot::channel();
|
||||||
let _subscription = cx.update(|cx| {
|
if let Some(workspace) = workspace.upgrade(&cx) {
|
||||||
cx.observe_release(&workspace, move |_, _| {
|
let _subscription = cx.update(|cx| {
|
||||||
let _ = done_tx.send(());
|
cx.observe_release(&workspace, move |_, _| {
|
||||||
})
|
let _ = done_tx.send(());
|
||||||
});
|
})
|
||||||
drop(workspace);
|
});
|
||||||
let _ = done_rx.await;
|
drop(workspace);
|
||||||
|
let _ = done_rx.await;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ =
|
let _ =
|
||||||
futures::future::try_join_all(item_release_futures).await;
|
futures::future::try_join_all(item_release_futures).await;
|
||||||
|
|
|
@ -374,7 +374,14 @@ pub fn build_window_options(
|
||||||
fn restart(_: &Restart, cx: &mut gpui::AppContext) {
|
fn restart(_: &Restart, cx: &mut gpui::AppContext) {
|
||||||
let mut workspaces = cx
|
let mut workspaces = cx
|
||||||
.window_ids()
|
.window_ids()
|
||||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
.filter_map(|window_id| {
|
||||||
|
Some(
|
||||||
|
cx.root_view(window_id)?
|
||||||
|
.clone()
|
||||||
|
.downcast::<Workspace>()?
|
||||||
|
.downgrade(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// If multiple windows have unsaved changes, and need a save prompt,
|
// If multiple windows have unsaved changes, and need a save prompt,
|
||||||
|
@ -419,7 +426,14 @@ fn restart(_: &Restart, cx: &mut gpui::AppContext) {
|
||||||
fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
||||||
let mut workspaces = cx
|
let mut workspaces = cx
|
||||||
.window_ids()
|
.window_ids()
|
||||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
.filter_map(|window_id| {
|
||||||
|
Some(
|
||||||
|
cx.root_view(window_id)?
|
||||||
|
.clone()
|
||||||
|
.downcast::<Workspace>()?
|
||||||
|
.downgrade(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// If multiple windows have unsaved changes, and need a save prompt,
|
// If multiple windows have unsaved changes, and need a save prompt,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue