mirror of
https://github.com/zed-industries/zed.git
synced 2024-10-26 16:37:58 +00:00
Merge pull request #2414 from zed-industries/spawn-weak-for-views
Pass a `WeakViewHandle` to `ViewContext::spawn`
This commit is contained in:
commit
a45282eb63
31 changed files with 605 additions and 680 deletions
|
@ -63,20 +63,16 @@ impl ActivityIndicator {
|
|||
let auto_updater = AutoUpdater::get(cx);
|
||||
let this = cx.add_view(|cx: &mut ViewContext<Self>| {
|
||||
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(())
|
||||
})
|
||||
|
|
|
@ -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(())
|
||||
})
|
||||
|
|
|
@ -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<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||
+ Fn(WeakViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||
F: 'static + Future<Output = Result<()>>,
|
||||
{
|
||||
self.add_entity_message_handler::<M, E, _, _>(move |handle, message, client, cx| {
|
||||
|
@ -666,7 +666,7 @@ impl Client {
|
|||
H: 'static
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Fn(ViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||
+ Fn(WeakViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||
F: 'static + Future<Output = Result<M::Response>>,
|
||||
{
|
||||
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<Subscriber> {
|
||||
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<Credentials> {
|
||||
if IMPERSONATE_LOGIN.is_some() {
|
||||
return None;
|
||||
|
|
|
@ -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 {
|
||||
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<AppState>, cx: &mut AppCont
|
|||
workspace
|
||||
},
|
||||
);
|
||||
workspace
|
||||
workspace.downgrade()
|
||||
};
|
||||
|
||||
cx.activate_window(workspace.window_id());
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<Editor>,
|
||||
workspace: ViewHandle<Workspace>,
|
||||
this: &WeakViewHandle<Editor>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
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::<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| {
|
||||
let buffer = editor
|
||||
.buffer()
|
||||
|
@ -6796,6 +6786,7 @@ impl Editor {
|
|||
|
||||
anyhow::Ok(())
|
||||
})??;
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
|
|
@ -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::<HoverState>(
|
||||
vec![hover_popover.symbol_range.clone()],
|
||||
|theme| theme.editor.hover_popover.highlight,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
this.clear_background_highlights::<HoverState>(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::<HoverState>(
|
||||
vec![hover_popover.symbol_range.clone()],
|
||||
|theme| theme.editor.hover_popover.highlight,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
this.clear_background_highlights::<HoverState>(cx);
|
||||
}
|
||||
|
||||
this.hover_state.info_popover = hover_popover;
|
||||
cx.notify();
|
||||
})?;
|
||||
|
||||
this.hover_state.info_popover = hover_popover;
|
||||
cx.notify();
|
||||
})?;
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
}
|
||||
.log_err()
|
||||
|
|
|
@ -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::<Vec<_>>()
|
||||
});
|
||||
|
||||
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<Editor>,
|
||||
this: WeakViewHandle<Editor>,
|
||||
project: ModelHandle<Project>,
|
||||
message: proto::update_view::Editor,
|
||||
cx: &mut AsyncAppContext,
|
||||
|
@ -863,7 +864,9 @@ impl Item for Editor {
|
|||
let buffer = project_item
|
||||
.downcast::<Buffer>()
|
||||
.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);
|
||||
|
|
|
@ -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::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<Settings>().theme.editor.link_definition;
|
||||
this.highlight_text::<LinkGoToDefinitionState>(
|
||||
vec![highlight_range],
|
||||
style,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
hide_link_definition(this, cx);
|
||||
}
|
||||
// Highlight symbol using theme link definition highlight style
|
||||
let style = cx.global::<Settings>().theme.editor.link_definition;
|
||||
this.highlight_text::<LinkGoToDefinitionState>(
|
||||
vec![highlight_range],
|
||||
style,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
hide_link_definition(this, cx);
|
||||
}
|
||||
})?;
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok::<_, anyhow::Error>(())
|
||||
}
|
||||
|
|
|
@ -245,16 +245,14 @@ impl ScrollManager {
|
|||
}
|
||||
|
||||
if cx.default_global::<ScrollbarAutoHide>().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;
|
||||
|
|
|
@ -124,11 +124,10 @@ impl FeedbackEditor {
|
|||
&["Yes, Submit!", "No"],
|
||||
);
|
||||
|
||||
let this = cx.handle();
|
||||
let client = cx.global::<Arc<Client>>().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) {
|
||||
|
|
|
@ -127,18 +127,8 @@ pub trait BorrowAppContext {
|
|||
}
|
||||
|
||||
pub trait BorrowWindowContext {
|
||||
type ReturnValue<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>;
|
||||
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;
|
||||
}
|
||||
|
||||
#[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 GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext);
|
||||
|
||||
|
@ -3262,26 +3230,16 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
|||
|
||||
pub fn spawn_labeled<F, Fut, S>(&mut self, task_label: &'static str, f: F) -> Task<S>
|
||||
where
|
||||
F: FnOnce(ViewHandle<V>, AsyncAppContext) -> Fut,
|
||||
F: FnOnce(WeakViewHandle<V>, AsyncAppContext) -> Fut,
|
||||
Fut: 'static + Future<Output = S>,
|
||||
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<F, Fut, S>(&mut self, f: F) -> Task<S>
|
||||
where
|
||||
F: FnOnce(ViewHandle<V>, AsyncAppContext) -> Fut,
|
||||
Fut: 'static + Future<Output = S>,
|
||||
S: 'static,
|
||||
{
|
||||
let handle = self.handle();
|
||||
self.window_context.spawn(|cx| f(handle, cx))
|
||||
}
|
||||
|
||||
pub fn spawn_weak<F, Fut, S>(&mut self, f: F) -> Task<S>
|
||||
where
|
||||
F: FnOnce(WeakViewHandle<V>, AsyncAppContext) -> Fut,
|
||||
Fut: 'static + Future<Output = S>,
|
||||
|
@ -3340,8 +3298,6 @@ impl<V> BorrowAppContext 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 {
|
||||
BorrowWindowContext::read_with(&*self.window_context, window_id, f)
|
||||
}
|
||||
|
@ -3394,8 +3350,6 @@ impl<V: View> BorrowAppContext 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 {
|
||||
BorrowWindowContext::read_with(&*self.view_context, window_id, f)
|
||||
}
|
||||
|
@ -3732,7 +3686,7 @@ impl<T: View> ViewHandle<T> {
|
|||
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
|
||||
C: BorrowWindowContext,
|
||||
F: FnOnce(&T, &ViewContext<T>) -> S,
|
||||
|
@ -3743,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
|
||||
C: BorrowWindowContext,
|
||||
F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
|
||||
|
@ -4094,15 +4048,31 @@ impl<V: View> WeakViewHandle<V> {
|
|||
cx.read_with(|cx| cx.upgrade_view_handle(self))
|
||||
}
|
||||
|
||||
pub fn read_with<T>(
|
||||
&self,
|
||||
cx: &AsyncAppContext,
|
||||
read: impl FnOnce(&V, &ViewContext<V>) -> T,
|
||||
) -> Result<T> {
|
||||
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<T>(
|
||||
&self,
|
||||
cx: &mut impl BorrowAppContext,
|
||||
cx: &mut AsyncAppContext,
|
||||
update: impl FnOnce(&mut V, &mut ViewContext<V>) -> T,
|
||||
) -> Option<T> {
|
||||
) -> Result<T> {
|
||||
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<T: 'static>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.view_type
|
||||
}
|
||||
|
||||
pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<AnyViewHandle> {
|
||||
cx.read_with(|cx| cx.upgrade_any_view_handle(self))
|
||||
}
|
||||
|
||||
pub fn downcast<T: View>(self) -> Option<WeakViewHandle<T>> {
|
||||
if self.is::<T>() {
|
||||
Some(WeakViewHandle {
|
||||
any_handle: self,
|
||||
view_type: PhantomData,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for AnyWeakViewHandle {
|
||||
|
|
|
@ -390,8 +390,6 @@ impl BorrowAppContext 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 {
|
||||
self.cx
|
||||
.borrow()
|
||||
|
|
|
@ -142,8 +142,6 @@ impl BorrowAppContext 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 {
|
||||
if self.window_id == window_id {
|
||||
f(self)
|
||||
|
|
|
@ -99,14 +99,12 @@ impl<V: View> Tooltip<V> {
|
|||
|
||||
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();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut AppContext) {
|
|||
.await;
|
||||
|
||||
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| {
|
||||
let len = editor.buffer().read(cx).len(cx);
|
||||
editor.change_selections(Some(Autoscroll::center()), cx, |s| {
|
||||
|
|
|
@ -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();
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -238,10 +238,9 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
|
||||
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()
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -163,7 +163,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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();
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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();
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -811,8 +811,6 @@ mod tests {
|
|||
}
|
||||
|
||||
impl BorrowWindowContext for DockTestContext<'_> {
|
||||
type ReturnValue<T> = T;
|
||||
|
||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||
BorrowWindowContext::read_with(self.cx, window_id, f)
|
||||
}
|
||||
|
|
|
@ -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<T: Item> ItemHandle for ViewHandle<T> {
|
|||
},
|
||||
);
|
||||
} 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(),
|
||||
|
|
|
@ -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<ViewHandle<Pane>>,
|
||||
pane: Option<WeakViewHandle<Pane>>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Task<Result<()>> {
|
||||
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<ViewHandle<Pane>>,
|
||||
pane: Option<WeakViewHandle<Pane>>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Task<Result<()>> {
|
||||
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<Result<()>> {
|
||||
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>,
|
||||
pane: WeakViewHandle<Pane>,
|
||||
mode: NavigationMode,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> 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| {
|
||||
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<Project>,
|
||||
pane: &ViewHandle<Pane>,
|
||||
pane: &WeakViewHandle<Pane>,
|
||||
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Vec<PathBuf>>);
|
||||
|
||||
|
@ -97,7 +95,7 @@ impl SerializedPaneGroup {
|
|||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
workspace_id: WorkspaceId,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
workspace: &WeakViewHandle<Workspace>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Option<(Member, Option<ViewHandle<Pane>>)> {
|
||||
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<Project>,
|
||||
pane_handle: &ViewHandle<Pane>,
|
||||
pane_handle: &WeakViewHandle<Pane>,
|
||||
workspace_id: WorkspaceId,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
workspace: &WeakViewHandle<Workspace>,
|
||||
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::<ItemDeserializers>().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 {
|
||||
|
|
|
@ -314,7 +314,7 @@ pub fn init(app_state: Arc<AppState>, 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<usize>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<(
|
||||
ViewHandle<Workspace>,
|
||||
WeakViewHandle<Workspace>,
|
||||
Vec<Option<Result<Box<dyn ItemHandle>, 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<Task<Result<()>>> {
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<Self>,
|
||||
this: WeakViewHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::Follow>,
|
||||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
|
@ -2274,7 +2268,7 @@ impl Workspace {
|
|||
}
|
||||
|
||||
async fn handle_unfollow(
|
||||
this: ViewHandle<Self>,
|
||||
this: WeakViewHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::Unfollow>,
|
||||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
|
@ -2289,7 +2283,7 @@ impl Workspace {
|
|||
}
|
||||
|
||||
async fn handle_update_followers(
|
||||
this: ViewHandle<Self>,
|
||||
this: WeakViewHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::UpdateFollowers>,
|
||||
_: Arc<Client>,
|
||||
cx: AsyncAppContext,
|
||||
|
@ -2303,7 +2297,7 @@ impl Workspace {
|
|||
}
|
||||
|
||||
async fn process_leader_update(
|
||||
this: ViewHandle<Self>,
|
||||
this: &WeakViewHandle<Self>,
|
||||
leader_id: PeerId,
|
||||
update: proto::UpdateFollowers,
|
||||
cx: &mut AsyncAppContext,
|
||||
|
@ -2363,7 +2357,7 @@ impl Workspace {
|
|||
}
|
||||
|
||||
async fn add_views_from_leader(
|
||||
this: ViewHandle<Self>,
|
||||
this: WeakViewHandle<Self>,
|
||||
leader_id: PeerId,
|
||||
panes: Vec<ViewHandle<Pane>>,
|
||||
views: Vec<proto::View>,
|
||||
|
@ -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<Workspace>, cx: &mut AsyncAppContext) {
|
||||
fn notify_if_database_failed(workspace: &WeakViewHandle<Workspace>, 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<Workspace>);
|
|||
pub fn activate_workspace_for_project(
|
||||
cx: &mut AppContext,
|
||||
predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
|
||||
) -> Option<ViewHandle<Workspace>> {
|
||||
) -> Option<WeakViewHandle<Workspace>> {
|
||||
for window_id in cx.window_ids().collect::<Vec<_>>() {
|
||||
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<Workspace>,
|
||||
WeakViewHandle<Workspace>,
|
||||
Vec<Option<Result<Box<dyn ItemHandle>, 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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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::<Workspace>())
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// 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::<Workspace>())
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// 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::<String>();
|
||||
|
||||
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::<String>();
|
||||
|
||||
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>,
|
||||
) {
|
||||
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<AppState>) -> Option<String> {
|
||||
let path = app_state.client.telemetry_log_file_path()?;
|
||||
app_state.fs.load(&path).await.log_err()
|
||||
|
|
Loading…
Reference in a new issue