diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 4b58b3ad0a..4ea031985e 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -63,20 +63,16 @@ impl ActivityIndicator { let auto_updater = AutoUpdater::get(cx); let this = cx.add_view(|cx: &mut ViewContext| { let mut status_events = languages.language_server_binary_statuses(); - cx.spawn_weak(|this, mut cx| async move { + cx.spawn(|this, mut cx| async move { while let Some((language, event)) = status_events.next().await { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.statuses.retain(|s| s.name != language.name()); - this.statuses.push(LspStatus { - name: language.name(), - status: event, - }); - cx.notify(); - })?; - } else { - break; - } + this.update(&mut cx, |this, cx| { + this.statuses.retain(|s| s.name != language.name()); + this.statuses.push(LspStatus { + name: language.name(), + status: event, + }); + cx.notify(); + })?; } anyhow::Ok(()) }) diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index b0aa9f1159..02cbab21d0 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -104,17 +104,15 @@ pub fn notify_of_any_new_update( cx.spawn(|mut cx| async move { let should_show_notification = should_show_notification.await?; if should_show_notification { - if let Some(workspace) = workspace.upgrade(&cx) { - workspace.update(&mut cx, |workspace, cx| { - workspace.show_notification(0, cx, |cx| { - cx.add_view(|_| UpdateNotification::new(version)) - }); - updater - .read(cx) - .set_should_show_update_notification(false, cx) - .detach_and_log_err(cx); - })?; - } + workspace.update(&mut cx, |workspace, cx| { + workspace.show_notification(0, cx, |cx| { + cx.add_view(|_| UpdateNotification::new(version)) + }); + updater + .read(cx) + .set_should_show_update_notification(false, cx) + .detach_and_log_err(cx); + })?; } anyhow::Ok(()) }) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 5a00f27ddf..62135900a3 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -18,8 +18,8 @@ use gpui::{ actions, platform::AppVersion, serde_json::{self, Value}, - AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, - AsyncAppContext, Entity, ModelHandle, Task, View, ViewContext, ViewHandle, + AnyModelHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, + ModelHandle, Task, View, ViewContext, WeakViewHandle, }; use lazy_static::lazy_static; use parking_lot::RwLock; @@ -221,7 +221,7 @@ enum WeakSubscriber { enum Subscriber { Model(AnyModelHandle), - View(AnyViewHandle), + View(AnyWeakViewHandle), } #[derive(Clone, Debug)] @@ -567,7 +567,7 @@ impl Client { H: 'static + Send + Sync - + Fn(ViewHandle, TypedEnvelope, Arc, AsyncAppContext) -> F, + + Fn(WeakViewHandle, TypedEnvelope, Arc, AsyncAppContext) -> F, F: 'static + Future>, { self.add_entity_message_handler::(move |handle, message, client, cx| { @@ -666,7 +666,7 @@ impl Client { H: 'static + Send + Sync - + Fn(ViewHandle, TypedEnvelope, Arc, AsyncAppContext) -> F, + + Fn(WeakViewHandle, TypedEnvelope, Arc, AsyncAppContext) -> F, F: 'static + Future>, { self.add_view_message_handler(move |entity, envelope, client, cx| { @@ -1273,7 +1273,15 @@ impl Client { pending.push(message); return; } - Some(weak_subscriber @ _) => subscriber = weak_subscriber.upgrade(cx), + Some(weak_subscriber @ _) => match weak_subscriber { + WeakSubscriber::Model(handle) => { + subscriber = handle.upgrade(cx).map(Subscriber::Model); + } + WeakSubscriber::View(handle) => { + subscriber = Some(Subscriber::View(handle.clone())); + } + WeakSubscriber::Pending(_) => {} + }, _ => {} } } @@ -1357,16 +1365,6 @@ impl Client { } } -impl WeakSubscriber { - fn upgrade(&self, cx: &AsyncAppContext) -> Option { - match self { - WeakSubscriber::Model(handle) => handle.upgrade(cx).map(Subscriber::Model), - WeakSubscriber::View(handle) => handle.upgrade(cx).map(Subscriber::View), - WeakSubscriber::Pending(_) => None, - } - } -} - fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option { if IMPERSONATE_LOGIN.is_some() { return None; diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 2902172684..557f9b58d9 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -61,7 +61,7 @@ fn join_project(action: &JoinProject, app_state: Arc, cx: &mut AppCont }); let workspace = if let Some(existing_workspace) = existing_workspace { - existing_workspace + existing_workspace.downgrade() } else { let active_call = cx.read(ActiveCall::global); let room = active_call @@ -93,7 +93,7 @@ fn join_project(action: &JoinProject, app_state: Arc, cx: &mut AppCont workspace }, ); - workspace + workspace.downgrade() }; cx.activate_window(workspace.window_id()); diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 49c9adb845..d0cf893a0f 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -79,7 +79,7 @@ impl IncomingCallNotification { let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx)); let caller_user_id = self.call.calling_user.id; let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id); - cx.spawn_weak(|_, mut cx| async move { + cx.spawn(|_, mut cx| async move { join.await?; if let Some(project_id) = initial_project_id { cx.update(|cx| { diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index cba3073caa..01a04cfd22 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -1,4 +1,5 @@ use gpui::{ + anyhow, elements::*, geometry::vector::Vector2F, impl_internal_actions, @@ -209,7 +210,8 @@ impl ContextMenu { cx.notify(); cx.spawn(|this, mut cx| async move { cx.background().timer(Duration::from_millis(50)).await; - this.update(&mut cx, |this, cx| this.cancel(&Default::default(), cx)) + this.update(&mut cx, |this, cx| this.cancel(&Default::default(), cx))?; + anyhow::Ok(()) }) .detach_and_log_err(cx); } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2fa5eabcd9..24a0b15a8f 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2477,7 +2477,7 @@ impl Editor { }); let id = post_inc(&mut self.next_completion_id); - let task = cx.spawn_weak(|this, mut cx| { + let task = cx.spawn(|this, mut cx| { async move { let menu = if let Some(completions) = completions.await.log_err() { let mut menu = CompletionsMenu { @@ -2510,9 +2510,6 @@ impl Editor { None }; - let this = this - .upgrade(&cx) - .ok_or_else(|| anyhow!("editor was dropped"))?; this.update(&mut cx, |this, cx| { this.completion_tasks.retain(|(task_id, _)| *task_id > id); @@ -2669,33 +2666,28 @@ impl Editor { let deployed_from_indicator = action.deployed_from_indicator; let mut task = self.code_actions_task.take(); - cx.spawn_weak(|this, mut cx| async move { + cx.spawn(|this, mut cx| async move { while let Some(prev_task) = task { prev_task.await; - task = this - .upgrade(&cx) - .ok_or_else(|| anyhow!("editor dropped"))? - .update(&mut cx, |this, _| this.code_actions_task.take())?; + task = this.update(&mut cx, |this, _| this.code_actions_task.take())?; } - this.upgrade(&cx) - .ok_or_else(|| anyhow!("editor dropped"))? - .update(&mut cx, |this, cx| { - if this.focused { - if let Some((buffer, actions)) = this.available_code_actions.clone() { - this.show_context_menu( - ContextMenu::CodeActions(CodeActionsMenu { - buffer, - actions, - selected_item: Default::default(), - list: Default::default(), - deployed_from_indicator, - }), - cx, - ); - } + this.update(&mut cx, |this, cx| { + if this.focused { + if let Some((buffer, actions)) = this.available_code_actions.clone() { + this.show_context_menu( + ContextMenu::CodeActions(CodeActionsMenu { + buffer, + actions, + selected_item: Default::default(), + list: Default::default(), + deployed_from_indicator, + }), + cx, + ); } - })?; + } + })?; Ok::<_, anyhow::Error>(()) }) @@ -2723,15 +2715,16 @@ impl Editor { let apply_code_actions = workspace.project().clone().update(cx, |project, cx| { project.apply_code_action(buffer, action, true, cx) }); + let editor = editor.downgrade(); Some(cx.spawn(|workspace, cx| async move { 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( - this: ViewHandle, - workspace: ViewHandle, + this: &WeakViewHandle, + workspace: WeakViewHandle, transaction: ProjectTransaction, title: String, mut cx: AsyncAppContext, @@ -2826,21 +2819,19 @@ impl Editor { let actions = project.update(cx, |project, cx| { project.code_actions(&start_buffer, start..end, cx) }); - self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move { + self.code_actions_task = Some(cx.spawn(|this, mut cx| async move { let actions = actions.await; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.available_code_actions = actions.log_err().and_then(|actions| { - if actions.is_empty() { - None - } else { - Some((start_buffer, actions.into())) - } - }); - cx.notify(); - }) - .log_err(); - } + this.update(&mut cx, |this, cx| { + this.available_code_actions = actions.log_err().and_then(|actions| { + if actions.is_empty() { + None + } else { + Some((start_buffer, actions.into())) + } + }); + cx.notify(); + }) + .log_err(); })); None } @@ -2865,9 +2856,8 @@ impl Editor { project.document_highlights(&cursor_buffer, cursor_buffer_position, cx) }); - self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move { - let highlights = highlights.log_err().await; - if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) { + self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move { + if let Some(highlights) = highlights.await.log_err() { this.update(&mut cx, |this, cx| { if this.pending_rename.is_some() { return; @@ -2961,7 +2951,7 @@ impl Editor { let (buffer, buffer_position) = self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; - self.copilot_state.pending_refresh = cx.spawn_weak(|this, mut cx| async move { + self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move { if debounce { cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await; } @@ -2976,21 +2966,20 @@ impl Editor { .flatten() .collect_vec(); - this.upgrade(&cx)? - .update(&mut cx, |this, cx| { - if !completions.is_empty() { - this.copilot_state.cycled = false; - this.copilot_state.pending_cycling_refresh = Task::ready(None); - this.copilot_state.completions.clear(); - this.copilot_state.active_completion_index = 0; - this.copilot_state.excerpt_id = Some(cursor.excerpt_id); - for completion in completions { - this.copilot_state.push_completion(completion); - } - this.update_visible_copilot_suggestion(cx); + this.update(&mut cx, |this, cx| { + if !completions.is_empty() { + this.copilot_state.cycled = false; + this.copilot_state.pending_cycling_refresh = Task::ready(None); + this.copilot_state.completions.clear(); + this.copilot_state.active_completion_index = 0; + this.copilot_state.excerpt_id = Some(cursor.excerpt_id); + for completion in completions { + this.copilot_state.push_completion(completion); } - }) - .log_err()?; + this.update_visible_copilot_suggestion(cx); + } + }) + .log_err()?; Some(()) }); @@ -3014,23 +3003,22 @@ impl Editor { let cursor = self.selections.newest_anchor().head(); let (buffer, buffer_position) = self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; - self.copilot_state.pending_cycling_refresh = cx.spawn_weak(|this, mut cx| async move { + self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move { let completions = copilot .update(&mut cx, |copilot, cx| { copilot.completions_cycling(&buffer, buffer_position, cx) }) .await; - this.upgrade(&cx)? - .update(&mut cx, |this, cx| { - this.copilot_state.cycled = true; - for completion in completions.log_err().into_iter().flatten() { - this.copilot_state.push_completion(completion); - } - this.copilot_state.cycle_completions(direction); - this.update_visible_copilot_suggestion(cx); - }) - .log_err()?; + this.update(&mut cx, |this, cx| { + this.copilot_state.cycled = true; + for completion in completions.log_err().into_iter().flatten() { + this.copilot_state.push_completion(completion); + } + this.copilot_state.cycle_completions(direction); + this.update_visible_copilot_suggestion(cx); + }) + .log_err()?; Some(()) }); @@ -5956,10 +5944,11 @@ impl Editor { project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx) }); + let editor = editor.downgrade(); Some(cx.spawn(|workspace, mut cx| async move { let project_transaction = rename.await?; Self::open_project_transaction( - editor.clone(), + &editor, workspace, project_transaction, format!("Rename: {} → {}", old_name, new_name), @@ -6770,11 +6759,12 @@ impl Editor { let editor = workspace.open_path(action.path.clone(), None, true, cx); let position = action.position; let anchor = action.anchor; - cx.spawn_weak(|_, mut cx| async move { + cx.spawn(|_, mut cx| async move { let editor = editor .await? .downcast::() - .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| { let buffer = editor .buffer() @@ -6796,6 +6786,7 @@ impl Editor { anyhow::Ok(()) })??; + anyhow::Ok(()) }) .detach_and_log_err(cx); diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 7c62f71bda..43c93cf33b 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -149,7 +149,7 @@ fn show_hover( } } - let task = cx.spawn_weak(|this, mut cx| { + let task = cx.spawn(|this, mut cx| { async move { // If we need to delay, delay a set amount initially before making the lsp request let delay = if !ignore_timeout { @@ -201,15 +201,13 @@ fn show_hover( }) }); - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, _| { - this.hover_state.diagnostic_popover = - local_diagnostic.map(|local_diagnostic| DiagnosticPopover { - local_diagnostic, - primary_diagnostic, - }); - })?; - } + this.update(&mut cx, |this, _| { + this.hover_state.diagnostic_popover = + local_diagnostic.map(|local_diagnostic| DiagnosticPopover { + local_diagnostic, + primary_diagnostic, + }); + })?; // Construct new hover popover from hover request let hover_popover = hover_request.await.ok().flatten().and_then(|hover_result| { @@ -239,23 +237,22 @@ fn show_hover( }) }); - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - if let Some(hover_popover) = hover_popover.as_ref() { - // Highlight the selected symbol using a background highlight - this.highlight_background::( - vec![hover_popover.symbol_range.clone()], - |theme| theme.editor.hover_popover.highlight, - cx, - ); - } else { - this.clear_background_highlights::(cx); - } + this.update(&mut cx, |this, cx| { + if let Some(hover_popover) = hover_popover.as_ref() { + // Highlight the selected symbol using a background highlight + this.highlight_background::( + vec![hover_popover.symbol_range.clone()], + |theme| theme.editor.hover_popover.highlight, + cx, + ); + } else { + this.clear_background_highlights::(cx); + } + + this.hover_state.info_popover = hover_popover; + cx.notify(); + })?; - this.hover_state.info_popover = hover_popover; - cx.notify(); - })?; - } Ok::<_, anyhow::Error>(()) } .log_err() diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 01db7de724..2eaf7b568e 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -3,7 +3,7 @@ use crate::{ movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, }; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use collections::HashSet; use futures::future::try_join_all; use gpui::{ @@ -67,6 +67,7 @@ impl FollowableItem for Editor { .collect::>() }); + let pane = pane.downgrade(); Some(cx.spawn(|mut cx| async move { let mut buffers = futures::future::try_join_all(buffers).await?; let editor = pane.read_with(&cx, |pane, cx| { @@ -127,7 +128,7 @@ impl FollowableItem for Editor { }; update_editor_from_message( - editor.clone(), + editor.downgrade(), project, proto::update_view::Editor { selections: state.selections, @@ -301,7 +302,7 @@ impl FollowableItem for Editor { } async fn update_editor_from_message( - this: ViewHandle, + this: WeakViewHandle, project: ModelHandle, message: proto::update_view::Editor, cx: &mut AsyncAppContext, @@ -863,7 +864,9 @@ impl Item for Editor { let buffer = project_item .downcast::() .context("Project item at stored path was not a buffer")?; - + let pane = pane + .upgrade(&cx) + .ok_or_else(|| anyhow!("pane was dropped"))?; Ok(cx.update(|cx| { cx.add_view(&pane, |cx| { let mut editor = Editor::for_buffer(buffer, Some(project), cx); diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index a61b97a683..ee0367103e 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -171,7 +171,7 @@ pub fn show_link_definition( } } - let task = cx.spawn_weak(|this, mut cx| { + let task = cx.spawn(|this, mut cx| { async move { // query the LSP for definition info let definition_request = cx.update(|cx| { @@ -202,67 +202,65 @@ pub fn show_link_definition( ) }); - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - // Clear any existing highlights - this.clear_text_highlights::(cx); - this.link_go_to_definition_state.kind = Some(definition_kind); - this.link_go_to_definition_state.symbol_range = result - .as_ref() - .and_then(|(symbol_range, _)| symbol_range.clone()); + this.update(&mut cx, |this, cx| { + // Clear any existing highlights + this.clear_text_highlights::(cx); + this.link_go_to_definition_state.kind = Some(definition_kind); + this.link_go_to_definition_state.symbol_range = result + .as_ref() + .and_then(|(symbol_range, _)| symbol_range.clone()); - if let Some((symbol_range, definitions)) = result { - this.link_go_to_definition_state.definitions = definitions.clone(); + if let Some((symbol_range, definitions)) = result { + this.link_go_to_definition_state.definitions = definitions.clone(); - let buffer_snapshot = buffer.read(cx).snapshot(); + let buffer_snapshot = buffer.read(cx).snapshot(); - // Only show highlight if there exists a definition to jump to that doesn't contain - // the current location. - let any_definition_does_not_contain_current_location = - definitions.iter().any(|definition| { - let target = &definition.target; - if target.buffer == buffer { - let range = &target.range; - // Expand range by one character as lsp definition ranges include positions adjacent - // but not contained by the symbol range - let start = buffer_snapshot.clip_offset( - range.start.to_offset(&buffer_snapshot).saturating_sub(1), - Bias::Left, - ); - let end = buffer_snapshot.clip_offset( - range.end.to_offset(&buffer_snapshot) + 1, - Bias::Right, - ); - let offset = buffer_position.to_offset(&buffer_snapshot); - !(start <= offset && end >= offset) - } else { - true - } - }); + // Only show highlight if there exists a definition to jump to that doesn't contain + // the current location. + let any_definition_does_not_contain_current_location = + definitions.iter().any(|definition| { + let target = &definition.target; + if target.buffer == buffer { + let range = &target.range; + // Expand range by one character as lsp definition ranges include positions adjacent + // but not contained by the symbol range + let start = buffer_snapshot.clip_offset( + range.start.to_offset(&buffer_snapshot).saturating_sub(1), + Bias::Left, + ); + let end = buffer_snapshot.clip_offset( + range.end.to_offset(&buffer_snapshot) + 1, + Bias::Right, + ); + let offset = buffer_position.to_offset(&buffer_snapshot); + !(start <= offset && end >= offset) + } else { + true + } + }); - if any_definition_does_not_contain_current_location { - // If no symbol range returned from language server, use the surrounding word. - let highlight_range = symbol_range.unwrap_or_else(|| { - let snapshot = &snapshot.buffer_snapshot; - let (offset_range, _) = snapshot.surrounding_word(trigger_point); + if any_definition_does_not_contain_current_location { + // If no symbol range returned from language server, use the surrounding word. + let highlight_range = symbol_range.unwrap_or_else(|| { + let snapshot = &snapshot.buffer_snapshot; + let (offset_range, _) = snapshot.surrounding_word(trigger_point); - snapshot.anchor_before(offset_range.start) - ..snapshot.anchor_after(offset_range.end) - }); + snapshot.anchor_before(offset_range.start) + ..snapshot.anchor_after(offset_range.end) + }); - // Highlight symbol using theme link definition highlight style - let style = cx.global::().theme.editor.link_definition; - this.highlight_text::( - vec![highlight_range], - style, - cx, - ); - } else { - hide_link_definition(this, cx); - } + // Highlight symbol using theme link definition highlight style + let style = cx.global::().theme.editor.link_definition; + this.highlight_text::( + vec![highlight_range], + style, + cx, + ); + } else { + hide_link_definition(this, cx); } - })?; - } + } + })?; Ok::<_, anyhow::Error>(()) } diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index 04b2860c19..ab8bbe0d5e 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -245,16 +245,14 @@ impl ScrollManager { } if cx.default_global::().0 { - self.hide_scrollbar_task = Some(cx.spawn_weak(|editor, mut cx| async move { + self.hide_scrollbar_task = Some(cx.spawn(|editor, mut cx| async move { cx.background().timer(SCROLLBAR_SHOW_INTERVAL).await; - if let Some(editor) = editor.upgrade(&cx) { - editor - .update(&mut cx, |editor, cx| { - editor.scroll_manager.show_scrollbars = false; - cx.notify(); - }) - .log_err(); - } + editor + .update(&mut cx, |editor, cx| { + editor.scroll_manager.show_scrollbars = false; + cx.notify(); + }) + .log_err(); })); } else { self.hide_scrollbar_task = None; diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index e5a406bc73..7bf5328048 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -124,11 +124,10 @@ impl FeedbackEditor { &["Yes, Submit!", "No"], ); - let this = cx.handle(); let client = cx.global::>().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; if answer == Some(0) { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index a1f9002fba..c201febed5 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -127,18 +127,8 @@ pub trait BorrowAppContext { } pub trait BorrowWindowContext { - type ReturnValue; - - fn read_with T>( - &self, - window_id: usize, - f: F, - ) -> Self::ReturnValue; - fn update T>( - &mut self, - window_id: usize, - f: F, - ) -> Self::ReturnValue; + fn read_with T>(&self, window_id: usize, f: F) -> T; + fn update T>(&mut self, window_id: usize, f: F) -> T; } #[derive(Clone)] @@ -381,28 +371,6 @@ impl BorrowAppContext for AsyncAppContext { } } -impl BorrowWindowContext for AsyncAppContext { - type ReturnValue = Result; - - fn read_with T>(&self, window_id: usize, f: F) -> Result { - self.0 - .borrow() - .read_window(window_id, f) - .ok_or_else(|| anyhow!("window was closed")) - } - - fn update T>( - &mut self, - window_id: usize, - f: F, - ) -> Result { - 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 GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext); @@ -3262,26 +3230,16 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { pub fn spawn_labeled(&mut self, task_label: &'static str, f: F) -> Task where - F: FnOnce(ViewHandle, AsyncAppContext) -> Fut, + F: FnOnce(WeakViewHandle, AsyncAppContext) -> Fut, Fut: 'static + Future, S: 'static, { - let handle = self.handle(); + let handle = self.weak_handle(); self.window_context .spawn_labeled(task_label, |cx| f(handle, cx)) } pub fn spawn(&mut self, f: F) -> Task - where - F: FnOnce(ViewHandle, AsyncAppContext) -> Fut, - Fut: 'static + Future, - S: 'static, - { - let handle = self.handle(); - self.window_context.spawn(|cx| f(handle, cx)) - } - - pub fn spawn_weak(&mut self, f: F) -> Task where F: FnOnce(WeakViewHandle, AsyncAppContext) -> Fut, Fut: 'static + Future, @@ -3340,8 +3298,6 @@ impl BorrowAppContext for ViewContext<'_, '_, V> { } impl BorrowWindowContext for ViewContext<'_, '_, V> { - type ReturnValue = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { BorrowWindowContext::read_with(&*self.window_context, window_id, f) } @@ -3394,8 +3350,6 @@ impl BorrowAppContext for EventContext<'_, '_, '_, V> { } impl BorrowWindowContext for EventContext<'_, '_, '_, V> { - type ReturnValue = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { BorrowWindowContext::read_with(&*self.view_context, window_id, f) } @@ -3732,7 +3686,7 @@ impl ViewHandle { cx.read_view(self) } - pub fn read_with(&self, cx: &C, read: F) -> C::ReturnValue + pub fn read_with(&self, cx: &C, read: F) -> S where C: BorrowWindowContext, F: FnOnce(&T, &ViewContext) -> S, @@ -3743,7 +3697,7 @@ impl ViewHandle { }) } - pub fn update(&self, cx: &mut C, update: F) -> C::ReturnValue + pub fn update(&self, cx: &mut C, update: F) -> S where C: BorrowWindowContext, F: FnOnce(&mut T, &mut ViewContext) -> S, @@ -4094,15 +4048,31 @@ impl WeakViewHandle { cx.read_with(|cx| cx.upgrade_view_handle(self)) } + pub fn read_with( + &self, + cx: &AsyncAppContext, + read: impl FnOnce(&V, &ViewContext) -> T, + ) -> Result { + cx.read(|cx| { + let handle = cx + .upgrade_view_handle(self) + .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?; + cx.read_window(self.window_id, |cx| handle.read_with(cx, read)) + .ok_or_else(|| anyhow!("window was removed")) + }) + } + pub fn update( &self, - cx: &mut impl BorrowAppContext, + cx: &mut AsyncAppContext, update: impl FnOnce(&mut V, &mut ViewContext) -> T, - ) -> Option { + ) -> Result { cx.update(|cx| { - let handle = cx.upgrade_view_handle(self)?; - + let handle = cx + .upgrade_view_handle(self) + .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?; cx.update_window(self.window_id, |cx| handle.update(cx, update)) + .ok_or_else(|| anyhow!("window was removed")) }) } } @@ -4150,9 +4120,24 @@ impl AnyWeakViewHandle { self.view_id } + fn is(&self) -> bool { + TypeId::of::() == self.view_type + } + pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option { cx.read_with(|cx| cx.upgrade_any_view_handle(self)) } + + pub fn downcast(self) -> Option> { + if self.is::() { + Some(WeakViewHandle { + any_handle: self, + view_type: PhantomData, + }) + } else { + None + } + } } impl Hash for AnyWeakViewHandle { diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 9aa6eb3393..3a03a81c47 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -390,8 +390,6 @@ impl BorrowAppContext for TestAppContext { } impl BorrowWindowContext for TestAppContext { - type ReturnValue = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { self.cx .borrow() diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 908f58e33e..f54c18c755 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -142,8 +142,6 @@ impl BorrowAppContext for WindowContext<'_> { } impl BorrowWindowContext for WindowContext<'_> { - type ReturnValue = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { if self.window_id == window_id { f(self) diff --git a/crates/gpui/src/elements/tooltip.rs b/crates/gpui/src/elements/tooltip.rs index 4cd924a48c..a879b996d5 100644 --- a/crates/gpui/src/elements/tooltip.rs +++ b/crates/gpui/src/elements/tooltip.rs @@ -99,14 +99,12 @@ impl Tooltip { let mut debounce = state.debounce.borrow_mut(); if debounce.is_none() { - *debounce = Some(cx.spawn_weak({ + *debounce = Some(cx.spawn({ let state = state.clone(); |view, mut cx| async move { cx.background().timer(DEBOUNCE_TIMEOUT).await; state.visible.set(true); - if let Some(view) = view.upgrade(&cx) { - view.update(&mut cx, |_, cx| cx.notify()).log_err(); - } + view.update(&mut cx, |_, cx| cx.notify()).log_err(); } })); } diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index c439edd87d..4b9622ece9 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -56,7 +56,7 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut AppContext) { .await; if let Some(Some(Ok(item))) = opened.first() { - if let Some(editor) = item.downcast::() { + if let Some(editor) = item.downcast::().map(|editor| editor.downgrade()) { editor.update(&mut cx, |editor, cx| { let len = editor.buffer().read(cx).len(cx); editor.change_selections(Some(Autoscroll::center()), cx, |s| { diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index 27ddd63e75..29da7c926d 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -102,7 +102,7 @@ impl PickerDelegate for LanguageSelectorDelegate { let language = self.language_registry.language_for_name(language_name); let project = self.project.downgrade(); let buffer = self.buffer.downgrade(); - cx.spawn_weak(|_, mut cx| async move { + cx.spawn(|_, mut cx| async move { let language = language.await?; let project = project .upgrade(&cx) @@ -138,7 +138,7 @@ impl PickerDelegate for LanguageSelectorDelegate { ) -> gpui::Task<()> { let background = cx.background().clone(); let candidates = self.candidates.clone(); - cx.spawn_weak(|this, mut cx| async move { + cx.spawn(|this, mut cx| async move { let matches = if query.is_empty() { candidates .into_iter() @@ -162,17 +162,15 @@ impl PickerDelegate for LanguageSelectorDelegate { .await }; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - let delegate = this.delegate_mut(); - delegate.matches = matches; - delegate.selected_index = delegate - .selected_index - .min(delegate.matches.len().saturating_sub(1)); - cx.notify(); - }) - .log_err(); - } + this.update(&mut cx, |this, cx| { + let delegate = this.delegate_mut(); + delegate.matches = matches; + delegate.selected_index = delegate + .selected_index + .min(delegate.matches.len().saturating_sub(1)); + cx.notify(); + }) + .log_err(); }) } diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 7274962a5b..83717af0fd 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -238,10 +238,9 @@ impl Picker { pub fn update_matches(&mut self, query: String, cx: &mut ViewContext) { let update = self.delegate.update_matches(query, cx); self.matches_updated(cx); - self.pending_update_matches = cx.spawn_weak(|this, mut cx| async move { + self.pending_update_matches = cx.spawn(|this, mut cx| async move { update.await; - this.upgrade(&cx)? - .update(&mut cx, |this, cx| this.matches_updated(cx)) + this.update(&mut cx, |this, cx| this.matches_updated(cx)) .log_err() }); } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index c730c4d38a..6720ef93de 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -1,4 +1,3 @@ -use anyhow::anyhow; use editor::{ combine_syntax_and_fuzzy_match_highlights, scroll::autoscroll::Autoscroll, styled_runs_for_code_label, Bias, Editor, @@ -117,11 +116,8 @@ impl PickerDelegate for ProjectSymbolsDelegate { }); let symbol = symbol.clone(); let workspace = self.workspace.clone(); - cx.spawn_weak(|_, mut cx| async move { + cx.spawn(|_, mut cx| async move { let buffer = buffer.await?; - let workspace = workspace - .upgrade(&cx) - .ok_or_else(|| anyhow!("workspace was dropped"))?; workspace.update(&mut cx, |workspace, cx| { let position = buffer .read(cx) @@ -161,36 +157,33 @@ impl PickerDelegate for ProjectSymbolsDelegate { let symbols = self .project .update(cx, |project, cx| project.symbols(&query, cx)); - cx.spawn_weak(|this, mut cx| async move { + cx.spawn(|this, mut cx| async move { let symbols = symbols.await.log_err(); - if let Some(this) = this.upgrade(&cx) { - if let Some(symbols) = symbols { - this.update(&mut cx, |this, cx| { - let delegate = this.delegate_mut(); - let project = delegate.project.read(cx); - let (visible_match_candidates, external_match_candidates) = symbols - .iter() - .enumerate() - .map(|(id, symbol)| { - StringMatchCandidate::new( - id, - symbol.label.text[symbol.label.filter_range.clone()] - .to_string(), - ) - }) - .partition(|candidate| { - project - .entry_for_path(&symbols[candidate.id].path, cx) - .map_or(false, |e| !e.is_ignored) - }); + if let Some(symbols) = symbols { + this.update(&mut cx, |this, cx| { + let delegate = this.delegate_mut(); + let project = delegate.project.read(cx); + let (visible_match_candidates, external_match_candidates) = symbols + .iter() + .enumerate() + .map(|(id, symbol)| { + StringMatchCandidate::new( + id, + symbol.label.text[symbol.label.filter_range.clone()].to_string(), + ) + }) + .partition(|candidate| { + project + .entry_for_path(&symbols[candidate.id].path, cx) + .map_or(false, |e| !e.is_ignored) + }); - delegate.visible_match_candidates = visible_match_candidates; - delegate.external_match_candidates = external_match_candidates; - delegate.symbols = symbols; - delegate.filter(&query, cx); - }) - .log_err(); - } + delegate.visible_match_candidates = visible_match_candidates; + delegate.external_match_candidates = external_match_candidates; + delegate.symbols = symbols; + delegate.filter(&query, cx); + }) + .log_err(); } }) } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 629a58387c..91ca99c5c3 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -196,12 +196,12 @@ impl ToolbarItemView for BufferSearchBar { if let Some(searchable_item_handle) = item.and_then(|item| item.to_searchable_item_handle(cx)) { - let handle = cx.weak_handle(); + let this = cx.weak_handle(); self.active_searchable_item_subscription = Some(searchable_item_handle.subscribe_to_search_events( cx, Box::new(move |search_event, cx| { - if let Some(this) = handle.upgrade(cx) { + if let Some(this) = this.upgrade(cx) { this.update(cx, |this, cx| { this.on_active_searchable_item_event(search_event, cx) }); @@ -582,36 +582,33 @@ impl BufferSearchBar { let matches = active_searchable_item.find_matches(query, cx); let active_searchable_item = active_searchable_item.downgrade(); - self.pending_search = Some(cx.spawn_weak(|this, mut cx| async move { + self.pending_search = Some(cx.spawn(|this, mut cx| async move { let matches = matches.await; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - if let Some(active_searchable_item) = WeakSearchableItemHandle::upgrade( - active_searchable_item.as_ref(), - cx, - ) { - this.seachable_items_with_matches - .insert(active_searchable_item.downgrade(), matches); + this.update(&mut cx, |this, cx| { + if let Some(active_searchable_item) = + WeakSearchableItemHandle::upgrade(active_searchable_item.as_ref(), cx) + { + this.seachable_items_with_matches + .insert(active_searchable_item.downgrade(), matches); - this.update_match_index(cx); - if !this.dismissed { - let matches = this - .seachable_items_with_matches - .get(&active_searchable_item.downgrade()) - .unwrap(); - active_searchable_item.update_matches(matches, cx); - if select_closest_match { - if let Some(match_ix) = this.active_match_index { - active_searchable_item - .activate_match(match_ix, matches, cx); - } + this.update_match_index(cx); + if !this.dismissed { + let matches = this + .seachable_items_with_matches + .get(&active_searchable_item.downgrade()) + .unwrap(); + active_searchable_item.update_matches(matches, cx); + if select_closest_match { + if let Some(match_ix) = this.active_match_index { + active_searchable_item + .activate_match(match_ix, matches, cx); } } - cx.notify(); } - }) - .log_err(); - } + cx.notify(); + } + }) + .log_err(); })); } } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index b7ca3c01c7..3c7d9a0ae7 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -2,13 +2,7 @@ mod persistence; pub mod terminal_button; pub mod terminal_element; -use std::{ - borrow::Cow, - ops::RangeInclusive, - path::{Path, PathBuf}, - time::Duration, -}; - +use anyhow::anyhow; use context_menu::{ContextMenu, ContextMenuItem}; use dirs::home_dir; use gpui::{ @@ -26,6 +20,12 @@ use serde::Deserialize; use settings::{Settings, TerminalBlink, WorkingDirectory}; use smallvec::{smallvec, SmallVec}; use smol::Timer; +use std::{ + borrow::Cow, + ops::RangeInclusive, + path::{Path, PathBuf}, + time::Duration, +}; use terminal::{ alacritty_terminal::{ index::Point, @@ -275,15 +275,10 @@ impl TerminalView { cx.notify(); let epoch = self.next_blink_epoch(); - cx.spawn(|this, mut cx| { - let this = this.downgrade(); - async move { - Timer::after(CURSOR_BLINK_INTERVAL).await; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx)) - .log_err(); - } - } + cx.spawn(|this, mut cx| async move { + Timer::after(CURSOR_BLINK_INTERVAL).await; + this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx)) + .log_err(); }) .detach(); } @@ -294,15 +289,10 @@ impl TerminalView { cx.notify(); let epoch = self.next_blink_epoch(); - cx.spawn(|this, mut cx| { - let this = this.downgrade(); - async move { - Timer::after(CURSOR_BLINK_INTERVAL).await; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx)) - .log_err(); - } - } + cx.spawn(|this, mut cx| async move { + Timer::after(CURSOR_BLINK_INTERVAL).await; + this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx)) + .log_err(); }) .detach(); } @@ -646,6 +636,9 @@ impl Item for TerminalView { }) }); + let pane = pane + .upgrade(&cx) + .ok_or_else(|| anyhow!("pane was dropped"))?; cx.update(|cx| { let terminal = project.update(cx, |project, cx| { project.create_terminal(cwd, window_id, cx) diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index ee7066b80f..1f2d73df14 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -163,7 +163,7 @@ impl PickerDelegate for ThemeSelectorDelegate { }) .collect::>(); - cx.spawn_weak(|this, mut cx| async move { + cx.spawn(|this, mut cx| async move { let matches = if query.is_empty() { candidates .into_iter() @@ -187,17 +187,15 @@ impl PickerDelegate for ThemeSelectorDelegate { .await }; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - let delegate = this.delegate_mut(); - delegate.matches = matches; - delegate.selected_index = delegate - .selected_index - .min(delegate.matches.len().saturating_sub(1)); - delegate.show_selected_theme(cx); - }) - .log_err(); - } + this.update(&mut cx, |this, cx| { + let delegate = this.delegate_mut(); + delegate.matches = matches; + delegate.selected_index = delegate + .selected_index + .min(delegate.matches.len().saturating_sub(1)); + delegate.show_selected_theme(cx); + }) + .log_err(); }) } diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index 1db340bde9..7347a559a9 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -81,7 +81,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { }) .collect::>(); - cx.spawn_weak(|this, mut cx| async move { + cx.spawn(|this, mut cx| async move { let matches = if query.is_empty() { candidates .into_iter() @@ -105,16 +105,14 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { .await }; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, _| { - let delegate = this.delegate_mut(); - delegate.matches = matches; - delegate.selected_index = delegate - .selected_index - .min(delegate.matches.len().saturating_sub(1)); - }) - .log_err(); - } + this.update(&mut cx, |this, _| { + let delegate = this.delegate_mut(); + delegate.matches = matches; + delegate.selected_index = delegate + .selected_index + .min(delegate.matches.len().saturating_sub(1)); + }) + .log_err(); }) } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index c80f71c422..67cfea3d9a 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -811,8 +811,6 @@ mod tests { } impl BorrowWindowContext for DockTestContext<'_> { - type ReturnValue = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { BorrowWindowContext::read_with(self.cx, window_id, f) } diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 52c8a60908..b30107c726 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -3,7 +3,7 @@ use crate::{ FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId, }; -use anyhow::{anyhow, Result}; +use anyhow::Result; use client::{proto, Client}; use gpui::{ fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View, @@ -479,10 +479,8 @@ impl ItemHandle for ViewHandle { }, ); } else { - cx.spawn_weak(|workspace, mut cx| async move { + cx.spawn(|workspace, mut cx| async move { workspace - .upgrade(&cx) - .ok_or_else(|| anyhow!("workspace was dropped"))? .update(&mut cx, |workspace, cx| { item.git_diff_recalc( workspace.project().clone(), diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 43bfaed1ad..aa913986de 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -214,26 +214,10 @@ pub fn init(cx: &mut AppContext) { Pane::reopen_closed_item(workspace, cx).detach(); }); cx.add_action(|workspace: &mut Workspace, action: &GoBack, cx| { - Pane::go_back( - workspace, - action - .pane - .as_ref() - .and_then(|weak_handle| weak_handle.upgrade(cx)), - cx, - ) - .detach(); + Pane::go_back(workspace, action.pane.clone(), cx).detach(); }); cx.add_action(|workspace: &mut Workspace, action: &GoForward, cx| { - Pane::go_forward( - workspace, - action - .pane - .as_ref() - .and_then(|weak_handle| weak_handle.upgrade(cx)), - cx, - ) - .detach(); + Pane::go_forward(workspace, action.pane.clone(), cx).detach(); }); } @@ -392,12 +376,12 @@ impl Pane { pub fn go_back( workspace: &mut Workspace, - pane: Option>, + pane: Option>, cx: &mut ViewContext, ) -> Task> { Self::navigate_history( workspace, - pane.unwrap_or_else(|| workspace.active_pane().clone()), + pane.unwrap_or_else(|| workspace.active_pane().downgrade()), NavigationMode::GoingBack, cx, ) @@ -405,12 +389,12 @@ impl Pane { pub fn go_forward( workspace: &mut Workspace, - pane: Option>, + pane: Option>, cx: &mut ViewContext, ) -> Task> { Self::navigate_history( workspace, - pane.unwrap_or_else(|| workspace.active_pane().clone()), + pane.unwrap_or_else(|| workspace.active_pane().downgrade()), NavigationMode::GoingForward, cx, ) @@ -422,7 +406,7 @@ impl Pane { ) -> Task> { Self::navigate_history( workspace, - workspace.active_pane().clone(), + workspace.active_pane().downgrade(), NavigationMode::ReopeningClosedItem, cx, ) @@ -450,62 +434,62 @@ impl Pane { fn navigate_history( workspace: &mut Workspace, - pane: ViewHandle, + pane: WeakViewHandle, mode: NavigationMode, cx: &mut ViewContext, ) -> Task> { - cx.focus(&pane); + let to_load = if let Some(pane) = pane.upgrade(cx) { + cx.focus(&pane); - let to_load = pane.update(cx, |pane, cx| { - loop { - // Retrieve the weak item handle from the history. - let entry = pane.nav_history.borrow_mut().pop(mode, cx)?; + pane.update(cx, |pane, cx| { + loop { + // Retrieve the weak item handle from the history. + let entry = pane.nav_history.borrow_mut().pop(mode, cx)?; - // If the item is still present in this pane, then activate it. - if let Some(index) = entry - .item - .upgrade(cx) - .and_then(|v| pane.index_for_item(v.as_ref())) - { - let prev_active_item_index = pane.active_item_index; - pane.nav_history.borrow_mut().set_mode(mode); - pane.activate_item(index, true, true, cx); - pane.nav_history - .borrow_mut() - .set_mode(NavigationMode::Normal); + // If the item is still present in this pane, then activate it. + if let Some(index) = entry + .item + .upgrade(cx) + .and_then(|v| pane.index_for_item(v.as_ref())) + { + let prev_active_item_index = pane.active_item_index; + pane.nav_history.borrow_mut().set_mode(mode); + pane.activate_item(index, true, true, cx); + pane.nav_history + .borrow_mut() + .set_mode(NavigationMode::Normal); - let mut navigated = prev_active_item_index != pane.active_item_index; - if let Some(data) = entry.data { - navigated |= pane.active_item()?.navigate(data, cx); + let mut navigated = prev_active_item_index != pane.active_item_index; + if let Some(data) = entry.data { + navigated |= pane.active_item()?.navigate(data, cx); + } + + if navigated { + break None; + } } - - if navigated { - break None; + // If the item is no longer present in this pane, then retrieve its + // project path in order to reopen it. + 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 { - break pane - .nav_history - .borrow() - .paths_by_item - .get(&entry.item.id()) - .cloned() - .map(|project_path| (project_path, entry)); - } - } - }); + }) + } else { + None + }; if let Some((project_path, entry)) = to_load { // 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); cx.spawn(|workspace, mut cx| async move { let task = task.await; - let pane = pane - .upgrade(&cx) - .ok_or_else(|| anyhow!("pane was dropped"))?; let mut navigated = false; if let Some((project_entry_id, build_item)) = task.log_err() { let prev_active_item_id = pane.update(&mut cx, |pane, _| { @@ -514,15 +498,18 @@ impl Pane { })?; 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, pane.clone(), project_entry_id, true, cx, build_item, - ) - })?; + )) + })??; pane.update(&mut cx, |pane, cx| { navigated |= Some(item.id()) != prev_active_item_id; @@ -973,6 +960,7 @@ impl Pane { // of what content they would be saving. items_to_close.sort_by_key(|item| !item.is_singleton(cx)); + let pane = pane.downgrade(); cx.spawn(|workspace, mut cx| async move { let mut saved_project_items_ids = HashSet::default(); for item in items_to_close.clone() { @@ -1084,7 +1072,7 @@ impl Pane { pub async fn save_item( project: ModelHandle, - pane: &ViewHandle, + pane: &WeakViewHandle, item_ix: usize, item: &dyn ItemHandle, should_prompt_for_save: bool, @@ -2006,7 +1994,9 @@ impl NavHistory { fn did_update(&self, cx: &mut WindowContext) { if let Some(pane) = self.pane.upgrade(cx) { - cx.defer(move |cx| pane.update(cx, |pane, cx| pane.history_updated(cx))); + cx.defer(move |cx| { + pane.update(cx, |pane, cx| pane.history_updated(cx)); + }); } } } diff --git a/crates/workspace/src/persistence/model.rs b/crates/workspace/src/persistence/model.rs index 523a80af7c..08da41d7e8 100644 --- a/crates/workspace/src/persistence/model.rs +++ b/crates/workspace/src/persistence/model.rs @@ -1,26 +1,24 @@ -use std::{ - path::{Path, PathBuf}, - sync::Arc, +use crate::{ + dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId, }; - -use anyhow::{Context, Result}; - +use anyhow::{anyhow, Context, Result}; use async_recursion::async_recursion; -use gpui::{platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle}; - use db::sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; +use gpui::{ + platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle, +}; use project::Project; use settings::DockAnchor; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; use util::ResultExt; use uuid::Uuid; -use crate::{ - dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId, -}; - #[derive(Debug, Clone, PartialEq, Eq)] pub struct WorkspaceLocation(Arc>); @@ -97,7 +95,7 @@ impl SerializedPaneGroup { &self, project: &ModelHandle, workspace_id: WorkspaceId, - workspace: &ViewHandle, + workspace: &WeakViewHandle, cx: &mut AsyncAppContext, ) -> Option<(Member, Option>)> { match self { @@ -132,7 +130,7 @@ impl SerializedPaneGroup { } SerializedPaneGroup::Pane(serialized_pane) => { let pane = workspace - .update(cx, |workspace, cx| workspace.add_pane(cx)) + .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade()) .log_err()?; let active = serialized_pane.active; serialized_pane @@ -144,8 +142,10 @@ impl SerializedPaneGroup { .read_with(cx, |pane, _| pane.items_len() != 0) .log_err()? { + let pane = pane.upgrade(cx)?; Some((Member::Pane(pane.clone()), active.then(|| pane))) } else { + let pane = pane.upgrade(cx)?; workspace .update(cx, |workspace, cx| workspace.remove_pane(pane, cx)) .log_err()?; @@ -170,9 +170,9 @@ impl SerializedPane { pub async fn deserialize_to( &self, project: &ModelHandle, - pane_handle: &ViewHandle, + pane_handle: &WeakViewHandle, workspace_id: WorkspaceId, - workspace: &ViewHandle, + workspace: &WeakViewHandle, cx: &mut AsyncAppContext, ) -> Result<()> { let mut active_item_index = None; @@ -181,13 +181,7 @@ impl SerializedPane { let item_handle = pane_handle .update(cx, |_, cx| { if let Some(deserializer) = cx.global::().get(&item.kind) { - deserializer( - project, - workspace.downgrade(), - workspace_id, - item.item_id, - cx, - ) + deserializer(project, workspace.clone(), workspace_id, item.item_id, cx) } else { Task::ready(Err(anyhow::anyhow!( "Deserializer does not exist for item kind: {}", @@ -200,8 +194,12 @@ impl SerializedPane { if let Some(item_handle) = item_handle { 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); - })?; + anyhow::Ok(()) + })??; } if item.active { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c0fb1a9448..7524b84bf8 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -314,7 +314,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { Some(workspace.prepare_to_close(false, cx)) }; - Some(cx.spawn_weak(|_, mut cx| async move { + Some(cx.spawn(|_, mut cx| async move { let window_id_to_replace = if let Some(close_task) = close_task { if !close_task.await? { return Ok(()); @@ -588,7 +588,7 @@ impl DelayedDebouncedEditAction { self.cancel_channel = Some(sender); let previous_task = self.task.take(); - self.task = Some(cx.spawn_weak(|workspace, mut cx| async move { + self.task = Some(cx.spawn(|workspace, mut cx| async move { let mut timer = cx.background().timer(delay).fuse(); if let Some(previous_task) = previous_task { previous_task.await; @@ -599,13 +599,11 @@ impl DelayedDebouncedEditAction { _ = timer => {} } - if let Some(workspace) = workspace.upgrade(&cx) { - if let Some(result) = workspace - .update(&mut cx, |workspace, cx| (f)(workspace, cx)) - .log_err() - { - result.await.log_err(); - } + if let Some(result) = workspace + .update(&mut cx, |workspace, cx| (f)(workspace, cx)) + .log_err() + { + result.await.log_err(); } })); } @@ -733,16 +731,14 @@ impl Workspace { let client = project.read(cx).client(); let mut current_user = user_store.read(cx).watch_current_user(); let mut connection_status = client.status(); - let _observe_current_user = cx.spawn_weak(|this, mut cx| async move { + let _observe_current_user = cx.spawn(|this, mut cx| async move { current_user.recv().await; connection_status.recv().await; let mut stream = Stream::map(current_user, drop).merge(Stream::map(connection_status, drop)); while stream.recv().await.is_some() { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |_, cx| cx.notify())?; - } + this.update(&mut cx, |_, cx| cx.notify())?; } anyhow::Ok(()) }); @@ -752,10 +748,9 @@ impl Workspace { // that each asynchronous operation can be run in order. let (leader_updates_tx, mut leader_updates_rx) = mpsc::unbounded::<(PeerId, proto::UpdateFollowers)>(); - let _apply_leader_updates = cx.spawn_weak(|this, mut cx| async move { + let _apply_leader_updates = cx.spawn(|this, mut cx| async move { while let Some((leader_id, update)) = leader_updates_rx.next().await { - let Some(this) = this.upgrade(&cx) else { break }; - Self::process_leader_update(this, leader_id, update, &mut cx) + Self::process_leader_update(&this, leader_id, update, &mut cx) .await .log_err(); } @@ -869,7 +864,7 @@ impl Workspace { requesting_window_id: Option, cx: &mut AppContext, ) -> Task<( - ViewHandle, + WeakViewHandle, Vec, anyhow::Error>>>, )> { let project_handle = Project::local( @@ -987,6 +982,7 @@ impl Workspace { .1 }); + let workspace = workspace.downgrade(); notify_if_database_failed(&workspace, &mut cx); // Call open path for each of the project paths @@ -1129,7 +1125,7 @@ impl Workspace { ) -> Option>> { let window_id = cx.window_id(); let prepare = self.prepare_to_close(false, cx); - Some(cx.spawn_weak(|_, mut cx| async move { + Some(cx.spawn(|_, mut cx| async move { if prepare.await? { cx.remove_window(window_id); } @@ -1211,7 +1207,7 @@ impl Workspace { .flat_map(|pane| { pane.read(cx).items().filter_map(|item| { if item.is_dirty(cx) { - Some((pane.clone(), item.boxed_clone())) + Some((pane.downgrade(), item.boxed_clone())) } else { None } @@ -1220,7 +1216,7 @@ impl Workspace { .collect::>(); let project = self.project.clone(); - cx.spawn_weak(|_, mut cx| async move { + cx.spawn(|_, mut cx| async move { for (pane, item) in dirty_items { let (singleton, project_entry_ids) = cx.read(|cx| (item.is_singleton(cx), item.project_entry_ids(cx))); @@ -1975,32 +1971,30 @@ impl Workspace { leader_id: Some(leader_id), }); - Some(cx.spawn_weak(|this, mut cx| async move { + Some(cx.spawn(|this, mut cx| async move { let response = request.await?; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, _| { - let state = this - .follower_states_by_leader - .get_mut(&leader_id) - .and_then(|states_by_pane| states_by_pane.get_mut(&pane)) - .ok_or_else(|| anyhow!("following interrupted"))?; - state.active_view_id = if let Some(active_view_id) = response.active_view_id { - Some(ViewId::from_proto(active_view_id)?) - } else { - None - }; - Ok::<_, anyhow::Error>(()) - })??; - Self::add_views_from_leader( - this.clone(), - leader_id, - vec![pane], - response.views, - &mut cx, - ) - .await?; - this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?; - } + this.update(&mut cx, |this, _| { + let state = this + .follower_states_by_leader + .get_mut(&leader_id) + .and_then(|states_by_pane| states_by_pane.get_mut(&pane)) + .ok_or_else(|| anyhow!("following interrupted"))?; + state.active_view_id = if let Some(active_view_id) = response.active_view_id { + Some(ViewId::from_proto(active_view_id)?) + } else { + None + }; + Ok::<_, anyhow::Error>(()) + })??; + Self::add_views_from_leader( + this.clone(), + leader_id, + vec![pane], + response.views, + &mut cx, + ) + .await?; + this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?; Ok(()) })) } @@ -2226,7 +2220,7 @@ impl Workspace { // RPC handlers async fn handle_follow( - this: ViewHandle, + this: WeakViewHandle, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -2274,7 +2268,7 @@ impl Workspace { } async fn handle_unfollow( - this: ViewHandle, + this: WeakViewHandle, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -2289,7 +2283,7 @@ impl Workspace { } async fn handle_update_followers( - this: ViewHandle, + this: WeakViewHandle, envelope: TypedEnvelope, _: Arc, cx: AsyncAppContext, @@ -2303,7 +2297,7 @@ impl Workspace { } async fn process_leader_update( - this: ViewHandle, + this: &WeakViewHandle, leader_id: PeerId, update: proto::UpdateFollowers, cx: &mut AsyncAppContext, @@ -2363,7 +2357,7 @@ impl Workspace { } async fn add_views_from_leader( - this: ViewHandle, + this: WeakViewHandle, leader_id: PeerId, panes: Vec>, views: Vec, @@ -2691,82 +2685,80 @@ impl Workspace { cx: &mut AppContext, ) { cx.spawn(|mut cx| async move { - if let Some(workspace) = workspace.upgrade(&cx) { - let (project, dock_pane_handle, old_center_pane) = - workspace.read_with(&cx, |workspace, _| { - ( - workspace.project().clone(), - workspace.dock_pane().clone(), - workspace.last_active_center_pane.clone(), - ) - })?; - - serialized_workspace - .dock_pane - .deserialize_to( - &project, - &dock_pane_handle, - serialized_workspace.id, - &workspace, - &mut cx, + let (project, dock_pane_handle, old_center_pane) = + workspace.read_with(&cx, |workspace, _| { + ( + workspace.project().clone(), + workspace.dock_pane().downgrade(), + workspace.last_active_center_pane.clone(), ) - .await?; - - // Traverse the splits tree and add to things - let center_group = serialized_workspace - .center_group - .deserialize(&project, serialized_workspace.id, &workspace, &mut cx) - .await; - - // Remove old panes from workspace panes list - workspace.update(&mut cx, |workspace, cx| { - if let Some((center_group, active_pane)) = center_group { - workspace.remove_panes(workspace.center.root.clone(), cx); - - // Swap workspace center group - workspace.center = PaneGroup::with_root(center_group); - - // Change the focus to the workspace first so that we retrigger focus in on the pane. - cx.focus_self(); - - if let Some(active_pane) = active_pane { - cx.focus(&active_pane); - } else { - cx.focus(workspace.panes.last().unwrap()); - } - } else { - let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade(cx)); - if let Some(old_center_handle) = old_center_handle { - cx.focus(&old_center_handle) - } else { - cx.focus_self() - } - } - - if workspace.left_sidebar().read(cx).is_open() - != serialized_workspace.left_sidebar_open - { - workspace.toggle_sidebar(SidebarSide::Left, cx); - } - - // Note that without after_window, the focus_self() and - // the focus the dock generates start generating alternating - // focus due to the deferred execution each triggering each other - cx.after_window_update(move |workspace, cx| { - Dock::set_dock_position( - workspace, - serialized_workspace.dock_position, - true, - cx, - ); - }); - - cx.notify(); })?; - // Serialize ourself to make sure our timestamps and any pane / item changes are replicated - workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))? - } + serialized_workspace + .dock_pane + .deserialize_to( + &project, + &dock_pane_handle, + serialized_workspace.id, + &workspace, + &mut cx, + ) + .await?; + + // Traverse the splits tree and add to things + let center_group = serialized_workspace + .center_group + .deserialize(&project, serialized_workspace.id, &workspace, &mut cx) + .await; + + // Remove old panes from workspace panes list + workspace.update(&mut cx, |workspace, cx| { + if let Some((center_group, active_pane)) = center_group { + workspace.remove_panes(workspace.center.root.clone(), cx); + + // Swap workspace center group + workspace.center = PaneGroup::with_root(center_group); + + // Change the focus to the workspace first so that we retrigger focus in on the pane. + cx.focus_self(); + + if let Some(active_pane) = active_pane { + cx.focus(&active_pane); + } else { + cx.focus(workspace.panes.last().unwrap()); + } + } else { + let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade(cx)); + if let Some(old_center_handle) = old_center_handle { + cx.focus(&old_center_handle) + } else { + cx.focus_self() + } + } + + if workspace.left_sidebar().read(cx).is_open() + != serialized_workspace.left_sidebar_open + { + workspace.toggle_sidebar(SidebarSide::Left, cx); + } + + // Note that without after_window, the focus_self() and + // the focus the dock generates start generating alternating + // focus due to the deferred execution each triggering each other + cx.after_window_update(move |workspace, cx| { + Dock::set_dock_position( + workspace, + serialized_workspace.dock_position, + true, + cx, + ); + }); + + cx.notify(); + })?; + + // Serialize ourself to make sure our timestamps and any pane / item changes are replicated + workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))?; anyhow::Ok(()) }) .detach_and_log_err(cx); @@ -2778,7 +2770,7 @@ impl Workspace { } } -fn notify_if_database_failed(workspace: &ViewHandle, cx: &mut AsyncAppContext) { +fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { workspace.update(cx, |workspace, cx| { if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) { workspace.show_notification_once(0, cx, |cx| { @@ -2989,7 +2981,7 @@ pub struct WorkspaceCreated(WeakViewHandle); pub fn activate_workspace_for_project( cx: &mut AppContext, predicate: impl Fn(&mut Project, &mut ModelContext) -> bool, -) -> Option> { +) -> Option> { for window_id in cx.window_ids().collect::>() { let handle = cx .update_window(window_id, |cx| { @@ -3004,8 +2996,8 @@ pub fn activate_workspace_for_project( }) .flatten(); - if handle.is_some() { - return handle; + if let Some(handle) = handle { + return Some(handle.downgrade()); } } None @@ -3023,7 +3015,7 @@ pub fn open_paths( cx: &mut AppContext, ) -> Task< Result<( - ViewHandle, + WeakViewHandle, Vec, anyhow::Error>>>, )>, > { @@ -3709,7 +3701,7 @@ mod tests { workspace .update(cx, |workspace, cx| { - Pane::go_back(workspace, Some(pane.clone()), cx) + Pane::go_back(workspace, Some(pane.downgrade()), cx) }) .await .unwrap(); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 8162eb5a09..081938788f 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -674,13 +674,15 @@ async fn handle_cli_connection( let wait = async move { if paths.is_empty() { let (done_tx, done_rx) = oneshot::channel(); - let _subscription = cx.update(|cx| { - cx.observe_release(&workspace, move |_, _| { - let _ = done_tx.send(()); - }) - }); - drop(workspace); - let _ = done_rx.await; + if let Some(workspace) = workspace.upgrade(&cx) { + let _subscription = cx.update(|cx| { + cx.observe_release(&workspace, move |_, _| { + let _ = done_tx.send(()); + }) + }); + drop(workspace); + let _ = done_rx.await; + } } else { let _ = futures::future::try_join_all(item_release_futures).await; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c035ac07dd..4325835cdb 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -374,7 +374,14 @@ pub fn build_window_options( fn restart(_: &Restart, cx: &mut gpui::AppContext) { let mut workspaces = cx .window_ids() - .filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::()) + .filter_map(|window_id| { + Some( + cx.root_view(window_id)? + .clone() + .downcast::()? + .downgrade(), + ) + }) .collect::>(); // 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) { let mut workspaces = cx .window_ids() - .filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::()) + .filter_map(|window_id| { + Some( + cx.root_view(window_id)? + .clone() + .downcast::()? + .downgrade(), + ) + }) .collect::>(); // If multiple windows have unsaved changes, and need a save prompt, @@ -503,49 +517,49 @@ fn open_log_file( workspace .with_local_workspace(&app_state.clone(), cx, move |_, cx| { - cx.spawn_weak(|workspace, mut cx| async move { + cx.spawn(|workspace, mut cx| async move { let (old_log, new_log) = futures::join!( app_state.fs.load(&paths::OLD_LOG), app_state.fs.load(&paths::LOG) ); - if let Some(workspace) = workspace.upgrade(&cx) { - let mut lines = VecDeque::with_capacity(MAX_LINES); - for line in old_log - .iter() - .flat_map(|log| log.lines()) - .chain(new_log.iter().flat_map(|log| log.lines())) - { - if lines.len() == MAX_LINES { - lines.pop_front(); - } - lines.push_back(line); + let mut lines = VecDeque::with_capacity(MAX_LINES); + for line in old_log + .iter() + .flat_map(|log| log.lines()) + .chain(new_log.iter().flat_map(|log| log.lines())) + { + if lines.len() == MAX_LINES { + lines.pop_front(); } - let log = lines - .into_iter() - .flat_map(|line| [line, "\n"]) - .collect::(); - - workspace - .update(&mut cx, |workspace, cx| { - let project = workspace.project().clone(); - let buffer = project - .update(cx, |project, cx| project.create_buffer("", None, cx)) - .expect("creating buffers on a local workspace always succeeds"); - buffer.update(cx, |buffer, cx| buffer.edit([(0..0, log)], None, cx)); - - let buffer = cx.add_model(|cx| { - MultiBuffer::singleton(buffer, cx).with_title("Log".into()) - }); - workspace.add_item( - Box::new(cx.add_view(|cx| { - Editor::for_multibuffer(buffer, Some(project), cx) - })), - cx, - ); - }) - .log_err(); + lines.push_back(line); } + let log = lines + .into_iter() + .flat_map(|line| [line, "\n"]) + .collect::(); + + workspace + .update(&mut cx, |workspace, cx| { + let project = workspace.project().clone(); + let buffer = project + .update(cx, |project, cx| project.create_buffer("", None, cx)) + .expect("creating buffers on a local workspace always succeeds"); + buffer.update(cx, |buffer, cx| buffer.edit([(0..0, log)], None, cx)); + + let buffer = cx.add_model(|cx| { + MultiBuffer::singleton(buffer, cx).with_title("Log".into()) + }); + workspace.add_item( + Box::new( + cx.add_view(|cx| { + Editor::for_multibuffer(buffer, Some(project), cx) + }), + ), + cx, + ); + }) + .log_err(); }) .detach(); }) @@ -558,9 +572,7 @@ fn open_telemetry_log_file( cx: &mut ViewContext, ) { workspace.with_local_workspace(&app_state.clone(), cx, move |_, cx| { - cx.spawn_weak(|workspace, mut cx| async move { - let workspace = workspace.upgrade(&cx)?; - + cx.spawn(|workspace, mut cx| async move { async fn fetch_log_string(app_state: &Arc) -> Option { let path = app_state.client.telemetry_log_file_path()?; app_state.fs.load(&path).await.log_err()