Move "async move" a few characters to the left in cx.spawn() (#26758)

This is the core change:
https://github.com/zed-industries/zed/pull/26758/files#diff-044302c0d57147af17e68a0009fee3e8dcdfb4f32c27a915e70cfa80e987f765R1052

TODO:
- [x] Use AsyncFn instead of Fn() -> Future in GPUI spawn methods
- [x] Implement it in the whole app
- [x] Implement it in the debugger 
- [x] Glance at the RPC crate, and see if those box future methods can
be switched over. Answer: It can't directly, as you can't make an
AsyncFn* into a trait object. There's ways around that, but they're all
more complex than just keeping the code as is.
- [ ] Fix platform specific code

Release Notes:

- N/A
This commit is contained in:
Mikayla Maki 2025-03-18 19:09:02 -07:00 committed by GitHub
parent 7f2e3fb5bd
commit 1aefa5178b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
256 changed files with 3110 additions and 3200 deletions

View file

@ -63,9 +63,9 @@ impl ActivityIndicator {
let auto_updater = AutoUpdater::get(cx); let auto_updater = AutoUpdater::get(cx);
let this = cx.new(|cx| { let this = cx.new(|cx| {
let mut status_events = languages.language_server_binary_statuses(); let mut status_events = languages.language_server_binary_statuses();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
while let Some((name, status)) = status_events.next().await { while let Some((name, status)) = status_events.next().await {
this.update(&mut cx, |this: &mut ActivityIndicator, cx| { this.update(cx, |this: &mut ActivityIndicator, cx| {
this.statuses.retain(|s| s.name != name); this.statuses.retain(|s| s.name != name);
this.statuses.push(ServerStatus { name, status }); this.statuses.push(ServerStatus { name, status });
cx.notify(); cx.notify();
@ -76,9 +76,9 @@ impl ActivityIndicator {
.detach(); .detach();
let mut status_events = languages.dap_server_binary_statuses(); let mut status_events = languages.dap_server_binary_statuses();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
while let Some((name, status)) = status_events.next().await { while let Some((name, status)) = status_events.next().await {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.statuses.retain(|s| s.name != name); this.statuses.retain(|s| s.name != name);
this.statuses.push(ServerStatus { name, status }); this.statuses.push(ServerStatus { name, status });
cx.notify(); cx.notify();
@ -123,9 +123,9 @@ impl ActivityIndicator {
let project = project.clone(); let project = project.clone();
let error = error.clone(); let error = error.clone();
let server_name = server_name.clone(); let server_name = server_name.clone();
cx.spawn_in(window, |workspace, mut cx| async move { cx.spawn_in(window, async move |workspace, cx| {
let buffer = create_buffer.await?; let buffer = create_buffer.await?;
buffer.update(&mut cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.edit( buffer.edit(
[( [(
0..0, 0..0,
@ -136,7 +136,7 @@ impl ActivityIndicator {
); );
buffer.set_capability(language::Capability::ReadOnly, cx); buffer.set_capability(language::Capability::ReadOnly, cx);
})?; })?;
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
workspace.add_item_to_active_pane( workspace.add_item_to_active_pane(
Box::new(cx.new(|cx| { Box::new(cx.new(|cx| {
Editor::for_buffer(buffer, Some(project.clone()), window, cx) Editor::for_buffer(buffer, Some(project.clone()), window, cx)

View file

@ -34,9 +34,9 @@ impl AskPassDelegate {
password_prompt: impl Fn(String, oneshot::Sender<String>, &mut AsyncApp) + Send + Sync + 'static, password_prompt: impl Fn(String, oneshot::Sender<String>, &mut AsyncApp) + Send + Sync + 'static,
) -> Self { ) -> Self {
let (tx, mut rx) = mpsc::unbounded::<(String, oneshot::Sender<String>)>(); let (tx, mut rx) = mpsc::unbounded::<(String, oneshot::Sender<String>)>();
let task = cx.spawn(|mut cx| async move { let task = cx.spawn(async move |cx: &mut AsyncApp| {
while let Some((prompt, channel)) = rx.next().await { while let Some((prompt, channel)) = rx.next().await {
password_prompt(prompt, channel, &mut cx); password_prompt(prompt, channel, cx);
} }
}); });
Self { tx, _task: task } Self { tx, _task: task }

View file

@ -98,9 +98,9 @@ pub fn init(
AssistantSettings::register(cx); AssistantSettings::register(cx);
SlashCommandSettings::register(cx); SlashCommandSettings::register(cx);
cx.spawn(|mut cx| { cx.spawn({
let client = client.clone(); let client = client.clone();
async move { async move |cx| {
let is_search_slash_command_enabled = cx let is_search_slash_command_enabled = cx
.update(|cx| cx.wait_for_flag::<SearchSlashCommandFeatureFlag>())? .update(|cx| cx.wait_for_flag::<SearchSlashCommandFeatureFlag>())?
.await; .await;
@ -116,7 +116,7 @@ pub fn init(
let semantic_index = SemanticDb::new( let semantic_index = SemanticDb::new(
paths::embeddings_dir().join("semantic-index-db.0.mdb"), paths::embeddings_dir().join("semantic-index-db.0.mdb"),
Arc::new(embedding_provider), Arc::new(embedding_provider),
&mut cx, cx,
) )
.await?; .await?;

View file

@ -98,16 +98,16 @@ impl AssistantPanel {
prompt_builder: Arc<PromptBuilder>, prompt_builder: Arc<PromptBuilder>,
cx: AsyncWindowContext, cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let slash_commands = Arc::new(SlashCommandWorkingSet::default()); let slash_commands = Arc::new(SlashCommandWorkingSet::default());
let context_store = workspace let context_store = workspace
.update(&mut cx, |workspace, cx| { .update(cx, |workspace, cx| {
let project = workspace.project().clone(); let project = workspace.project().clone();
ContextStore::new(project, prompt_builder.clone(), slash_commands, cx) ContextStore::new(project, prompt_builder.clone(), slash_commands, cx)
})? })?
.await?; .await?;
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
// TODO: deserialize state. // TODO: deserialize state.
cx.new(|cx| Self::new(workspace, context_store, window, cx)) cx.new(|cx| Self::new(workspace, context_store, window, cx))
}) })
@ -357,9 +357,9 @@ impl AssistantPanel {
) -> Task<()> { ) -> Task<()> {
let mut status_rx = client.status(); let mut status_rx = client.status();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
while let Some(status) = status_rx.next().await { while let Some(status) = status_rx.next().await {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if this.client_status.is_none() if this.client_status.is_none()
|| this || this
.client_status .client_status
@ -371,7 +371,7 @@ impl AssistantPanel {
}) })
.log_err(); .log_err();
} }
this.update(&mut cx, |this, _cx| this.watch_client_status = None) this.update(cx, |this, _cx| this.watch_client_status = None)
.log_err(); .log_err();
}) })
} }
@ -576,11 +576,11 @@ impl AssistantPanel {
if self.authenticate_provider_task.is_none() { if self.authenticate_provider_task.is_none() {
self.authenticate_provider_task = Some(( self.authenticate_provider_task = Some((
provider.id(), provider.id(),
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
if let Some(future) = load_credentials { if let Some(future) = load_credentials {
let _ = future.await; let _ = future.await;
} }
this.update(&mut cx, |this, _cx| { this.update(cx, |this, _cx| {
this.authenticate_provider_task = None; this.authenticate_provider_task = None;
}) })
.log_err(); .log_err();
@ -641,9 +641,9 @@ impl AssistantPanel {
} }
} else { } else {
let assistant_panel = assistant_panel.downgrade(); let assistant_panel = assistant_panel.downgrade();
cx.spawn_in(window, |workspace, mut cx| async move { cx.spawn_in(window, async move |workspace, cx| {
let Some(task) = let Some(task) =
assistant_panel.update(&mut cx, |assistant, cx| assistant.authenticate(cx))? assistant_panel.update(cx, |assistant, cx| assistant.authenticate(cx))?
else { else {
let answer = cx let answer = cx
.prompt( .prompt(
@ -665,7 +665,7 @@ impl AssistantPanel {
return Ok(()); return Ok(());
}; };
task.await?; task.await?;
if assistant_panel.update(&mut cx, |panel, cx| panel.is_authenticated(cx))? { if assistant_panel.update(cx, |panel, cx| panel.is_authenticated(cx))? {
cx.update(|window, cx| match inline_assist_target { cx.update(|window, cx| match inline_assist_target {
InlineAssistTarget::Editor(active_editor, include_context) => { InlineAssistTarget::Editor(active_editor, include_context) => {
let assistant_panel = if include_context { let assistant_panel = if include_context {
@ -698,7 +698,7 @@ impl AssistantPanel {
} }
})? })?
} else { } else {
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx) workspace.focus_panel::<AssistantPanel>(window, cx)
})?; })?;
} }
@ -791,10 +791,10 @@ impl AssistantPanel {
.context_store .context_store
.update(cx, |store, cx| store.create_remote_context(cx)); .update(cx, |store, cx| store.create_remote_context(cx));
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let context = task.await?; let context = task.await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
let workspace = this.workspace.clone(); let workspace = this.workspace.clone();
let project = this.project.clone(); let project = this.project.clone();
let lsp_adapter_delegate = let lsp_adapter_delegate =
@ -847,9 +847,9 @@ impl AssistantPanel {
self.show_context(editor.clone(), window, cx); self.show_context(editor.clone(), window, cx);
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
cx.spawn_in(window, move |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
workspace workspace
.update_in(&mut cx, |workspace, window, cx| { .update_in(cx, |workspace, window, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx); workspace.focus_panel::<AssistantPanel>(window, cx);
}) })
.ok(); .ok();
@ -1069,8 +1069,8 @@ impl AssistantPanel {
.filter(|editor| editor.read(cx).context().read(cx).path() == Some(&path)) .filter(|editor| editor.read(cx).context().read(cx).path() == Some(&path))
}); });
if let Some(existing_context) = existing_context { if let Some(existing_context) = existing_context {
return cx.spawn_in(window, |this, mut cx| async move { return cx.spawn_in(window, async move |this, cx| {
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.show_context(existing_context, window, cx) this.show_context(existing_context, window, cx)
}) })
}); });
@ -1085,9 +1085,9 @@ impl AssistantPanel {
let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten(); let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let context = context.await?; let context = context.await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
let editor = cx.new(|cx| { let editor = cx.new(|cx| {
ContextEditor::for_context( ContextEditor::for_context(
context, context,
@ -1117,8 +1117,8 @@ impl AssistantPanel {
.filter(|editor| *editor.read(cx).context().read(cx).id() == id) .filter(|editor| *editor.read(cx).context().read(cx).id() == id)
}); });
if let Some(existing_context) = existing_context { if let Some(existing_context) = existing_context {
return cx.spawn_in(window, |this, mut cx| async move { return cx.spawn_in(window, async move |this, cx| {
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.show_context(existing_context.clone(), window, cx) this.show_context(existing_context.clone(), window, cx)
})?; })?;
Ok(existing_context) Ok(existing_context)
@ -1134,9 +1134,9 @@ impl AssistantPanel {
.log_err() .log_err()
.flatten(); .flatten();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let context = context.await?; let context = context.await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
let editor = cx.new(|cx| { let editor = cx.new(|cx| {
ContextEditor::for_context( ContextEditor::for_context(
context, context,

View file

@ -1311,9 +1311,9 @@ impl EditorInlineAssists {
assist_ids: Vec::new(), assist_ids: Vec::new(),
scroll_lock: None, scroll_lock: None,
highlight_updates: highlight_updates_tx, highlight_updates: highlight_updates_tx,
_update_highlights: cx.spawn(|cx| { _update_highlights: cx.spawn({
let editor = editor.downgrade(); let editor = editor.downgrade();
async move { async move |cx| {
while let Ok(()) = highlight_updates_rx.changed().await { while let Ok(()) = highlight_updates_rx.changed().await {
let editor = editor.upgrade().context("editor was dropped")?; let editor = editor.upgrade().context("editor was dropped")?;
cx.update_global(|assistant: &mut InlineAssistant, cx| { cx.update_global(|assistant: &mut InlineAssistant, cx| {
@ -1850,7 +1850,7 @@ impl PromptEditor {
fn count_tokens(&mut self, cx: &mut Context<Self>) { fn count_tokens(&mut self, cx: &mut Context<Self>) {
let assist_id = self.id; let assist_id = self.id;
self.pending_token_count = cx.spawn(|this, mut cx| async move { self.pending_token_count = cx.spawn(async move |this, cx| {
cx.background_executor().timer(Duration::from_secs(1)).await; cx.background_executor().timer(Duration::from_secs(1)).await;
let token_count = cx let token_count = cx
.update_global(|inline_assistant: &mut InlineAssistant, cx| { .update_global(|inline_assistant: &mut InlineAssistant, cx| {
@ -1862,7 +1862,7 @@ impl PromptEditor {
})?? })??
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.token_counts = Some(token_count); this.token_counts = Some(token_count);
cx.notify(); cx.notify();
}) })
@ -2882,7 +2882,7 @@ impl CodegenAlternative {
let request = self.build_request(user_prompt, assistant_panel_context, cx)?; let request = self.build_request(user_prompt, assistant_panel_context, cx)?;
self.request = Some(request.clone()); self.request = Some(request.clone());
cx.spawn(|_, cx| async move { model.stream_completion_text(request, &cx).await }) cx.spawn(async move |_, cx| model.stream_completion_text(request, &cx).await)
.boxed_local() .boxed_local()
}; };
self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx); self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx);
@ -2999,213 +2999,207 @@ impl CodegenAlternative {
let completion = Arc::new(Mutex::new(String::new())); let completion = Arc::new(Mutex::new(String::new()));
let completion_clone = completion.clone(); let completion_clone = completion.clone();
self.generation = cx.spawn(|codegen, mut cx| { self.generation = cx.spawn(async move |codegen, cx| {
async move { let stream = stream.await;
let stream = stream.await; let message_id = stream
let message_id = stream .as_ref()
.as_ref() .ok()
.ok() .and_then(|stream| stream.message_id.clone());
.and_then(|stream| stream.message_id.clone()); let generate = async {
let generate = async { let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
let (mut diff_tx, mut diff_rx) = mpsc::channel(1); let executor = cx.background_executor().clone();
let executor = cx.background_executor().clone(); let message_id = message_id.clone();
let message_id = message_id.clone(); let line_based_stream_diff: Task<anyhow::Result<()>> =
let line_based_stream_diff: Task<anyhow::Result<()>> = cx.background_spawn(async move {
cx.background_spawn(async move { let mut response_latency = None;
let mut response_latency = None; let request_start = Instant::now();
let request_start = Instant::now(); let diff = async {
let diff = async { let chunks = StripInvalidSpans::new(stream?.stream);
let chunks = StripInvalidSpans::new(stream?.stream); futures::pin_mut!(chunks);
futures::pin_mut!(chunks); let mut diff = StreamingDiff::new(selected_text.to_string());
let mut diff = StreamingDiff::new(selected_text.to_string()); let mut line_diff = LineDiff::default();
let mut line_diff = LineDiff::default();
let mut new_text = String::new(); let mut new_text = String::new();
let mut base_indent = None; let mut base_indent = None;
let mut line_indent = None; let mut line_indent = None;
let mut first_line = true; let mut first_line = true;
while let Some(chunk) = chunks.next().await { while let Some(chunk) = chunks.next().await {
if response_latency.is_none() { if response_latency.is_none() {
response_latency = Some(request_start.elapsed()); response_latency = Some(request_start.elapsed());
} }
let chunk = chunk?; let chunk = chunk?;
completion_clone.lock().push_str(&chunk); completion_clone.lock().push_str(&chunk);
let mut lines = chunk.split('\n').peekable(); let mut lines = chunk.split('\n').peekable();
while let Some(line) = lines.next() { while let Some(line) = lines.next() {
new_text.push_str(line); new_text.push_str(line);
if line_indent.is_none() { if line_indent.is_none() {
if let Some(non_whitespace_ch_ix) = if let Some(non_whitespace_ch_ix) =
new_text.find(|ch: char| !ch.is_whitespace()) new_text.find(|ch: char| !ch.is_whitespace())
{ {
line_indent = Some(non_whitespace_ch_ix); line_indent = Some(non_whitespace_ch_ix);
base_indent = base_indent.or(line_indent); base_indent = base_indent.or(line_indent);
let line_indent = line_indent.unwrap(); let line_indent = line_indent.unwrap();
let base_indent = base_indent.unwrap(); let base_indent = base_indent.unwrap();
let indent_delta = let indent_delta =
line_indent as i32 - base_indent as i32; line_indent as i32 - base_indent as i32;
let mut corrected_indent_len = cmp::max( let mut corrected_indent_len = cmp::max(
0, 0,
suggested_line_indent.len as i32 + indent_delta, suggested_line_indent.len as i32 + indent_delta,
) )
as usize; as usize;
if first_line { if first_line {
corrected_indent_len = corrected_indent_len corrected_indent_len = corrected_indent_len
.saturating_sub( .saturating_sub(
selection_start.column as usize, selection_start.column as usize,
); );
}
let indent_char = suggested_line_indent.char();
let mut indent_buffer = [0; 4];
let indent_str =
indent_char.encode_utf8(&mut indent_buffer);
new_text.replace_range(
..line_indent,
&indent_str.repeat(corrected_indent_len),
);
} }
}
if line_indent.is_some() { let indent_char = suggested_line_indent.char();
let char_ops = diff.push_new(&new_text); let mut indent_buffer = [0; 4];
line_diff let indent_str =
.push_char_operations(&char_ops, &selected_text); indent_char.encode_utf8(&mut indent_buffer);
diff_tx new_text.replace_range(
.send((char_ops, line_diff.line_operations())) ..line_indent,
.await?; &indent_str.repeat(corrected_indent_len),
);
}
}
if line_indent.is_some() {
let char_ops = diff.push_new(&new_text);
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
new_text.clear();
}
if lines.peek().is_some() {
let char_ops = diff.push_new("\n");
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear(); new_text.clear();
} }
line_indent = None;
if lines.peek().is_some() { first_line = false;
let char_ops = diff.push_new("\n");
line_diff
.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear();
}
line_indent = None;
first_line = false;
}
} }
} }
let mut char_ops = diff.push_new(&new_text);
char_ops.extend(diff.finish());
line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
anyhow::Ok(())
};
let result = diff.await;
let error_message =
result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
result?;
Ok(())
});
while let Some((char_ops, line_ops)) = diff_rx.next().await {
codegen.update(&mut cx, |codegen, cx| {
codegen.last_equal_ranges.clear();
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
} }
codegen.edits.extend(edits);
codegen.line_operations = line_ops;
codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify(); let mut char_ops = diff.push_new(&new_text);
})?; char_ops.extend(diff.finish());
} line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer. anyhow::Ok(())
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff. };
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(&mut cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) =
join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(()) let result = diff.await;
};
let result = generate.await; let error_message = result.as_ref().err().map(|error| error.to_string());
let elapsed_time = start_time.elapsed().as_secs_f64(); report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
codegen result?;
.update(&mut cx, |this, cx| { Ok(())
this.message_id = message_id; });
this.last_equal_ranges.clear();
if let Err(error) = result { while let Some((char_ops, line_ops)) = diff_rx.next().await {
this.status = CodegenStatus::Error(error); codegen.update(cx, |codegen, cx| {
} else { codegen.last_equal_ranges.clear();
this.status = CodegenStatus::Done;
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
} }
this.elapsed_time = Some(elapsed_time); codegen.edits.extend(edits);
this.completion = Some(completion.lock().clone()); codegen.line_operations = line_ops;
cx.emit(CodegenEvent::Finished); codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify(); cx.notify();
}) })?;
.ok(); }
}
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer.
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff.
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) = join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(())
};
let result = generate.await;
let elapsed_time = start_time.elapsed().as_secs_f64();
codegen
.update(cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());
cx.emit(CodegenEvent::Finished);
cx.notify();
})
.ok();
}); });
cx.notify(); cx.notify();
} }
@ -3323,7 +3317,7 @@ impl CodegenAlternative {
let new_snapshot = self.buffer.read(cx).snapshot(cx); let new_snapshot = self.buffer.read(cx).snapshot(cx);
let new_range = self.range.to_point(&new_snapshot); let new_range = self.range.to_point(&new_snapshot);
cx.spawn(|codegen, mut cx| async move { cx.spawn(async move |codegen, cx| {
let (deleted_row_ranges, inserted_row_ranges) = cx let (deleted_row_ranges, inserted_row_ranges) = cx
.background_spawn(async move { .background_spawn(async move {
let old_text = old_snapshot let old_text = old_snapshot
@ -3373,7 +3367,7 @@ impl CodegenAlternative {
.await; .await;
codegen codegen
.update(&mut cx, |codegen, cx| { .update(cx, |codegen, cx| {
codegen.diff.deleted_row_ranges = deleted_row_ranges; codegen.diff.deleted_row_ranges = deleted_row_ranges;
codegen.diff.inserted_row_ranges = inserted_row_ranges; codegen.diff.inserted_row_ranges = inserted_row_ranges;
cx.notify(); cx.notify();
@ -3587,10 +3581,10 @@ impl CodeActionProvider for AssistantCodeActionProvider {
) -> Task<Result<ProjectTransaction>> { ) -> Task<Result<ProjectTransaction>> {
let editor = self.editor.clone(); let editor = self.editor.clone();
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let editor = editor.upgrade().context("editor was released")?; let editor = editor.upgrade().context("editor was released")?;
let range = editor let range = editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
editor.buffer().update(cx, |multibuffer, cx| { editor.buffer().update(cx, |multibuffer, cx| {
let buffer = buffer.read(cx); let buffer = buffer.read(cx);
let multibuffer_snapshot = multibuffer.read(cx); let multibuffer_snapshot = multibuffer.read(cx);
@ -3625,7 +3619,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
}) })
})? })?
.context("invalid range")?; .context("invalid range")?;
let assistant_panel = workspace.update(&mut cx, |workspace, cx| { let assistant_panel = workspace.update(cx, |workspace, cx| {
workspace workspace
.panel::<AssistantPanel>(cx) .panel::<AssistantPanel>(cx)
.context("assistant panel was released") .context("assistant panel was released")

View file

@ -825,7 +825,7 @@ impl PromptEditor {
let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else { let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
return; return;
}; };
self.pending_token_count = cx.spawn(|this, mut cx| async move { self.pending_token_count = cx.spawn(async move |this, cx| {
cx.background_executor().timer(Duration::from_secs(1)).await; cx.background_executor().timer(Duration::from_secs(1)).await;
let request = let request =
cx.update_global(|inline_assistant: &mut TerminalInlineAssistant, cx| { cx.update_global(|inline_assistant: &mut TerminalInlineAssistant, cx| {
@ -833,7 +833,7 @@ impl PromptEditor {
})??; })??;
let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?; let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.token_count = Some(token_count); this.token_count = Some(token_count);
cx.notify(); cx.notify();
}) })
@ -1140,7 +1140,7 @@ impl Codegen {
let telemetry = self.telemetry.clone(); let telemetry = self.telemetry.clone();
self.status = CodegenStatus::Pending; self.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone())); self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
self.generation = cx.spawn(|this, mut cx| async move { self.generation = cx.spawn(async move |this, cx| {
let model_telemetry_id = model.telemetry_id(); let model_telemetry_id = model.telemetry_id();
let model_provider_id = model.provider_id(); let model_provider_id = model.provider_id();
let response = model.stream_completion_text(prompt, &cx).await; let response = model.stream_completion_text(prompt, &cx).await;
@ -1197,12 +1197,12 @@ impl Codegen {
} }
}); });
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.message_id = message_id; this.message_id = message_id;
})?; })?;
while let Some(hunk) = hunks_rx.next().await { while let Some(hunk) = hunks_rx.next().await {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Some(transaction) = &mut this.transaction { if let Some(transaction) = &mut this.transaction {
transaction.push(hunk, cx); transaction.push(hunk, cx);
cx.notify(); cx.notify();
@ -1216,7 +1216,7 @@ impl Codegen {
let result = generate.await; let result = generate.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Err(error) = result { if let Err(error) = result {
this.status = CodegenStatus::Error(error); this.status = CodegenStatus::Error(error);
} else { } else {

View file

@ -372,10 +372,10 @@ impl ActiveThread {
cx, cx,
); );
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let updated_context_ids = refresh_task.await; let updated_context_ids = refresh_task.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.context_store.read_with(cx, |context_store, cx| { this.context_store.read_with(cx, |context_store, cx| {
context_store context_store
.context() .context()
@ -394,10 +394,10 @@ impl ActiveThread {
let model_registry = LanguageModelRegistry::read_global(cx); let model_registry = LanguageModelRegistry::read_global(cx);
if let Some(model) = model_registry.active_model() { if let Some(model) = model_registry.active_model() {
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let updated_context = context_update_task.await?; let updated_context = context_update_task.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.thread.update(cx, |thread, cx| { this.thread.update(cx, |thread, cx| {
thread.attach_tool_results(updated_context, cx); thread.attach_tool_results(updated_context, cx);
if !canceled { if !canceled {
@ -418,9 +418,9 @@ impl ActiveThread {
/// Only one task to save the thread will be in flight at a time. /// Only one task to save the thread will be in flight at a time.
fn save_thread(&mut self, cx: &mut Context<Self>) { fn save_thread(&mut self, cx: &mut Context<Self>) {
let thread = self.thread.clone(); let thread = self.thread.clone();
self.save_thread_task = Some(cx.spawn(|this, mut cx| async move { self.save_thread_task = Some(cx.spawn(async move |this, cx| {
let task = this let task = this
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.thread_store this.thread_store
.update(cx, |thread_store, cx| thread_store.save_thread(&thread, cx)) .update(cx, |thread_store, cx| thread_store.save_thread(&thread, cx))
}) })

View file

@ -110,16 +110,16 @@ impl AssistantPanel {
prompt_builder: Arc<PromptBuilder>, prompt_builder: Arc<PromptBuilder>,
cx: AsyncWindowContext, cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let tools = Arc::new(ToolWorkingSet::default()); let tools = Arc::new(ToolWorkingSet::default());
let thread_store = workspace.update(&mut cx, |workspace, cx| { let thread_store = workspace.update(cx, |workspace, cx| {
let project = workspace.project().clone(); let project = workspace.project().clone();
ThreadStore::new(project, tools.clone(), prompt_builder.clone(), cx) ThreadStore::new(project, tools.clone(), prompt_builder.clone(), cx)
})??; })??;
let slash_commands = Arc::new(SlashCommandWorkingSet::default()); let slash_commands = Arc::new(SlashCommandWorkingSet::default());
let context_store = workspace let context_store = workspace
.update(&mut cx, |workspace, cx| { .update(cx, |workspace, cx| {
let project = workspace.project().clone(); let project = workspace.project().clone();
assistant_context_editor::ContextStore::new( assistant_context_editor::ContextStore::new(
project, project,
@ -130,7 +130,7 @@ impl AssistantPanel {
})? })?
.await?; .await?;
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
cx.new(|cx| Self::new(workspace, thread_store, context_store, window, cx)) cx.new(|cx| Self::new(workspace, thread_store, context_store, window, cx))
}) })
}) })
@ -344,9 +344,9 @@ impl AssistantPanel {
let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten(); let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let context = context.await?; let context = context.await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
let editor = cx.new(|cx| { let editor = cx.new(|cx| {
ContextEditor::for_context( ContextEditor::for_context(
context, context,
@ -377,9 +377,9 @@ impl AssistantPanel {
.thread_store .thread_store
.update(cx, |this, cx| this.open_thread(thread_id, cx)); .update(cx, |this, cx| this.open_thread(thread_id, cx));
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let thread = open_thread_task.await?; let thread = open_thread_task.await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.active_view = ActiveView::Thread; this.active_view = ActiveView::Thread;
let message_editor_context_store = let message_editor_context_store =
cx.new(|_cx| crate::context_store::ContextStore::new(this.workspace.clone())); cx.new(|_cx| crate::context_store::ContextStore::new(this.workspace.clone()));
@ -450,10 +450,10 @@ impl AssistantPanel {
.languages .languages
.language_for_name("Markdown"); .language_for_name("Markdown");
let thread = self.active_thread(cx); let thread = self.active_thread(cx);
cx.spawn_in(window, |_this, mut cx| async move { cx.spawn_in(window, async move |_this, cx| {
let markdown_language = markdown_language_task.await?; let markdown_language = markdown_language_task.await?;
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
let thread = thread.read(cx); let thread = thread.read(cx);
let markdown = thread.to_markdown()?; let markdown = thread.to_markdown()?;
let thread_summary = thread let thread_summary = thread

View file

@ -367,7 +367,7 @@ impl CodegenAlternative {
let request = self.build_request(user_prompt, cx)?; let request = self.build_request(user_prompt, cx)?;
self.request = Some(request.clone()); self.request = Some(request.clone());
cx.spawn(|_, cx| async move { model.stream_completion_text(request, &cx).await }) cx.spawn(async move |_, cx| model.stream_completion_text(request, &cx).await)
.boxed_local() .boxed_local()
}; };
self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx); self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx);
@ -480,213 +480,207 @@ impl CodegenAlternative {
let completion = Arc::new(Mutex::new(String::new())); let completion = Arc::new(Mutex::new(String::new()));
let completion_clone = completion.clone(); let completion_clone = completion.clone();
self.generation = cx.spawn(|codegen, mut cx| { self.generation = cx.spawn(async move |codegen, cx| {
async move { let stream = stream.await;
let stream = stream.await; let message_id = stream
let message_id = stream .as_ref()
.as_ref() .ok()
.ok() .and_then(|stream| stream.message_id.clone());
.and_then(|stream| stream.message_id.clone()); let generate = async {
let generate = async { let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
let (mut diff_tx, mut diff_rx) = mpsc::channel(1); let executor = cx.background_executor().clone();
let executor = cx.background_executor().clone(); let message_id = message_id.clone();
let message_id = message_id.clone(); let line_based_stream_diff: Task<anyhow::Result<()>> =
let line_based_stream_diff: Task<anyhow::Result<()>> = cx.background_spawn(async move {
cx.background_spawn(async move { let mut response_latency = None;
let mut response_latency = None; let request_start = Instant::now();
let request_start = Instant::now(); let diff = async {
let diff = async { let chunks = StripInvalidSpans::new(stream?.stream);
let chunks = StripInvalidSpans::new(stream?.stream); futures::pin_mut!(chunks);
futures::pin_mut!(chunks); let mut diff = StreamingDiff::new(selected_text.to_string());
let mut diff = StreamingDiff::new(selected_text.to_string()); let mut line_diff = LineDiff::default();
let mut line_diff = LineDiff::default();
let mut new_text = String::new(); let mut new_text = String::new();
let mut base_indent = None; let mut base_indent = None;
let mut line_indent = None; let mut line_indent = None;
let mut first_line = true; let mut first_line = true;
while let Some(chunk) = chunks.next().await { while let Some(chunk) = chunks.next().await {
if response_latency.is_none() { if response_latency.is_none() {
response_latency = Some(request_start.elapsed()); response_latency = Some(request_start.elapsed());
} }
let chunk = chunk?; let chunk = chunk?;
completion_clone.lock().push_str(&chunk); completion_clone.lock().push_str(&chunk);
let mut lines = chunk.split('\n').peekable(); let mut lines = chunk.split('\n').peekable();
while let Some(line) = lines.next() { while let Some(line) = lines.next() {
new_text.push_str(line); new_text.push_str(line);
if line_indent.is_none() { if line_indent.is_none() {
if let Some(non_whitespace_ch_ix) = if let Some(non_whitespace_ch_ix) =
new_text.find(|ch: char| !ch.is_whitespace()) new_text.find(|ch: char| !ch.is_whitespace())
{ {
line_indent = Some(non_whitespace_ch_ix); line_indent = Some(non_whitespace_ch_ix);
base_indent = base_indent.or(line_indent); base_indent = base_indent.or(line_indent);
let line_indent = line_indent.unwrap(); let line_indent = line_indent.unwrap();
let base_indent = base_indent.unwrap(); let base_indent = base_indent.unwrap();
let indent_delta = let indent_delta =
line_indent as i32 - base_indent as i32; line_indent as i32 - base_indent as i32;
let mut corrected_indent_len = cmp::max( let mut corrected_indent_len = cmp::max(
0, 0,
suggested_line_indent.len as i32 + indent_delta, suggested_line_indent.len as i32 + indent_delta,
) )
as usize; as usize;
if first_line { if first_line {
corrected_indent_len = corrected_indent_len corrected_indent_len = corrected_indent_len
.saturating_sub( .saturating_sub(
selection_start.column as usize, selection_start.column as usize,
); );
}
let indent_char = suggested_line_indent.char();
let mut indent_buffer = [0; 4];
let indent_str =
indent_char.encode_utf8(&mut indent_buffer);
new_text.replace_range(
..line_indent,
&indent_str.repeat(corrected_indent_len),
);
} }
}
if line_indent.is_some() { let indent_char = suggested_line_indent.char();
let char_ops = diff.push_new(&new_text); let mut indent_buffer = [0; 4];
line_diff let indent_str =
.push_char_operations(&char_ops, &selected_text); indent_char.encode_utf8(&mut indent_buffer);
diff_tx new_text.replace_range(
.send((char_ops, line_diff.line_operations())) ..line_indent,
.await?; &indent_str.repeat(corrected_indent_len),
);
}
}
if line_indent.is_some() {
let char_ops = diff.push_new(&new_text);
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
new_text.clear();
}
if lines.peek().is_some() {
let char_ops = diff.push_new("\n");
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear(); new_text.clear();
} }
line_indent = None;
if lines.peek().is_some() { first_line = false;
let char_ops = diff.push_new("\n");
line_diff
.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear();
}
line_indent = None;
first_line = false;
}
} }
} }
let mut char_ops = diff.push_new(&new_text);
char_ops.extend(diff.finish());
line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
anyhow::Ok(())
};
let result = diff.await;
let error_message =
result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
result?;
Ok(())
});
while let Some((char_ops, line_ops)) = diff_rx.next().await {
codegen.update(&mut cx, |codegen, cx| {
codegen.last_equal_ranges.clear();
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
} }
codegen.edits.extend(edits);
codegen.line_operations = line_ops;
codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify(); let mut char_ops = diff.push_new(&new_text);
})?; char_ops.extend(diff.finish());
} line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer. anyhow::Ok(())
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff. };
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(&mut cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) =
join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(()) let result = diff.await;
};
let result = generate.await; let error_message = result.as_ref().err().map(|error| error.to_string());
let elapsed_time = start_time.elapsed().as_secs_f64(); report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
codegen result?;
.update(&mut cx, |this, cx| { Ok(())
this.message_id = message_id; });
this.last_equal_ranges.clear();
if let Err(error) = result { while let Some((char_ops, line_ops)) = diff_rx.next().await {
this.status = CodegenStatus::Error(error); codegen.update(cx, |codegen, cx| {
} else { codegen.last_equal_ranges.clear();
this.status = CodegenStatus::Done;
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
} }
this.elapsed_time = Some(elapsed_time); codegen.edits.extend(edits);
this.completion = Some(completion.lock().clone()); codegen.line_operations = line_ops;
cx.emit(CodegenEvent::Finished); codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify(); cx.notify();
}) })?;
.ok(); }
}
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer.
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff.
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) = join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(())
};
let result = generate.await;
let elapsed_time = start_time.elapsed().as_secs_f64();
codegen
.update(cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());
cx.emit(CodegenEvent::Finished);
cx.notify();
})
.ok();
}); });
cx.notify(); cx.notify();
} }
@ -804,7 +798,7 @@ impl CodegenAlternative {
let new_snapshot = self.buffer.read(cx).snapshot(cx); let new_snapshot = self.buffer.read(cx).snapshot(cx);
let new_range = self.range.to_point(&new_snapshot); let new_range = self.range.to_point(&new_snapshot);
cx.spawn(|codegen, mut cx| async move { cx.spawn(async move |codegen, cx| {
let (deleted_row_ranges, inserted_row_ranges) = cx let (deleted_row_ranges, inserted_row_ranges) = cx
.background_spawn(async move { .background_spawn(async move {
let old_text = old_snapshot let old_text = old_snapshot
@ -854,7 +848,7 @@ impl CodegenAlternative {
.await; .await;
codegen codegen
.update(&mut cx, |codegen, cx| { .update(cx, |codegen, cx| {
codegen.diff.deleted_row_ranges = deleted_row_ranges; codegen.diff.deleted_row_ranges = deleted_row_ranges;
codegen.diff.inserted_row_ranges = inserted_row_ranges; codegen.diff.inserted_row_ranges = inserted_row_ranges;
cx.notify(); cx.notify();

View file

@ -281,10 +281,8 @@ impl ContextPicker {
context_store.add_file_from_path(project_path.clone(), cx) context_store.add_file_from_path(project_path.clone(), cx)
}); });
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| task.await.notify_async_err(cx))
task.await.notify_async_err(&mut cx) .detach();
})
.detach();
cx.notify(); cx.notify();
} }
@ -307,13 +305,13 @@ impl ContextPicker {
}; };
let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&thread.id, cx)); let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&thread.id, cx));
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let thread = open_thread_task.await?; let thread = open_thread_task.await?;
context_store.update(&mut cx, |context_store, cx| { context_store.update(cx, |context_store, cx| {
context_store.add_thread(thread, cx); context_store.add_thread(thread, cx);
})?; })?;
this.update(&mut cx, |_this, cx| cx.notify()) this.update(cx, |_this, cx| cx.notify())
}) })
} }

View file

@ -206,12 +206,12 @@ impl PickerDelegate for FetchContextPickerDelegate {
let http_client = workspace.read(cx).client().http_client().clone(); let http_client = workspace.read(cx).client().http_client().clone();
let url = self.url.clone(); let url = self.url.clone();
let confirm_behavior = self.confirm_behavior; let confirm_behavior = self.confirm_behavior;
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let text = cx let text = cx
.background_spawn(Self::build_message(http_client, url.clone())) .background_spawn(Self::build_message(http_client, url.clone()))
.await?; .await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.delegate this.delegate
.context_store .context_store
.update(cx, |context_store, _cx| { .update(cx, |context_store, _cx| {

View file

@ -206,11 +206,11 @@ impl PickerDelegate for FileContextPickerDelegate {
let search_task = self.search(query, Arc::<AtomicBool>::default(), &workspace, cx); let search_task = self.search(query, Arc::<AtomicBool>::default(), &workspace, cx);
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
// TODO: This should be probably be run in the background. // TODO: This should be probably be run in the background.
let paths = search_task.await; let paths = search_task.await;
this.update(&mut cx, |this, _cx| { this.update(cx, |this, _cx| {
this.delegate.matches = paths; this.delegate.matches = paths;
}) })
.log_err(); .log_err();
@ -345,10 +345,10 @@ impl PickerDelegate for FileContextPickerDelegate {
}; };
let confirm_behavior = self.confirm_behavior; let confirm_behavior = self.confirm_behavior;
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
match task.await.notify_async_err(&mut cx) { match task.await.notify_async_err(cx) {
None => anyhow::Ok(()), None => anyhow::Ok(()),
Some(()) => this.update_in(&mut cx, |this, window, cx| match confirm_behavior { Some(()) => this.update_in(cx, |this, window, cx| match confirm_behavior {
ConfirmBehavior::KeepOpen => {} ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(window, cx), ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
}), }),

View file

@ -149,9 +149,9 @@ impl PickerDelegate for ThreadContextPickerDelegate {
} }
}); });
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let matches = search_task.await; let matches = search_task.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.delegate.matches = matches; this.delegate.matches = matches;
this.delegate.selected_index = 0; this.delegate.selected_index = 0;
cx.notify(); cx.notify();
@ -171,9 +171,9 @@ impl PickerDelegate for ThreadContextPickerDelegate {
let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&entry.id, cx)); let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&entry.id, cx));
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let thread = open_thread_task.await?; let thread = open_thread_task.await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.delegate this.delegate
.context_store .context_store
.update(cx, |context_store, cx| context_store.add_thread(thread, cx)) .update(cx, |context_store, cx| context_store.add_thread(thread, cx))

View file

@ -75,15 +75,15 @@ impl ContextStore {
return Task::ready(Err(anyhow!("failed to read project"))); return Task::ready(Err(anyhow!("failed to read project")));
}; };
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let open_buffer_task = project.update(&mut cx, |project, cx| { let open_buffer_task = project.update(cx, |project, cx| {
project.open_buffer(project_path.clone(), cx) project.open_buffer(project_path.clone(), cx)
})?; })?;
let buffer_entity = open_buffer_task.await?; let buffer_entity = open_buffer_task.await?;
let buffer_id = this.update(&mut cx, |_, cx| buffer_entity.read(cx).remote_id())?; let buffer_id = this.update(cx, |_, cx| buffer_entity.read(cx).remote_id())?;
let already_included = this.update(&mut cx, |this, _cx| { let already_included = this.update(cx, |this, _cx| {
match this.will_include_buffer(buffer_id, &project_path.path) { match this.will_include_buffer(buffer_id, &project_path.path) {
Some(FileInclusion::Direct(context_id)) => { Some(FileInclusion::Direct(context_id)) => {
this.remove_context(context_id); this.remove_context(context_id);
@ -98,7 +98,7 @@ impl ContextStore {
return anyhow::Ok(()); return anyhow::Ok(());
} }
let (buffer_info, text_task) = this.update(&mut cx, |_, cx| { let (buffer_info, text_task) = this.update(cx, |_, cx| {
let buffer = buffer_entity.read(cx); let buffer = buffer_entity.read(cx);
collect_buffer_info_and_text( collect_buffer_info_and_text(
project_path.path.clone(), project_path.path.clone(),
@ -110,7 +110,7 @@ impl ContextStore {
let text = text_task.await; let text = text_task.await;
this.update(&mut cx, |this, _cx| { this.update(cx, |this, _cx| {
this.insert_file(make_context_buffer(buffer_info, text)); this.insert_file(make_context_buffer(buffer_info, text));
})?; })?;
@ -123,8 +123,8 @@ impl ContextStore {
buffer_entity: Entity<Buffer>, buffer_entity: Entity<Buffer>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let (buffer_info, text_task) = this.update(&mut cx, |_, cx| { let (buffer_info, text_task) = this.update(cx, |_, cx| {
let buffer = buffer_entity.read(cx); let buffer = buffer_entity.read(cx);
let Some(file) = buffer.file() else { let Some(file) = buffer.file() else {
return Err(anyhow!("Buffer has no path.")); return Err(anyhow!("Buffer has no path."));
@ -139,7 +139,7 @@ impl ContextStore {
let text = text_task.await; let text = text_task.await;
this.update(&mut cx, |this, _cx| { this.update(cx, |this, _cx| {
this.insert_file(make_context_buffer(buffer_info, text)) this.insert_file(make_context_buffer(buffer_info, text))
})?; })?;
@ -179,18 +179,18 @@ impl ContextStore {
} }
let worktree_id = project_path.worktree_id; let worktree_id = project_path.worktree_id;
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let worktree = project.update(&mut cx, |project, cx| { let worktree = project.update(cx, |project, cx| {
project project
.worktree_for_id(worktree_id, cx) .worktree_for_id(worktree_id, cx)
.ok_or_else(|| anyhow!("no worktree found for {worktree_id:?}")) .ok_or_else(|| anyhow!("no worktree found for {worktree_id:?}"))
})??; })??;
let files = worktree.update(&mut cx, |worktree, _cx| { let files = worktree.update(cx, |worktree, _cx| {
collect_files_in_path(worktree, &project_path.path) collect_files_in_path(worktree, &project_path.path)
})?; })?;
let open_buffers_task = project.update(&mut cx, |project, cx| { let open_buffers_task = project.update(cx, |project, cx| {
let tasks = files.iter().map(|file_path| { let tasks = files.iter().map(|file_path| {
project.open_buffer( project.open_buffer(
ProjectPath { ProjectPath {
@ -207,7 +207,7 @@ impl ContextStore {
let mut buffer_infos = Vec::new(); let mut buffer_infos = Vec::new();
let mut text_tasks = Vec::new(); let mut text_tasks = Vec::new();
this.update(&mut cx, |_, cx| { this.update(cx, |_, cx| {
for (path, buffer_entity) in files.into_iter().zip(buffers) { for (path, buffer_entity) in files.into_iter().zip(buffers) {
// Skip all binary files and other non-UTF8 files // Skip all binary files and other non-UTF8 files
if let Ok(buffer_entity) = buffer_entity { if let Ok(buffer_entity) = buffer_entity {
@ -236,7 +236,7 @@ impl ContextStore {
bail!("No text files found in {}", &project_path.path.display()); bail!("No text files found in {}", &project_path.path.display());
} }
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.insert_directory(&project_path.path, context_buffers); this.insert_directory(&project_path.path, context_buffers);
})?; })?;
@ -595,10 +595,10 @@ fn refresh_file_text(
let id = file_context.id; let id = file_context.id;
let task = refresh_context_buffer(&file_context.context_buffer, cx); let task = refresh_context_buffer(&file_context.context_buffer, cx);
if let Some(task) = task { if let Some(task) = task {
Some(cx.spawn(|mut cx| async move { Some(cx.spawn(async move |cx| {
let context_buffer = task.await; let context_buffer = task.await;
context_store context_store
.update(&mut cx, |context_store, _| { .update(cx, |context_store, _| {
let new_file_context = FileContext { id, context_buffer }; let new_file_context = FileContext { id, context_buffer };
context_store.replace_context(AssistantContext::File(new_file_context)); context_store.replace_context(AssistantContext::File(new_file_context));
}) })
@ -636,10 +636,10 @@ fn refresh_directory_text(
let id = directory_context.snapshot.id; let id = directory_context.snapshot.id;
let path = directory_context.path.clone(); let path = directory_context.path.clone();
Some(cx.spawn(|mut cx| async move { Some(cx.spawn(async move |cx| {
let context_buffers = context_buffers.await; let context_buffers = context_buffers.await;
context_store context_store
.update(&mut cx, |context_store, _| { .update(cx, |context_store, _| {
let new_directory_context = DirectoryContext::new(id, &path, context_buffers); let new_directory_context = DirectoryContext::new(id, &path, context_buffers);
context_store.replace_context(AssistantContext::Directory(new_directory_context)); context_store.replace_context(AssistantContext::Directory(new_directory_context));
}) })
@ -654,9 +654,9 @@ fn refresh_thread_text(
) -> Task<()> { ) -> Task<()> {
let id = thread_context.id; let id = thread_context.id;
let thread = thread_context.thread.clone(); let thread = thread_context.thread.clone();
cx.spawn(move |mut cx| async move { cx.spawn(async move |cx| {
context_store context_store
.update(&mut cx, |context_store, cx| { .update(cx, |context_store, cx| {
let text = thread.read(cx).text().into(); let text = thread.read(cx).text().into();
context_store.replace_context(AssistantContext::Thread(ThreadContext { context_store.replace_context(AssistantContext::Thread(ThreadContext {
id, id,

View file

@ -335,12 +335,12 @@ impl ContextStrip {
context_store.accept_suggested_context(&suggested, cx) context_store.accept_suggested_context(&suggested, cx)
}); });
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
match task.await.notify_async_err(&mut cx) { match task.await.notify_async_err(cx) {
None => {} None => {}
Some(()) => { Some(()) => {
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
this.update(&mut cx, |_, cx| cx.notify())?; this.update(cx, |_, cx| cx.notify())?;
} }
} }
} }

View file

@ -276,7 +276,7 @@ impl InlineAssistant {
if is_authenticated() { if is_authenticated() {
handle_assist(window, cx); handle_assist(window, cx);
} else { } else {
cx.spawn_in(window, |_workspace, mut cx| async move { cx.spawn_in(window, async move |_workspace, cx| {
let Some(task) = cx.update(|_, cx| { let Some(task) = cx.update(|_, cx| {
LanguageModelRegistry::read_global(cx) LanguageModelRegistry::read_global(cx)
.active_provider() .active_provider()
@ -1456,9 +1456,9 @@ impl EditorInlineAssists {
assist_ids: Vec::new(), assist_ids: Vec::new(),
scroll_lock: None, scroll_lock: None,
highlight_updates: highlight_updates_tx, highlight_updates: highlight_updates_tx,
_update_highlights: cx.spawn(|cx| { _update_highlights: cx.spawn({
let editor = editor.downgrade(); let editor = editor.downgrade();
async move { async move |cx| {
while let Ok(()) = highlight_updates_rx.changed().await { while let Ok(()) = highlight_updates_rx.changed().await {
let editor = editor.upgrade().context("editor was dropped")?; let editor = editor.upgrade().context("editor was dropped")?;
cx.update_global(|assistant: &mut InlineAssistant, cx| { cx.update_global(|assistant: &mut InlineAssistant, cx| {
@ -1748,10 +1748,10 @@ impl CodeActionProvider for AssistantCodeActionProvider {
let editor = self.editor.clone(); let editor = self.editor.clone();
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
let thread_store = self.thread_store.clone(); let thread_store = self.thread_store.clone();
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let editor = editor.upgrade().context("editor was released")?; let editor = editor.upgrade().context("editor was released")?;
let range = editor let range = editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
editor.buffer().update(cx, |multibuffer, cx| { editor.buffer().update(cx, |multibuffer, cx| {
let buffer = buffer.read(cx); let buffer = buffer.read(cx);
let multibuffer_snapshot = multibuffer.read(cx); let multibuffer_snapshot = multibuffer.read(cx);

View file

@ -206,10 +206,10 @@ impl MessageEditor {
let thread = self.thread.clone(); let thread = self.thread.clone();
let context_store = self.context_store.clone(); let context_store = self.context_store.clone();
cx.spawn(move |_, mut cx| async move { cx.spawn(async move |_, cx| {
refresh_task.await; refresh_task.await;
thread thread
.update(&mut cx, |thread, cx| { .update(cx, |thread, cx| {
let context = context_store.read(cx).snapshot(cx).collect::<Vec<_>>(); let context = context_store.read(cx).snapshot(cx).collect::<Vec<_>>();
thread.insert_user_message(user_message, context, cx); thread.insert_user_message(user_message, context, cx);
thread.send_to_model(model, request_kind, cx); thread.send_to_model(model, request_kind, cx);
@ -297,9 +297,9 @@ impl MessageEditor {
.thread .thread
.update(cx, |thread, cx| thread.report_feedback(is_positive, cx)); .update(cx, |thread, cx| thread.report_feedback(is_positive, cx));
cx.spawn(|_, mut cx| async move { cx.spawn(async move |_, cx| {
report.await?; report.await?;
workspace.update(&mut cx, |workspace, cx| { workspace.update(cx, |workspace, cx| {
let message = if is_positive { let message = if is_positive {
"Positive feedback recorded. Thank you!" "Positive feedback recorded. Thank you!"
} else { } else {

View file

@ -40,7 +40,7 @@ impl TerminalCodegen {
let telemetry = self.telemetry.clone(); let telemetry = self.telemetry.clone();
self.status = CodegenStatus::Pending; self.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone())); self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
self.generation = cx.spawn(|this, mut cx| async move { self.generation = cx.spawn(async move |this, cx| {
let model_telemetry_id = model.telemetry_id(); let model_telemetry_id = model.telemetry_id();
let model_provider_id = model.provider_id(); let model_provider_id = model.provider_id();
let response = model.stream_completion_text(prompt, &cx).await; let response = model.stream_completion_text(prompt, &cx).await;
@ -97,12 +97,12 @@ impl TerminalCodegen {
} }
}); });
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.message_id = message_id; this.message_id = message_id;
})?; })?;
while let Some(hunk) = hunks_rx.next().await { while let Some(hunk) = hunks_rx.next().await {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Some(transaction) = &mut this.transaction { if let Some(transaction) = &mut this.transaction {
transaction.push(hunk, cx); transaction.push(hunk, cx);
cx.notify(); cx.notify();
@ -116,7 +116,7 @@ impl TerminalCodegen {
let result = generate.await; let result = generate.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Err(error) = result { if let Err(error) = result {
this.status = CodegenStatus::Error(error); this.status = CodegenStatus::Error(error);
} else { } else {

View file

@ -394,9 +394,9 @@ impl Thread {
/// Serializes this thread into a format for storage or telemetry. /// Serializes this thread into a format for storage or telemetry.
pub fn serialize(&self, cx: &mut Context<Self>) -> Task<Result<SerializedThread>> { pub fn serialize(&self, cx: &mut Context<Self>) -> Task<Result<SerializedThread>> {
let initial_project_snapshot = self.initial_project_snapshot.clone(); let initial_project_snapshot = self.initial_project_snapshot.clone();
cx.spawn(|this, cx| async move { cx.spawn(async move |this, cx| {
let initial_project_snapshot = initial_project_snapshot.await; let initial_project_snapshot = initial_project_snapshot.await;
this.read_with(&cx, |this, _| SerializedThread { this.read_with(cx, |this, _| SerializedThread {
summary: this.summary_or_default(), summary: this.summary_or_default(),
updated_at: this.updated_at(), updated_at: this.updated_at(),
messages: this messages: this
@ -602,7 +602,7 @@ impl Thread {
) { ) {
let pending_completion_id = post_inc(&mut self.completion_count); let pending_completion_id = post_inc(&mut self.completion_count);
let task = cx.spawn(|thread, mut cx| async move { let task = cx.spawn(async move |thread, cx| {
let stream = model.stream_completion(request, &cx); let stream = model.stream_completion(request, &cx);
let stream_completion = async { let stream_completion = async {
let mut events = stream.await?; let mut events = stream.await?;
@ -612,7 +612,7 @@ impl Thread {
while let Some(event) = events.next().await { while let Some(event) = events.next().await {
let event = event?; let event = event?;
thread.update(&mut cx, |thread, cx| { thread.update(cx, |thread, cx| {
match event { match event {
LanguageModelCompletionEvent::StartMessage { .. } => { LanguageModelCompletionEvent::StartMessage { .. } => {
thread.insert_message(Role::Assistant, String::new(), cx); thread.insert_message(Role::Assistant, String::new(), cx);
@ -671,7 +671,7 @@ impl Thread {
smol::future::yield_now().await; smol::future::yield_now().await;
} }
thread.update(&mut cx, |thread, cx| { thread.update(cx, |thread, cx| {
thread thread
.pending_completions .pending_completions
.retain(|completion| completion.id != pending_completion_id); .retain(|completion| completion.id != pending_completion_id);
@ -687,7 +687,7 @@ impl Thread {
let result = stream_completion.await; let result = stream_completion.await;
thread thread
.update(&mut cx, |thread, cx| { .update(cx, |thread, cx| {
match result.as_ref() { match result.as_ref() {
Ok(stop_reason) => match stop_reason { Ok(stop_reason) => match stop_reason {
StopReason::ToolUse => { StopReason::ToolUse => {
@ -750,7 +750,7 @@ impl Thread {
cache: false, cache: false,
}); });
self.pending_summary = cx.spawn(|this, mut cx| { self.pending_summary = cx.spawn(async move |this, cx| {
async move { async move {
let stream = model.stream_completion_text(request, &cx); let stream = model.stream_completion_text(request, &cx);
let mut messages = stream.await?; let mut messages = stream.await?;
@ -767,7 +767,7 @@ impl Thread {
} }
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if !new_summary.is_empty() { if !new_summary.is_empty() {
this.summary = Some(new_summary.into()); this.summary = Some(new_summary.into());
} }
@ -778,6 +778,7 @@ impl Thread {
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}); });
} }
@ -823,10 +824,10 @@ impl Thread {
}); });
let session = self.scripting_session.clone(); let session = self.scripting_session.clone();
cx.spawn(|_, cx| async move { cx.spawn(async move |_, cx| {
script_task.await; script_task.await;
let message = session.read_with(&cx, |session, _cx| { let message = session.read_with(cx, |session, _cx| {
// Using a id to get the script output seems impractical. // Using a id to get the script output seems impractical.
// Why not just include it in the Task result? // Why not just include it in the Task result?
// This is because we'll later report the script state as it runs, // This is because we'll later report the script state as it runs,
@ -851,12 +852,12 @@ impl Thread {
output: Task<Result<String>>, output: Task<Result<String>>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let insert_output_task = cx.spawn(|thread, mut cx| { let insert_output_task = cx.spawn({
let tool_use_id = tool_use_id.clone(); let tool_use_id = tool_use_id.clone();
async move { async move |thread, cx| {
let output = output.await; let output = output.await;
thread thread
.update(&mut cx, |thread, cx| { .update(cx, |thread, cx| {
let pending_tool_use = thread let pending_tool_use = thread
.tool_use .tool_use
.insert_tool_output(tool_use_id.clone(), output); .insert_tool_output(tool_use_id.clone(), output);
@ -881,12 +882,12 @@ impl Thread {
output: Task<Result<String>>, output: Task<Result<String>>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let insert_output_task = cx.spawn(|thread, mut cx| { let insert_output_task = cx.spawn({
let tool_use_id = tool_use_id.clone(); let tool_use_id = tool_use_id.clone();
async move { async move |thread, cx| {
let output = output.await; let output = output.await;
thread thread
.update(&mut cx, |thread, cx| { .update(cx, |thread, cx| {
let pending_tool_use = thread let pending_tool_use = thread
.scripting_tool_use .scripting_tool_use
.insert_tool_output(tool_use_id.clone(), output); .insert_tool_output(tool_use_id.clone(), output);
@ -985,7 +986,7 @@ impl Thread {
.map(|worktree| Self::worktree_snapshot(worktree, cx)) .map(|worktree| Self::worktree_snapshot(worktree, cx))
.collect(); .collect();
cx.spawn(move |_, cx| async move { cx.spawn(async move |_, cx| {
let worktree_snapshots = futures::future::join_all(worktree_snapshots).await; let worktree_snapshots = futures::future::join_all(worktree_snapshots).await;
let mut unsaved_buffers = Vec::new(); let mut unsaved_buffers = Vec::new();
@ -1012,7 +1013,7 @@ impl Thread {
} }
fn worktree_snapshot(worktree: Entity<project::Worktree>, cx: &App) -> Task<WorktreeSnapshot> { fn worktree_snapshot(worktree: Entity<project::Worktree>, cx: &App) -> Task<WorktreeSnapshot> {
cx.spawn(move |cx| async move { cx.spawn(async move |cx| {
// Get worktree path and snapshot // Get worktree path and snapshot
let worktree_info = cx.update(|app_cx| { let worktree_info = cx.update(|app_cx| {
let worktree = worktree.read(app_cx); let worktree = worktree.read(app_cx);
@ -1036,7 +1037,7 @@ impl Thread {
let current_branch = repo_entry.branch().map(|branch| branch.name.to_string()); let current_branch = repo_entry.branch().map(|branch| branch.name.to_string());
// Get repository info // Get repository info
let repo_result = worktree.read_with(&cx, |worktree, _cx| { let repo_result = worktree.read_with(cx, |worktree, _cx| {
if let project::Worktree::Local(local_worktree) = &worktree { if let project::Worktree::Local(local_worktree) = &worktree {
local_worktree.get_local_repo(repo_entry).map(|local_repo| { local_worktree.get_local_repo(repo_entry).map(|local_repo| {
let repo = local_repo.repo(); let repo = local_repo.repo();
@ -1051,7 +1052,7 @@ impl Thread {
Ok(Some((remote_url, head_sha, repository))) => { Ok(Some((remote_url, head_sha, repository))) => {
// Get diff asynchronously // Get diff asynchronously
let diff = repository let diff = repository
.diff(git::repository::DiffType::HeadToWorktree, cx) .diff(git::repository::DiffType::HeadToWorktree, cx.clone())
.await .await
.ok(); .ok();

View file

@ -106,14 +106,14 @@ impl ThreadStore {
) -> Task<Result<Entity<Thread>>> { ) -> Task<Result<Entity<Thread>>> {
let id = id.clone(); let id = id.clone();
let database_future = ThreadsDatabase::global_future(cx); let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let database = database_future.await.map_err(|err| anyhow!(err))?; let database = database_future.await.map_err(|err| anyhow!(err))?;
let thread = database let thread = database
.try_find_thread(id.clone()) .try_find_thread(id.clone())
.await? .await?
.ok_or_else(|| anyhow!("no thread found with ID: {id:?}"))?; .ok_or_else(|| anyhow!("no thread found with ID: {id:?}"))?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
cx.new(|cx| { cx.new(|cx| {
Thread::deserialize( Thread::deserialize(
id.clone(), id.clone(),
@ -133,23 +133,23 @@ impl ThreadStore {
thread.update(cx, |thread, cx| (thread.id().clone(), thread.serialize(cx))); thread.update(cx, |thread, cx| (thread.id().clone(), thread.serialize(cx)));
let database_future = ThreadsDatabase::global_future(cx); let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let serialized_thread = serialized_thread.await?; let serialized_thread = serialized_thread.await?;
let database = database_future.await.map_err(|err| anyhow!(err))?; let database = database_future.await.map_err(|err| anyhow!(err))?;
database.save_thread(metadata, serialized_thread).await?; database.save_thread(metadata, serialized_thread).await?;
this.update(&mut cx, |this, cx| this.reload(cx))?.await this.update(cx, |this, cx| this.reload(cx))?.await
}) })
} }
pub fn delete_thread(&mut self, id: &ThreadId, cx: &mut Context<Self>) -> Task<Result<()>> { pub fn delete_thread(&mut self, id: &ThreadId, cx: &mut Context<Self>) -> Task<Result<()>> {
let id = id.clone(); let id = id.clone();
let database_future = ThreadsDatabase::global_future(cx); let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let database = database_future.await.map_err(|err| anyhow!(err))?; let database = database_future.await.map_err(|err| anyhow!(err))?;
database.delete_thread(id.clone()).await?; database.delete_thread(id.clone()).await?;
this.update(&mut cx, |this, _cx| { this.update(cx, |this, _cx| {
this.threads.retain(|thread| thread.id != id) this.threads.retain(|thread| thread.id != id)
}) })
}) })
@ -157,14 +157,14 @@ impl ThreadStore {
pub fn reload(&self, cx: &mut Context<Self>) -> Task<Result<()>> { pub fn reload(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
let database_future = ThreadsDatabase::global_future(cx); let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let threads = database_future let threads = database_future
.await .await
.map_err(|err| anyhow!(err))? .map_err(|err| anyhow!(err))?
.list_threads() .list_threads()
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.threads = threads; this.threads = threads;
cx.notify(); cx.notify();
}) })
@ -193,7 +193,7 @@ impl ThreadStore {
cx.spawn({ cx.spawn({
let server = server.clone(); let server = server.clone();
let server_id = server_id.clone(); let server_id = server_id.clone();
|this, mut cx| async move { async move |this, cx| {
let Some(protocol) = server.client() else { let Some(protocol) = server.client() else {
return; return;
}; };
@ -218,7 +218,7 @@ impl ThreadStore {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
this.update(&mut cx, |this, _cx| { this.update(cx, |this, _cx| {
this.context_server_tool_ids.insert(server_id, tool_ids); this.context_server_tool_ids.insert(server_id, tool_ids);
}) })
.log_err(); .log_err();

View file

@ -1144,9 +1144,9 @@ impl AssistantContext {
fn set_language(&mut self, cx: &mut Context<Self>) { fn set_language(&mut self, cx: &mut Context<Self>) {
let markdown = self.language_registry.language_for_name("Markdown"); let markdown = self.language_registry.language_for_name("Markdown");
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let markdown = markdown.await?; let markdown = markdown.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.buffer this.buffer
.update(cx, |buffer, cx| buffer.set_language(Some(markdown), cx)); .update(cx, |buffer, cx| buffer.set_language(Some(markdown), cx));
}) })
@ -1188,7 +1188,7 @@ impl AssistantContext {
return; return;
}; };
let debounce = self.token_count.is_some(); let debounce = self.token_count.is_some();
self.pending_token_count = cx.spawn(|this, mut cx| { self.pending_token_count = cx.spawn(async move |this, cx| {
async move { async move {
if debounce { if debounce {
cx.background_executor() cx.background_executor()
@ -1197,13 +1197,14 @@ impl AssistantContext {
} }
let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?; let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.token_count = Some(token_count); this.token_count = Some(token_count);
this.start_cache_warming(&model, cx); this.start_cache_warming(&model, cx);
cx.notify() cx.notify()
}) })
} }
.log_err() .log_err()
.await
}); });
} }
@ -1342,7 +1343,7 @@ impl AssistantContext {
}; };
let model = Arc::clone(model); let model = Arc::clone(model);
self.pending_cache_warming_task = cx.spawn(|this, mut cx| { self.pending_cache_warming_task = cx.spawn(async move |this, cx| {
async move { async move {
match model.stream_completion(request, &cx).await { match model.stream_completion(request, &cx).await {
Ok(mut stream) => { Ok(mut stream) => {
@ -1353,13 +1354,14 @@ impl AssistantContext {
log::warn!("Cache warming failed: {}", e); log::warn!("Cache warming failed: {}", e);
} }
}; };
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.update_cache_status_for_completion(cx); this.update_cache_status_for_completion(cx);
}) })
.ok(); .ok();
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}); });
} }
@ -1916,7 +1918,7 @@ impl AssistantContext {
}); });
self.reparse(cx); self.reparse(cx);
let insert_output_task = cx.spawn(|this, mut cx| async move { let insert_output_task = cx.spawn(async move |this, cx| {
let run_command = async { let run_command = async {
let mut stream = output.await?; let mut stream = output.await?;
@ -1933,7 +1935,7 @@ impl AssistantContext {
while let Some(event) = stream.next().await { while let Some(event) = stream.next().await {
let event = event?; let event = event?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.buffer.update(cx, |buffer, _cx| { this.buffer.update(cx, |buffer, _cx| {
buffer.finalize_last_transaction(); buffer.finalize_last_transaction();
buffer.start_transaction() buffer.start_transaction()
@ -2034,7 +2036,7 @@ impl AssistantContext {
})?; })?;
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.buffer.update(cx, |buffer, cx| { this.buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction(); buffer.finalize_last_transaction();
buffer.start_transaction(); buffer.start_transaction();
@ -2080,7 +2082,7 @@ impl AssistantContext {
let command_result = run_command.await; let command_result = run_command.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let version = this.version.clone(); let version = this.version.clone();
let timestamp = this.next_timestamp(); let timestamp = this.next_timestamp();
let Some(invoked_slash_command) = this.invoked_slash_commands.get_mut(&command_id) let Some(invoked_slash_command) = this.invoked_slash_commands.get_mut(&command_id)
@ -2210,7 +2212,7 @@ impl AssistantContext {
let pending_completion_id = post_inc(&mut self.completion_count); let pending_completion_id = post_inc(&mut self.completion_count);
let task = cx.spawn({ let task = cx.spawn({
|this, mut cx| async move { async move |this, cx| {
let stream = model.stream_completion(request, &cx); let stream = model.stream_completion(request, &cx);
let assistant_message_id = assistant_message.id; let assistant_message_id = assistant_message.id;
let mut response_latency = None; let mut response_latency = None;
@ -2225,7 +2227,7 @@ impl AssistantContext {
} }
let event = event?; let event = event?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let message_ix = this let message_ix = this
.message_anchors .message_anchors
.iter() .iter()
@ -2264,7 +2266,7 @@ impl AssistantContext {
})?; })?;
smol::future::yield_now().await; smol::future::yield_now().await;
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.pending_completions this.pending_completions
.retain(|completion| completion.id != pending_completion_id); .retain(|completion| completion.id != pending_completion_id);
this.summarize(false, cx); this.summarize(false, cx);
@ -2276,7 +2278,7 @@ impl AssistantContext {
let result = stream_completion.await; let result = stream_completion.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let error_message = if let Some(error) = result.as_ref().err() { let error_message = if let Some(error) = result.as_ref().err() {
if error.is::<PaymentRequiredError>() { if error.is::<PaymentRequiredError>() {
cx.emit(ContextEvent::ShowPaymentRequiredError); cx.emit(ContextEvent::ShowPaymentRequiredError);
@ -2786,7 +2788,7 @@ impl AssistantContext {
cache: false, cache: false,
}); });
self.pending_summary = cx.spawn(|this, mut cx| { self.pending_summary = cx.spawn(async move |this, cx| {
async move { async move {
let stream = model.stream_completion_text(request, &cx); let stream = model.stream_completion_text(request, &cx);
let mut messages = stream.await?; let mut messages = stream.await?;
@ -2795,7 +2797,7 @@ impl AssistantContext {
while let Some(message) = messages.stream.next().await { while let Some(message) = messages.stream.next().await {
let text = message?; let text = message?;
let mut lines = text.lines(); let mut lines = text.lines();
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let version = this.version.clone(); let version = this.version.clone();
let timestamp = this.next_timestamp(); let timestamp = this.next_timestamp();
let summary = this.summary.get_or_insert(ContextSummary::default()); let summary = this.summary.get_or_insert(ContextSummary::default());
@ -2819,7 +2821,7 @@ impl AssistantContext {
} }
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let version = this.version.clone(); let version = this.version.clone();
let timestamp = this.next_timestamp(); let timestamp = this.next_timestamp();
if let Some(summary) = this.summary.as_mut() { if let Some(summary) = this.summary.as_mut() {
@ -2837,6 +2839,7 @@ impl AssistantContext {
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}); });
} }
} }
@ -2943,12 +2946,12 @@ impl AssistantContext {
return; return;
} }
self.pending_save = cx.spawn(|this, mut cx| async move { self.pending_save = cx.spawn(async move |this, cx| {
if let Some(debounce) = debounce { if let Some(debounce) = debounce {
cx.background_executor().timer(debounce).await; cx.background_executor().timer(debounce).await;
} }
let (old_path, summary) = this.read_with(&cx, |this, _| { let (old_path, summary) = this.read_with(cx, |this, _| {
let path = this.path.clone(); let path = this.path.clone();
let summary = if let Some(summary) = this.summary.as_ref() { let summary = if let Some(summary) = this.summary.as_ref() {
if summary.done { if summary.done {
@ -2963,7 +2966,7 @@ impl AssistantContext {
})?; })?;
if let Some(summary) = summary { if let Some(summary) = summary {
let context = this.read_with(&cx, |this, cx| this.serialize(cx))?; let context = this.read_with(cx, |this, cx| this.serialize(cx))?;
let mut discriminant = 1; let mut discriminant = 1;
let mut new_path; let mut new_path;
loop { loop {
@ -2995,7 +2998,7 @@ impl AssistantContext {
} }
} }
this.update(&mut cx, |this, _| this.path = Some(new_path))?; this.update(cx, |this, _| this.path = Some(new_path))?;
} }
Ok(()) Ok(())

View file

@ -907,7 +907,7 @@ impl ContextEditor {
if editor_state.opened_patch != patch { if editor_state.opened_patch != patch {
state.update_task = Some({ state.update_task = Some({
let this = this.clone(); let this = this.clone();
cx.spawn_in(window, |_, cx| async move { cx.spawn_in(window, async move |_, cx| {
Self::update_patch_editor(this.clone(), patch, cx) Self::update_patch_editor(this.clone(), patch, cx)
.await .await
.log_err(); .log_err();
@ -1070,10 +1070,9 @@ impl ContextEditor {
}) })
.ok(); .ok();
} else { } else {
patch_state.update_task = patch_state.update_task = Some(cx.spawn_in(window, async move |this, cx| {
Some(cx.spawn_in(window, move |this, cx| async move { Self::open_patch_editor(this, new_patch, cx).await.log_err();
Self::open_patch_editor(this, new_patch, cx).await.log_err(); }));
}));
} }
} }
} }
@ -1103,10 +1102,10 @@ impl ContextEditor {
async fn open_patch_editor( async fn open_patch_editor(
this: WeakEntity<Self>, this: WeakEntity<Self>,
patch: AssistantPatch, patch: AssistantPatch,
mut cx: AsyncWindowContext, cx: &mut AsyncWindowContext,
) -> Result<()> { ) -> Result<()> {
let project = this.read_with(&cx, |this, _| this.project.clone())?; let project = this.read_with(cx, |this, _| this.project.clone())?;
let resolved_patch = patch.resolve(project.clone(), &mut cx).await; let resolved_patch = patch.resolve(project.clone(), cx).await;
let editor = cx.new_window_entity(|window, cx| { let editor = cx.new_window_entity(|window, cx| {
let editor = ProposedChangesEditor::new( let editor = ProposedChangesEditor::new(
@ -1130,7 +1129,7 @@ impl ContextEditor {
editor editor
})?; })?;
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
if let Some(patch_state) = this.patches.get_mut(&patch.range) { if let Some(patch_state) = this.patches.get_mut(&patch.range) {
patch_state.editor = Some(PatchEditorState { patch_state.editor = Some(PatchEditorState {
editor: editor.downgrade(), editor: editor.downgrade(),
@ -1139,8 +1138,8 @@ impl ContextEditor {
patch_state.update_task.take(); patch_state.update_task.take();
} }
})?; })?;
this.read_with(&cx, |this, _| this.workspace.clone())? this.read_with(cx, |this, _| this.workspace.clone())?
.update_in(&mut cx, |workspace, window, cx| { .update_in(cx, |workspace, window, cx| {
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, window, cx) workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, window, cx)
}) })
.log_err(); .log_err();
@ -1151,11 +1150,11 @@ impl ContextEditor {
async fn update_patch_editor( async fn update_patch_editor(
this: WeakEntity<Self>, this: WeakEntity<Self>,
patch: AssistantPatch, patch: AssistantPatch,
mut cx: AsyncWindowContext, cx: &mut AsyncWindowContext,
) -> Result<()> { ) -> Result<()> {
let project = this.update(&mut cx, |this, _| this.project.clone())?; let project = this.update(cx, |this, _| this.project.clone())?;
let resolved_patch = patch.resolve(project.clone(), &mut cx).await; let resolved_patch = patch.resolve(project.clone(), cx).await;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
let patch_state = this.patches.get_mut(&patch.range)?; let patch_state = this.patches.get_mut(&patch.range)?;
let locations = resolved_patch let locations = resolved_patch
@ -1625,14 +1624,14 @@ impl ContextEditor {
.map(|path| Workspace::project_path_for_path(project.clone(), &path, false, cx)) .map(|path| Workspace::project_path_for_path(project.clone(), &path, false, cx))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
cx.spawn(move |_, cx| async move { cx.spawn(async move |_, cx| {
let mut paths = vec![]; let mut paths = vec![];
let mut worktrees = vec![]; let mut worktrees = vec![];
let opened_paths = futures::future::join_all(tasks).await; let opened_paths = futures::future::join_all(tasks).await;
for (worktree, project_path) in opened_paths.into_iter().flatten() { for (worktree, project_path) in opened_paths.into_iter().flatten() {
let Ok(worktree_root_name) = let Ok(worktree_root_name) =
worktree.read_with(&cx, |worktree, _| worktree.root_name().to_string()) worktree.read_with(cx, |worktree, _| worktree.root_name().to_string())
else { else {
continue; continue;
}; };
@ -1649,12 +1648,12 @@ impl ContextEditor {
}; };
window window
.spawn(cx, |mut cx| async move { .spawn(cx, async move |cx| {
let (paths, dragged_file_worktrees) = paths.await; let (paths, dragged_file_worktrees) = paths.await;
let cmd_name = FileSlashCommand.name(); let cmd_name = FileSlashCommand.name();
context_editor_view context_editor_view
.update_in(&mut cx, |context_editor, window, cx| { .update_in(cx, |context_editor, window, cx| {
let file_argument = paths let file_argument = paths
.into_iter() .into_iter()
.map(|path| path.to_string_lossy().to_string()) .map(|path| path.to_string_lossy().to_string())
@ -2200,9 +2199,9 @@ impl ContextEditor {
.log_err(); .log_err();
if let Some(client) = client { if let Some(client) = client {
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
client.authenticate_and_connect(true, &mut cx).await?; client.authenticate_and_connect(true, cx).await?;
this.update(&mut cx, |_, cx| cx.notify()) this.update(cx, |_, cx| cx.notify())
}) })
.detach_and_log_err(cx) .detach_and_log_err(cx)
} }
@ -3161,10 +3160,10 @@ impl FollowableItem for ContextEditor {
assistant_panel_delegate.open_remote_context(workspace, context_id, window, cx) assistant_panel_delegate.open_remote_context(workspace, context_id, window, cx)
}); });
Some(window.spawn(cx, |mut cx| async move { Some(window.spawn(cx, async move |cx| {
let context_editor = context_editor_task.await?; let context_editor = context_editor_task.await?;
context_editor context_editor
.update_in(&mut cx, |context_editor, window, cx| { .update_in(cx, |context_editor, window, cx| {
context_editor.remote_id = Some(id); context_editor.remote_id = Some(id);
context_editor.editor.update(cx, |editor, cx| { context_editor.editor.update(cx, |editor, cx| {
editor.apply_update_proto( editor.apply_update_proto(

View file

@ -164,9 +164,9 @@ impl PickerDelegate for SavedContextPickerDelegate {
cx: &mut Context<Picker<Self>>, cx: &mut Context<Picker<Self>>,
) -> Task<()> { ) -> Task<()> {
let search = self.store.read(cx).search(query, cx); let search = self.store.read(cx).search(query, cx);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let matches = search.await; let matches = search.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let host_contexts = this.delegate.store.read(cx).host_contexts(); let host_contexts = this.delegate.store.read(cx).host_contexts();
this.delegate.matches = host_contexts this.delegate.matches = host_contexts
.iter() .iter()

View file

@ -100,7 +100,7 @@ impl ContextStore {
let fs = project.read(cx).fs().clone(); let fs = project.read(cx).fs().clone();
let languages = project.read(cx).languages().clone(); let languages = project.read(cx).languages().clone();
let telemetry = project.read(cx).client().telemetry().clone(); let telemetry = project.read(cx).client().telemetry().clone();
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100); const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await; let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
@ -125,16 +125,15 @@ impl ContextStore {
languages, languages,
slash_commands, slash_commands,
telemetry, telemetry,
_watch_updates: cx.spawn(|this, mut cx| { _watch_updates: cx.spawn(async move |this, cx| {
async move { async move {
while events.next().await.is_some() { while events.next().await.is_some() {
this.update(&mut cx, |this, cx| this.reload(cx))? this.update(cx, |this, cx| this.reload(cx))?.await.log_err();
.await
.log_err();
} }
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}), }),
client_subscription: None, client_subscription: None,
_project_subscriptions: vec![ _project_subscriptions: vec![
@ -395,7 +394,7 @@ impl ContextStore {
let prompt_builder = self.prompt_builder.clone(); let prompt_builder = self.prompt_builder.clone();
let slash_commands = self.slash_commands.clone(); let slash_commands = self.slash_commands.clone();
let request = self.client.request(proto::CreateContext { project_id }); let request = self.client.request(proto::CreateContext { project_id });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = request.await?; let response = request.await?;
let context_id = ContextId::from_proto(response.context_id); let context_id = ContextId::from_proto(response.context_id);
let context_proto = response.context.context("invalid context")?; let context_proto = response.context.context("invalid context")?;
@ -421,8 +420,8 @@ impl ContextStore {
.collect::<Result<Vec<_>>>() .collect::<Result<Vec<_>>>()
}) })
.await?; .await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))?; context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) { if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context existing_context
} else { } else {
@ -457,7 +456,7 @@ impl ContextStore {
let prompt_builder = self.prompt_builder.clone(); let prompt_builder = self.prompt_builder.clone();
let slash_commands = self.slash_commands.clone(); let slash_commands = self.slash_commands.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let saved_context = load.await?; let saved_context = load.await?;
let context = cx.new(|cx| { let context = cx.new(|cx| {
AssistantContext::deserialize( AssistantContext::deserialize(
@ -471,7 +470,7 @@ impl ContextStore {
cx, cx,
) )
})?; })?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_path(&path, cx) { if let Some(existing_context) = this.loaded_context_for_path(&path, cx) {
existing_context existing_context
} else { } else {
@ -489,7 +488,7 @@ impl ContextStore {
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let fs = self.fs.clone(); let fs = self.fs.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
fs.remove_file( fs.remove_file(
&path, &path,
RemoveOptions { RemoveOptions {
@ -499,7 +498,7 @@ impl ContextStore {
) )
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.contexts.retain(|context| { this.contexts.retain(|context| {
context context
.upgrade() .upgrade()
@ -565,7 +564,7 @@ impl ContextStore {
}); });
let prompt_builder = self.prompt_builder.clone(); let prompt_builder = self.prompt_builder.clone();
let slash_commands = self.slash_commands.clone(); let slash_commands = self.slash_commands.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = request.await?; let response = request.await?;
let context_proto = response.context.context("invalid context")?; let context_proto = response.context.context("invalid context")?;
let context = cx.new(|cx| { let context = cx.new(|cx| {
@ -590,8 +589,8 @@ impl ContextStore {
.collect::<Result<Vec<_>>>() .collect::<Result<Vec<_>>>()
}) })
.await?; .await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))?; context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) { if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context existing_context
} else { } else {
@ -700,12 +699,12 @@ impl ContextStore {
project_id, project_id,
contexts, contexts,
}); });
cx.spawn(|this, cx| async move { cx.spawn(async move |this, cx| {
let response = request.await?; let response = request.await?;
let mut context_ids = Vec::new(); let mut context_ids = Vec::new();
let mut operations = Vec::new(); let mut operations = Vec::new();
this.read_with(&cx, |this, cx| { this.read_with(cx, |this, cx| {
for context_version_proto in response.contexts { for context_version_proto in response.contexts {
let context_version = ContextVersion::from_proto(&context_version_proto); let context_version = ContextVersion::from_proto(&context_version_proto);
let context_id = ContextId::from_proto(context_version_proto.context_id); let context_id = ContextId::from_proto(context_version_proto.context_id);
@ -768,7 +767,7 @@ impl ContextStore {
fn reload(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> { fn reload(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
let fs = self.fs.clone(); let fs = self.fs.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
fs.create_dir(contexts_dir()).await?; fs.create_dir(contexts_dir()).await?;
let mut paths = fs.read_dir(contexts_dir()).await?; let mut paths = fs.read_dir(contexts_dir()).await?;
@ -808,7 +807,7 @@ impl ContextStore {
} }
contexts.sort_unstable_by_key(|context| Reverse(context.mtime)); contexts.sort_unstable_by_key(|context| Reverse(context.mtime));
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.contexts_metadata = contexts; this.contexts_metadata = contexts;
cx.notify(); cx.notify();
}) })
@ -850,7 +849,7 @@ impl ContextStore {
cx.spawn({ cx.spawn({
let server = server.clone(); let server = server.clone();
let server_id = server_id.clone(); let server_id = server_id.clone();
|this, mut cx| async move { async move |this, cx| {
let Some(protocol) = server.client() else { let Some(protocol) = server.client() else {
return; return;
}; };
@ -875,7 +874,7 @@ impl ContextStore {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
this.update(&mut cx, |this, _cx| { this.update( cx, |this, _cx| {
this.context_server_slash_command_ids this.context_server_slash_command_ids
.insert(server_id.clone(), slash_command_ids); .insert(server_id.clone(), slash_command_ids);
}) })

View file

@ -59,7 +59,7 @@ impl SlashCommandCompletionProvider {
let command_name = command_name.to_string(); let command_name = command_name.to_string();
let editor = self.editor.clone(); let editor = self.editor.clone();
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let matches = match_strings( let matches = match_strings(
&candidates, &candidates,
&command_name, &command_name,

View file

@ -100,7 +100,7 @@ impl PickerDelegate for SlashCommandDelegate {
cx: &mut Context<Picker<Self>>, cx: &mut Context<Picker<Self>>,
) -> Task<()> { ) -> Task<()> {
let all_commands = self.all_commands.clone(); let all_commands = self.all_commands.clone();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let filtered_commands = cx let filtered_commands = cx
.background_spawn(async move { .background_spawn(async move {
if query.is_empty() { if query.is_empty() {
@ -119,7 +119,7 @@ impl PickerDelegate for SlashCommandDelegate {
}) })
.await; .await;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.delegate.filtered_commands = filtered_commands; this.delegate.filtered_commands = filtered_commands;
this.delegate.set_selected_index(0, window, cx); this.delegate.set_selected_index(0, window, cx);
cx.notify(); cx.notify();

View file

@ -63,14 +63,14 @@ impl Eval {
model: Arc<dyn LanguageModel>, model: Arc<dyn LanguageModel>,
cx: &mut App, cx: &mut App,
) -> Task<anyhow::Result<EvalOutput>> { ) -> Task<anyhow::Result<EvalOutput>> {
cx.spawn(move |mut cx| async move { cx.spawn(async move |cx| {
checkout_repo(&self.eval_setup, &self.repo_path).await?; checkout_repo(&self.eval_setup, &self.repo_path).await?;
let (assistant, done_rx) = let (assistant, done_rx) =
cx.update(|cx| HeadlessAssistant::new(app_state.clone(), cx))??; cx.update(|cx| HeadlessAssistant::new(app_state.clone(), cx))??;
let _worktree = assistant let _worktree = assistant
.update(&mut cx, |assistant, cx| { .update(cx, |assistant, cx| {
assistant.project.update(cx, |project, cx| { assistant.project.update(cx, |project, cx| {
project.create_worktree(&self.repo_path, true, cx) project.create_worktree(&self.repo_path, true, cx)
}) })
@ -79,7 +79,7 @@ impl Eval {
let start_time = std::time::SystemTime::now(); let start_time = std::time::SystemTime::now();
assistant.update(&mut cx, |assistant, cx| { assistant.update(cx, |assistant, cx| {
assistant.thread.update(cx, |thread, cx| { assistant.thread.update(cx, |thread, cx| {
let context = vec![]; let context = vec![];
thread.insert_user_message(self.user_prompt.clone(), context, cx); thread.insert_user_message(self.user_prompt.clone(), context, cx);
@ -93,7 +93,7 @@ impl Eval {
let diff = query_git(&self.repo_path, vec!["diff"]).await?; let diff = query_git(&self.repo_path, vec!["diff"]).await?;
assistant.update(&mut cx, |assistant, cx| { assistant.update(cx, |assistant, cx| {
let thread = assistant.thread.read(cx); let thread = assistant.thread.read(cx);
let last_message = thread.messages().last().unwrap(); let last_message = thread.messages().last().unwrap();
if last_message.role != language_model::Role::Assistant { if last_message.role != language_model::Role::Assistant {

View file

@ -212,7 +212,7 @@ pub fn authenticate_model_provider(
pub async fn send_language_model_request( pub async fn send_language_model_request(
model: Arc<dyn LanguageModel>, model: Arc<dyn LanguageModel>,
request: LanguageModelRequest, request: LanguageModelRequest,
cx: AsyncApp, cx: &mut AsyncApp,
) -> anyhow::Result<String> { ) -> anyhow::Result<String> {
match model.stream_completion_text(request, &cx).await { match model.stream_completion_text(request, &cx).await {
Ok(mut stream) => { Ok(mut stream) => {

View file

@ -61,7 +61,7 @@ impl Judge {
}; };
let model = self.model.clone(); let model = self.model.clone();
cx.spawn(move |cx| send_language_model_request(model, request, cx)) cx.spawn(async move |cx| send_language_model_request(model, request, cx).await)
} }
} }

View file

@ -111,7 +111,7 @@ fn main() {
let editor_model_provider_id = editor_model.provider_id(); let editor_model_provider_id = editor_model.provider_id();
let judge_model_provider_id = judge_model.provider_id(); let judge_model_provider_id = judge_model.provider_id();
cx.spawn(move |cx| async move { cx.spawn(async move |cx| {
// Authenticate all model providers first // Authenticate all model providers first
cx.update(|cx| authenticate_model_provider(model_provider_id.clone(), cx)) cx.update(|cx| authenticate_model_provider(model_provider_id.clone(), cx))
.unwrap() .unwrap()

View file

@ -77,8 +77,8 @@ impl SlashCommand for AutoCommand {
let cx: &mut App = cx; let cx: &mut App = cx;
cx.spawn(|cx: gpui::AsyncApp| async move { cx.spawn(async move |cx| {
let task = project_index.read_with(&cx, |project_index, cx| { let task = project_index.read_with(cx, |project_index, cx| {
project_index.flush_summary_backlogs(cx) project_index.flush_summary_backlogs(cx)
})?; })?;
@ -117,9 +117,9 @@ impl SlashCommand for AutoCommand {
return Task::ready(Err(anyhow!("no project indexer"))); return Task::ready(Err(anyhow!("no project indexer")));
}; };
let task = window.spawn(cx, |cx| async move { let task = window.spawn(cx, async move |cx| {
let summaries = project_index let summaries = project_index
.read_with(&cx, |project_index, cx| project_index.all_summaries(cx))? .read_with(cx, |project_index, cx| project_index.all_summaries(cx))?
.await?; .await?;
commands_for_summaries(&summaries, &original_prompt, &cx).await commands_for_summaries(&summaries, &original_prompt, &cx).await

View file

@ -186,7 +186,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx); let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
window.spawn(cx, move |_| async move { window.spawn(cx, async move |_| {
task.await? task.await?
.map(|output| output.to_event_stream()) .map(|output| output.to_event_stream())
.ok_or_else(|| anyhow!("No diagnostics found")) .ok_or_else(|| anyhow!("No diagnostics found"))
@ -268,7 +268,7 @@ fn collect_diagnostics(
}) })
.collect(); .collect();
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let mut output = SlashCommandOutput::default(); let mut output = SlashCommandOutput::default();
if let Some(error_source) = error_source.as_ref() { if let Some(error_source) = error_source.as_ref() {
@ -299,7 +299,7 @@ fn collect_diagnostics(
} }
if let Some(buffer) = project_handle if let Some(buffer) = project_handle
.update(&mut cx, |project, cx| project.open_buffer(project_path, cx))? .update(cx, |project, cx| project.open_buffer(project_path, cx))?
.await .await
.log_err() .log_err()
{ {

View file

@ -241,7 +241,7 @@ fn collect_files(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (events_tx, events_rx) = mpsc::unbounded(); let (events_tx, events_rx) = mpsc::unbounded();
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
for snapshot in snapshots { for snapshot in snapshots {
let worktree_id = snapshot.id(); let worktree_id = snapshot.id();
let mut directory_stack: Vec<Arc<Path>> = Vec::new(); let mut directory_stack: Vec<Arc<Path>> = Vec::new();
@ -352,7 +352,7 @@ fn collect_files(
)))?; )))?;
} else if entry.is_file() { } else if entry.is_file() {
let Some(open_buffer_task) = project_handle let Some(open_buffer_task) = project_handle
.update(&mut cx, |project, cx| { .update(cx, |project, cx| {
project.open_buffer((worktree_id, &entry.path), cx) project.open_buffer((worktree_id, &entry.path), cx)
}) })
.ok() .ok()
@ -361,7 +361,7 @@ fn collect_files(
}; };
if let Some(buffer) = open_buffer_task.await.log_err() { if let Some(buffer) = open_buffer_task.await.log_err() {
let mut output = SlashCommandOutput::default(); let mut output = SlashCommandOutput::default();
let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?; let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
append_buffer_to_output( append_buffer_to_output(
&snapshot, &snapshot,
Some(&path_including_worktree_name), Some(&path_including_worktree_name),

View file

@ -99,7 +99,7 @@ impl SlashCommand for ProjectSlashCommand {
return Task::ready(Err(anyhow::anyhow!("no project indexer"))); return Task::ready(Err(anyhow::anyhow!("no project indexer")));
}; };
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let current_model = current_model.ok_or_else(|| anyhow!("no model selected"))?; let current_model = current_model.ok_or_else(|| anyhow!("no model selected"))?;
let prompt = let prompt =
@ -123,7 +123,7 @@ impl SlashCommand for ProjectSlashCommand {
.search_queries; .search_queries;
let results = project_index let results = project_index
.read_with(&cx, |project_index, cx| { .read_with(cx, |project_index, cx| {
project_index.search(search_queries.clone(), 25, cx) project_index.search(search_queries.clone(), 25, cx)
})? })?
.await?; .await?;

View file

@ -109,9 +109,9 @@ impl SlashCommand for SearchSlashCommand {
return Task::ready(Err(anyhow::anyhow!("no project indexer"))); return Task::ready(Err(anyhow::anyhow!("no project indexer")));
}; };
window.spawn(cx, |cx| async move { window.spawn(cx, async move |cx| {
let results = project_index let results = project_index
.read_with(&cx, |project_index, cx| { .read_with(cx, |project_index, cx| {
project_index.search(vec![query.clone()], limit.unwrap_or(5), cx) project_index.search(vec![query.clone()], limit.unwrap_or(5), cx)
})? })?
.await?; .await?;

View file

@ -86,7 +86,7 @@ impl SlashCommand for TabSlashCommand {
tab_items_for_queries(workspace, &[current_query], cancel, false, window, cx); tab_items_for_queries(workspace, &[current_query], cancel, false, window, cx);
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId); let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
window.spawn(cx, |_| async move { window.spawn(cx, async move |_| {
let tab_items = tab_items_search.await?; let tab_items = tab_items_search.await?;
let run_command = tab_items.len() == 1; let run_command = tab_items.len() == 1;
let tab_completion_items = tab_items.into_iter().filter_map(|(path, ..)| { let tab_completion_items = tab_items.into_iter().filter_map(|(path, ..)| {
@ -172,11 +172,11 @@ fn tab_items_for_queries(
) -> Task<anyhow::Result<Vec<(Option<PathBuf>, BufferSnapshot, usize)>>> { ) -> Task<anyhow::Result<Vec<(Option<PathBuf>, BufferSnapshot, usize)>>> {
let empty_query = queries.is_empty() || queries.iter().all(|query| query.trim().is_empty()); let empty_query = queries.is_empty() || queries.iter().all(|query| query.trim().is_empty());
let queries = queries.to_owned(); let queries = queries.to_owned();
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let mut open_buffers = let mut open_buffers =
workspace workspace
.context("no workspace")? .context("no workspace")?
.update(&mut cx, |workspace, cx| { .update(cx, |workspace, cx| {
if strict_match && empty_query { if strict_match && empty_query {
let snapshot = active_item_buffer(workspace, cx)?; let snapshot = active_item_buffer(workspace, cx)?;
let full_path = snapshot.resolve_file_path(cx, true); let full_path = snapshot.resolve_file_path(cx, true);

View file

@ -50,7 +50,7 @@ impl Tool for BashTool {
}; };
let working_directory = worktree.read(cx).abs_path(); let working_directory = worktree.read(cx).abs_path();
cx.spawn(|_| async move { cx.spawn(async move |_| {
// Add 2>&1 to merge stderr into stdout for proper interleaving. // Add 2>&1 to merge stderr into stdout for proper interleaving.
let command = format!("({}) 2>&1", input.command); let command = format!("({}) 2>&1", input.command);

View file

@ -65,10 +65,10 @@ impl Tool for DiagnosticsTool {
}; };
let buffer = project.update(cx, |project, cx| project.open_buffer(project_path, cx)); let buffer = project.update(cx, |project, cx| project.open_buffer(project_path, cx));
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
let mut output = String::new(); let mut output = String::new();
let buffer = buffer.await?; let buffer = buffer.await?;
let snapshot = buffer.read_with(&cx, |buffer, _cx| buffer.snapshot())?; let snapshot = buffer.read_with(cx, |buffer, _cx| buffer.snapshot())?;
for (_, group) in snapshot.diagnostic_groups(None) { for (_, group) in snapshot.diagnostic_groups(None) {
let entry = &group.entries[group.primary_ix]; let entry = &group.entries[group.primary_ix];

View file

@ -103,7 +103,7 @@ impl Tool for EditFilesTool {
cx, cx,
); );
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let result = task.await; let result = task.await;
let str_result = match &result { let str_result = match &result {
@ -111,10 +111,8 @@ impl Tool for EditFilesTool {
Err(err) => Err(err.to_string()), Err(err) => Err(err.to_string()),
}; };
log.update(&mut cx, |log, cx| { log.update(cx, |log, cx| log.set_tool_output(req_id, str_result, cx))
log.set_tool_output(req_id, str_result, cx) .log_err();
})
.log_err();
result result
}) })
@ -188,7 +186,7 @@ impl EditToolRequest {
cache: false, cache: false,
}); });
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let llm_request = LanguageModelRequest { let llm_request = LanguageModelRequest {
messages, messages,
tools: vec![], tools: vec![],
@ -211,10 +209,10 @@ impl EditToolRequest {
}; };
while let Some(chunk) = chunks.stream.next().await { while let Some(chunk) = chunks.stream.next().await {
request.process_response_chunk(&chunk?, &mut cx).await?; request.process_response_chunk(&chunk?, cx).await?;
} }
request.finalize(&mut cx).await request.finalize(cx).await
}) })
} }

View file

@ -70,14 +70,14 @@ impl Tool for ReadFileTool {
return Task::ready(Err(anyhow!("Path not found in project"))); return Task::ready(Err(anyhow!("Path not found in project")));
}; };
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let buffer = cx let buffer = cx
.update(|cx| { .update(|cx| {
project.update(cx, |project, cx| project.open_buffer(project_path, cx)) project.update(cx, |project, cx| project.open_buffer(project_path, cx))
})? })?
.await?; .await?;
let result = buffer.read_with(&cx, |buffer, _cx| { let result = buffer.read_with(cx, |buffer, _cx| {
if buffer if buffer
.file() .file()
.map_or(false, |file| file.disk_state().exists()) .map_or(false, |file| file.disk_state().exists())
@ -102,7 +102,7 @@ impl Tool for ReadFileTool {
} }
})??; })??;
action_log.update(&mut cx, |log, cx| { action_log.update(cx, |log, cx| {
log.buffer_read(buffer, cx); log.buffer_read(buffer, cx);
})?; })?;

View file

@ -73,7 +73,7 @@ impl Tool for RegexSearchTool {
let results = project.update(cx, |project, cx| project.search(query, cx)); let results = project.update(cx, |project, cx| project.search(query, cx));
cx.spawn(|cx| async move { cx.spawn(async move|cx| {
futures::pin_mut!(results); futures::pin_mut!(results);
let mut output = String::new(); let mut output = String::new();
@ -86,7 +86,7 @@ impl Tool for RegexSearchTool {
continue; continue;
} }
buffer.read_with(&cx, |buffer, cx| -> Result<(), anyhow::Error> { buffer.read_with(cx, |buffer, cx| -> Result<(), anyhow::Error> {
if let Some(path) = buffer.file().map(|file| file.full_path(cx)) { if let Some(path) = buffer.file().map(|file| file.full_path(cx)) {
let mut file_header_written = false; let mut file_header_written = false;
let mut ranges = ranges let mut ranges = ranges

View file

@ -252,11 +252,9 @@ impl AutoUpdater {
} }
pub fn start_polling(&self, cx: &mut Context<Self>) -> Task<Result<()>> { pub fn start_polling(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| loop {
loop { this.update(cx, |this, cx| this.poll(cx))?;
this.update(&mut cx, |this, cx| this.poll(cx))?; cx.background_executor().timer(POLL_INTERVAL).await;
cx.background_executor().timer(POLL_INTERVAL).await;
}
}) })
} }
@ -267,9 +265,9 @@ impl AutoUpdater {
cx.notify(); cx.notify();
self.pending_poll = Some(cx.spawn(|this, mut cx| async move { self.pending_poll = Some(cx.spawn(async move |this, cx| {
let result = Self::update(this.upgrade()?, cx.clone()).await; let result = Self::update(this.upgrade()?, cx.clone()).await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.pending_poll = None; this.pending_poll = None;
if let Err(error) = result { if let Err(error) = result {
log::error!("auto-update failed: error:{:?}", error); log::error!("auto-update failed: error:{:?}", error);

View file

@ -64,7 +64,7 @@ fn view_release_notes_locally(
workspace workspace
.with_local_workspace(window, cx, move |_, window, cx| { .with_local_workspace(window, cx, move |_, window, cx| {
cx.spawn_in(window, |workspace, mut cx| async move { cx.spawn_in(window, async move |workspace, cx| {
let markdown = markdown.await.log_err(); let markdown = markdown.await.log_err();
let response = client.get(&url, Default::default(), true).await; let response = client.get(&url, Default::default(), true).await;
let Some(mut response) = response.log_err() else { let Some(mut response) = response.log_err() else {
@ -79,7 +79,7 @@ fn view_release_notes_locally(
if let Ok(body) = body { if let Ok(body) = body {
workspace workspace
.update_in(&mut cx, |workspace, window, cx| { .update_in(cx, |workspace, window, cx| {
let project = workspace.project().clone(); let project = workspace.project().clone();
let buffer = project.update(cx, |project, cx| { let buffer = project.update(cx, |project, cx| {
project.create_local_buffer("", markdown, cx) project.create_local_buffer("", markdown, cx)
@ -130,7 +130,7 @@ pub fn notify_if_app_was_updated(cx: &mut App) {
return; return;
}; };
let should_show_notification = updater.read(cx).should_show_update_notification(cx); let should_show_notification = updater.read(cx).should_show_update_notification(cx);
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
let should_show_notification = should_show_notification.await?; let should_show_notification = should_show_notification.await?;
if should_show_notification { if should_show_notification {
cx.update(|cx| { cx.update(|cx| {

View file

@ -1080,12 +1080,12 @@ impl BufferDiff {
let complete_on_drop = util::defer(|| { let complete_on_drop = util::defer(|| {
tx.send(()).ok(); tx.send(()).ok();
}); });
cx.spawn(|_, mut cx| async move { cx.spawn(async move |_, cx| {
let snapshot = snapshot.await; let snapshot = snapshot.await;
let Some(this) = this.upgrade() else { let Some(this) = this.upgrade() else {
return; return;
}; };
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.set_state(snapshot, &buffer); this.set_state(snapshot, &buffer);
}) })
.log_err(); .log_err();

View file

@ -54,10 +54,10 @@ impl OneAtATime {
{ {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
self.cancel.replace(tx); self.cancel.replace(tx);
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
futures::select_biased! { futures::select_biased! {
_ = rx.fuse() => Ok(None), _ = rx.fuse() => Ok(None),
result = f(cx).fuse() => result.map(Some), result = f(cx.clone()).fuse() => result.map(Some),
} }
}) })
} }
@ -192,19 +192,19 @@ impl ActiveCall {
}; };
let invite = if let Some(room) = room { let invite = if let Some(room) = room {
cx.spawn(move |_, mut cx| async move { cx.spawn(async move |_, cx| {
let room = room.await.map_err(|err| anyhow!("{:?}", err))?; let room = room.await.map_err(|err| anyhow!("{:?}", err))?;
let initial_project_id = if let Some(initial_project) = initial_project { let initial_project_id = if let Some(initial_project) = initial_project {
Some( Some(
room.update(&mut cx, |room, cx| room.share_project(initial_project, cx))? room.update(cx, |room, cx| room.share_project(initial_project, cx))?
.await?, .await?,
) )
} else { } else {
None None
}; };
room.update(&mut cx, move |room, cx| { room.update(cx, move |room, cx| {
room.call(called_user_id, initial_project_id, cx) room.call(called_user_id, initial_project_id, cx)
})? })?
.await?; .await?;
@ -215,7 +215,7 @@ impl ActiveCall {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let room = cx let room = cx
.spawn(move |this, mut cx| async move { .spawn(async move |this, cx| {
let create_room = async { let create_room = async {
let room = cx let room = cx
.update(|cx| { .update(|cx| {
@ -229,14 +229,14 @@ impl ActiveCall {
})? })?
.await?; .await?;
this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx))? this.update(cx, |this, cx| this.set_room(Some(room.clone()), cx))?
.await?; .await?;
anyhow::Ok(room) anyhow::Ok(room)
}; };
let room = create_room.await; let room = create_room.await;
this.update(&mut cx, |this, _| this.pending_room_creation = None)?; this.update(cx, |this, _| this.pending_room_creation = None)?;
room.map_err(Arc::new) room.map_err(Arc::new)
}) })
.shared(); .shared();
@ -247,10 +247,10 @@ impl ActiveCall {
}) })
}; };
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let result = invite.await; let result = invite.await;
if result.is_ok() { if result.is_ok() {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.report_call_event("Participant Invited", cx) this.report_call_event("Participant Invited", cx)
})?; })?;
} else { } else {
@ -258,7 +258,7 @@ impl ActiveCall {
log::error!("invite failed: {:?}", result); log::error!("invite failed: {:?}", result);
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.pending_invites.remove(&called_user_id); this.pending_invites.remove(&called_user_id);
cx.notify(); cx.notify();
})?; })?;
@ -315,11 +315,11 @@ impl ActiveCall {
._join_debouncer ._join_debouncer
.spawn(cx, move |cx| Room::join(room_id, client, user_store, cx)); .spawn(cx, move |cx| Room::join(room_id, client, user_store, cx));
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let room = join.await?; let room = join.await?;
this.update(&mut cx, |this, cx| this.set_room(room.clone(), cx))? this.update(cx, |this, cx| this.set_room(room.clone(), cx))?
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.report_call_event("Incoming Call Accepted", cx) this.report_call_event("Incoming Call Accepted", cx)
})?; })?;
Ok(()) Ok(())
@ -363,13 +363,11 @@ impl ActiveCall {
Room::join_channel(channel_id, client, user_store, cx).await Room::join_channel(channel_id, client, user_store, cx).await
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let room = join.await?; let room = join.await?;
this.update(&mut cx, |this, cx| this.set_room(room.clone(), cx))? this.update(cx, |this, cx| this.set_room(room.clone(), cx))?
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| this.report_call_event("Channel Joined", cx))?;
this.report_call_event("Channel Joined", cx)
})?;
Ok(room) Ok(room)
}) })
} }

View file

@ -128,7 +128,11 @@ impl Room {
let maintain_connection = cx.spawn({ let maintain_connection = cx.spawn({
let client = client.clone(); let client = client.clone();
move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err() async move |this, cx| {
Self::maintain_connection(this, client.clone(), cx)
.log_err()
.await
}
}); });
Audio::play_sound(Sound::Joined, cx); Audio::play_sound(Sound::Joined, cx);
@ -172,7 +176,7 @@ impl Room {
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
cx: &mut App, cx: &mut App,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
cx.spawn(move |mut cx| async move { cx.spawn(async move |cx| {
let response = client.request(proto::CreateRoom {}).await?; let response = client.request(proto::CreateRoom {}).await?;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
let room = cx.new(|cx| { let room = cx.new(|cx| {
@ -192,7 +196,7 @@ impl Room {
let initial_project_id = if let Some(initial_project) = initial_project { let initial_project_id = if let Some(initial_project) = initial_project {
let initial_project_id = room let initial_project_id = room
.update(&mut cx, |room, cx| { .update(cx, |room, cx| {
room.share_project(initial_project.clone(), cx) room.share_project(initial_project.clone(), cx)
})? })?
.await?; .await?;
@ -202,7 +206,7 @@ impl Room {
}; };
let did_join = room let did_join = room
.update(&mut cx, |room, cx| { .update(cx, |room, cx| {
room.leave_when_empty = true; room.leave_when_empty = true;
room.call(called_user_id, initial_project_id, cx) room.call(called_user_id, initial_project_id, cx)
})? })?
@ -358,7 +362,7 @@ impl Room {
async fn maintain_connection( async fn maintain_connection(
this: WeakEntity<Self>, this: WeakEntity<Self>,
client: Arc<Client>, client: Arc<Client>,
mut cx: AsyncApp, cx: &mut AsyncApp,
) -> Result<()> { ) -> Result<()> {
let mut client_status = client.status(); let mut client_status = client.status();
loop { loop {
@ -370,7 +374,7 @@ impl Room {
this.upgrade() this.upgrade()
.ok_or_else(|| anyhow!("room was dropped"))? .ok_or_else(|| anyhow!("room was dropped"))?
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.status = RoomStatus::Rejoining; this.status = RoomStatus::Rejoining;
cx.notify(); cx.notify();
})?; })?;
@ -386,7 +390,7 @@ impl Room {
log::info!("client reconnected, attempting to rejoin room"); log::info!("client reconnected, attempting to rejoin room");
let Some(this) = this.upgrade() else { break }; let Some(this) = this.upgrade() else { break };
match this.update(&mut cx, |this, cx| this.rejoin(cx)) { match this.update(cx, |this, cx| this.rejoin(cx)) {
Ok(task) => { Ok(task) => {
if task.await.log_err().is_some() { if task.await.log_err().is_some() {
return true; return true;
@ -435,7 +439,7 @@ impl Room {
// we leave the room and return an error. // we leave the room and return an error.
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
log::info!("reconnection failed, leaving room"); log::info!("reconnection failed, leaving room");
this.update(&mut cx, |this, cx| this.leave(cx))?.await?; this.update(cx, |this, cx| this.leave(cx))?.await?;
} }
Err(anyhow!( Err(anyhow!(
"can't reconnect to room: client failed to re-establish connection" "can't reconnect to room: client failed to re-establish connection"
@ -490,12 +494,12 @@ impl Room {
rejoined_projects, rejoined_projects,
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = response.await?; let response = response.await?;
let message_id = response.message_id; let message_id = response.message_id;
let response = response.payload; let response = response.payload;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.status = RoomStatus::Online; this.status = RoomStatus::Online;
this.apply_room_update(room_proto, cx)?; this.apply_room_update(room_proto, cx)?;
@ -577,7 +581,7 @@ impl Room {
let client = self.client.clone(); let client = self.client.clone();
let room_id = self.id; let room_id = self.id;
let role = role.into(); let role = role.into();
cx.spawn(|_, _| async move { cx.spawn(async move |_, _| {
client client
.request(proto::SetRoomParticipantRole { .request(proto::SetRoomParticipantRole {
room_id, room_id,
@ -709,11 +713,11 @@ impl Room {
user_store.get_users(pending_participant_user_ids, cx), user_store.get_users(pending_participant_user_ids, cx),
) )
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let (remote_participants, pending_participants) = let (remote_participants, pending_participants) =
futures::join!(remote_participants, pending_participants); futures::join!(remote_participants, pending_participants);
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.participant_user_ids.clear(); this.participant_user_ids.clear();
if let Some(participant) = local_participant { if let Some(participant) = local_participant {
@ -1116,7 +1120,7 @@ impl Room {
let client = self.client.clone(); let client = self.client.clone();
let room_id = self.id; let room_id = self.id;
self.pending_call_count += 1; self.pending_call_count += 1;
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let result = client let result = client
.request(proto::Call { .request(proto::Call {
room_id, room_id,
@ -1124,7 +1128,7 @@ impl Room {
initial_project_id, initial_project_id,
}) })
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.pending_call_count -= 1; this.pending_call_count -= 1;
if this.should_leave() { if this.should_leave() {
this.leave(cx).detach_and_log_err(cx); this.leave(cx).detach_and_log_err(cx);
@ -1145,11 +1149,11 @@ impl Room {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
cx.emit(Event::RemoteProjectJoined { project_id: id }); cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let project = let project =
Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?; Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.joined_projects.retain(|project| { this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() { if let Some(project) = project.upgrade() {
!project.read(cx).is_disconnected(cx) !project.read(cx).is_disconnected(cx)
@ -1178,15 +1182,13 @@ impl Room {
is_ssh_project: project.read(cx).is_via_ssh(), is_ssh_project: project.read(cx).is_via_ssh(),
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = request.await?; let response = request.await?;
project.update(&mut cx, |project, cx| { project.update(cx, |project, cx| project.shared(response.project_id, cx))??;
project.shared(response.project_id, cx)
})??;
// If the user's location is in this project, it changes from UnsharedProject to SharedProject. // If the user's location is in this project, it changes from UnsharedProject to SharedProject.
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.shared_projects.insert(project.downgrade()); this.shared_projects.insert(project.downgrade());
let active_project = this.local_participant.active_project.as_ref(); let active_project = this.local_participant.active_project.as_ref();
if active_project.map_or(false, |location| *location == project) { if active_project.map_or(false, |location| *location == project) {
@ -1342,7 +1344,7 @@ impl Room {
return Task::ready(Err(anyhow!("live-kit was not initialized"))); return Task::ready(Err(anyhow!("live-kit was not initialized")));
}; };
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let (track, stream) = capture_local_audio_track(cx.background_executor())?.await; let (track, stream) = capture_local_audio_track(cx.background_executor())?.await;
let publication = participant let publication = participant
@ -1355,7 +1357,7 @@ impl Room {
) )
.await .await
.map_err(|error| anyhow!("failed to publish track: {error}")); .map_err(|error| anyhow!("failed to publish track: {error}"));
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let live_kit = this let live_kit = this
.live_kit .live_kit
.as_mut() .as_mut()
@ -1428,7 +1430,7 @@ impl Room {
let sources = cx.screen_capture_sources(); let sources = cx.screen_capture_sources();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let sources = sources.await??; let sources = sources.await??;
let source = sources.first().ok_or_else(|| anyhow!("no display found"))?; let source = sources.first().ok_or_else(|| anyhow!("no display found"))?;
@ -1446,7 +1448,7 @@ impl Room {
.await .await
.map_err(|error| anyhow!("error publishing screen track {error:?}")); .map_err(|error| anyhow!("error publishing screen track {error:?}"));
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let live_kit = this let live_kit = this
.live_kit .live_kit
.as_mut() .as_mut()
@ -1639,7 +1641,7 @@ fn spawn_room_connection(
cx: &mut Context<'_, Room>, cx: &mut Context<'_, Room>,
) { ) {
if let Some(connection_info) = livekit_connection_info { if let Some(connection_info) = livekit_connection_info {
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let (room, mut events) = livekit::Room::connect( let (room, mut events) = livekit::Room::connect(
&connection_info.server_url, &connection_info.server_url,
&connection_info.token, &connection_info.token,
@ -1647,11 +1649,11 @@ fn spawn_room_connection(
) )
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let _handle_updates = cx.spawn(|this, mut cx| async move { let _handle_updates = cx.spawn(async move |this, cx| {
while let Some(event) = events.recv().await { while let Some(event) = events.recv().await {
if this if this
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.livekit_room_updated(event, cx).warn_on_err(); this.livekit_room_updated(event, cx).warn_on_err();
}) })
.is_err() .is_err()

View file

@ -47,10 +47,10 @@ impl OneAtATime {
{ {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
self.cancel.replace(tx); self.cancel.replace(tx);
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
futures::select_biased! { futures::select_biased! {
_ = rx.fuse() => Ok(None), _ = rx.fuse() => Ok(None),
result = f(cx).fuse() => result.map(Some), result = f(cx.clone()).fuse() => result.map(Some),
} }
}) })
} }
@ -185,19 +185,19 @@ impl ActiveCall {
}; };
let invite = if let Some(room) = room { let invite = if let Some(room) = room {
cx.spawn(move |_, mut cx| async move { cx.spawn(async move |_, cx| {
let room = room.await.map_err(|err| anyhow!("{:?}", err))?; let room = room.await.map_err(|err| anyhow!("{:?}", err))?;
let initial_project_id = if let Some(initial_project) = initial_project { let initial_project_id = if let Some(initial_project) = initial_project {
Some( Some(
room.update(&mut cx, |room, cx| room.share_project(initial_project, cx))? room.update(cx, |room, cx| room.share_project(initial_project, cx))?
.await?, .await?,
) )
} else { } else {
None None
}; };
room.update(&mut cx, move |room, cx| { room.update(cx, move |room, cx| {
room.call(called_user_id, initial_project_id, cx) room.call(called_user_id, initial_project_id, cx)
})? })?
.await?; .await?;
@ -208,7 +208,7 @@ impl ActiveCall {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let room = cx let room = cx
.spawn(move |this, mut cx| async move { .spawn(async move |this, cx| {
let create_room = async { let create_room = async {
let room = cx let room = cx
.update(|cx| { .update(|cx| {
@ -222,14 +222,14 @@ impl ActiveCall {
})? })?
.await?; .await?;
this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx))? this.update(cx, |this, cx| this.set_room(Some(room.clone()), cx))?
.await?; .await?;
anyhow::Ok(room) anyhow::Ok(room)
}; };
let room = create_room.await; let room = create_room.await;
this.update(&mut cx, |this, _| this.pending_room_creation = None)?; this.update(cx, |this, _| this.pending_room_creation = None)?;
room.map_err(Arc::new) room.map_err(Arc::new)
}) })
.shared(); .shared();
@ -240,10 +240,10 @@ impl ActiveCall {
}) })
}; };
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let result = invite.await; let result = invite.await;
if result.is_ok() { if result.is_ok() {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.report_call_event("Participant Invited", cx) this.report_call_event("Participant Invited", cx)
})?; })?;
} else { } else {
@ -251,7 +251,7 @@ impl ActiveCall {
log::error!("invite failed: {:?}", result); log::error!("invite failed: {:?}", result);
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.pending_invites.remove(&called_user_id); this.pending_invites.remove(&called_user_id);
cx.notify(); cx.notify();
})?; })?;
@ -304,15 +304,15 @@ impl ActiveCall {
let room_id = call.room_id; let room_id = call.room_id;
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let join = self let join = self._join_debouncer.spawn(cx, move |mut cx| async move {
._join_debouncer Room::join(room_id, client, user_store, &mut cx).await
.spawn(cx, move |cx| Room::join(room_id, client, user_store, cx)); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let room = join.await?; let room = join.await?;
this.update(&mut cx, |this, cx| this.set_room(room.clone(), cx))? this.update(cx, |this, cx| this.set_room(room.clone(), cx))?
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.report_call_event("Incoming Call Accepted", cx) this.report_call_event("Incoming Call Accepted", cx)
})?; })?;
Ok(()) Ok(())
@ -352,17 +352,15 @@ impl ActiveCall {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let join = self._join_debouncer.spawn(cx, move |cx| async move { let join = self._join_debouncer.spawn(cx, move |mut cx| async move {
Room::join_channel(channel_id, client, user_store, cx).await Room::join_channel(channel_id, client, user_store, &mut cx).await
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let room = join.await?; let room = join.await?;
this.update(&mut cx, |this, cx| this.set_room(room.clone(), cx))? this.update(cx, |this, cx| this.set_room(room.clone(), cx))?
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| this.report_call_event("Channel Joined", cx))?;
this.report_call_event("Channel Joined", cx)
})?;
Ok(room) Ok(room)
}) })
} }

View file

@ -115,7 +115,7 @@ impl Room {
let mut status = room.status(); let mut status = room.status();
// Consume the initial status of the room. // Consume the initial status of the room.
let _ = status.try_recv(); let _ = status.try_recv();
let _maintain_room = cx.spawn(|this, mut cx| async move { let _maintain_room = cx.spawn(async move |this, cx| {
while let Some(status) = status.next().await { while let Some(status) = status.next().await {
let this = if let Some(this) = this.upgrade() { let this = if let Some(this) = this.upgrade() {
this this
@ -124,8 +124,7 @@ impl Room {
}; };
if status == livekit_client_macos::ConnectionState::Disconnected { if status == livekit_client_macos::ConnectionState::Disconnected {
this.update(&mut cx, |this, cx| this.leave(cx).log_err()) this.update(cx, |this, cx| this.leave(cx).log_err()).ok();
.ok();
break; break;
} }
} }
@ -133,7 +132,7 @@ impl Room {
let _handle_updates = cx.spawn({ let _handle_updates = cx.spawn({
let room = room.clone(); let room = room.clone();
move |this, mut cx| async move { async move |this, cx| {
let mut updates = room.updates(); let mut updates = room.updates();
while let Some(update) = updates.next().await { while let Some(update) = updates.next().await {
let this = if let Some(this) = this.upgrade() { let this = if let Some(this) = this.upgrade() {
@ -142,7 +141,7 @@ impl Room {
break; break;
}; };
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.live_kit_room_updated(update, cx).log_err() this.live_kit_room_updated(update, cx).log_err()
}) })
.ok(); .ok();
@ -151,9 +150,9 @@ impl Room {
}); });
let connect = room.connect(&connection_info.server_url, &connection_info.token); let connect = room.connect(&connection_info.server_url, &connection_info.token);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
connect.await?; connect.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if this.can_use_microphone() { if this.can_use_microphone() {
if let Some(live_kit) = &this.live_kit { if let Some(live_kit) = &this.live_kit {
if !live_kit.muted_by_user && !live_kit.deafened { if !live_kit.muted_by_user && !live_kit.deafened {
@ -184,7 +183,11 @@ impl Room {
let maintain_connection = cx.spawn({ let maintain_connection = cx.spawn({
let client = client.clone(); let client = client.clone();
move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err() async move |this, cx| {
Self::maintain_connection(this, client.clone(), cx)
.log_err()
.await
}
}); });
Audio::play_sound(Sound::Joined, cx); Audio::play_sound(Sound::Joined, cx);
@ -228,7 +231,7 @@ impl Room {
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
cx: &mut App, cx: &mut App,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
cx.spawn(move |mut cx| async move { cx.spawn(async move |cx| {
let response = client.request(proto::CreateRoom {}).await?; let response = client.request(proto::CreateRoom {}).await?;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
let room = cx.new(|cx| { let room = cx.new(|cx| {
@ -248,7 +251,7 @@ impl Room {
let initial_project_id = if let Some(initial_project) = initial_project { let initial_project_id = if let Some(initial_project) = initial_project {
let initial_project_id = room let initial_project_id = room
.update(&mut cx, |room, cx| { .update(cx, |room, cx| {
room.share_project(initial_project.clone(), cx) room.share_project(initial_project.clone(), cx)
})? })?
.await?; .await?;
@ -258,7 +261,7 @@ impl Room {
}; };
let did_join = room let did_join = room
.update(&mut cx, |room, cx| { .update(cx, |room, cx| {
room.leave_when_empty = true; room.leave_when_empty = true;
room.call(called_user_id, initial_project_id, cx) room.call(called_user_id, initial_project_id, cx)
})? })?
@ -274,7 +277,7 @@ impl Room {
channel_id: ChannelId, channel_id: ChannelId,
client: Arc<Client>, client: Arc<Client>,
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
cx: AsyncApp, cx: &mut AsyncApp,
) -> Result<Entity<Self>> { ) -> Result<Entity<Self>> {
Self::from_join_response( Self::from_join_response(
client client
@ -292,7 +295,7 @@ impl Room {
room_id: u64, room_id: u64,
client: Arc<Client>, client: Arc<Client>,
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
cx: AsyncApp, cx: &mut AsyncApp,
) -> Result<Entity<Self>> { ) -> Result<Entity<Self>> {
Self::from_join_response( Self::from_join_response(
client.request(proto::JoinRoom { id: room_id }).await?, client.request(proto::JoinRoom { id: room_id }).await?,
@ -333,7 +336,7 @@ impl Room {
response: proto::JoinRoomResponse, response: proto::JoinRoomResponse,
client: Arc<Client>, client: Arc<Client>,
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
mut cx: AsyncApp, cx: &mut AsyncApp,
) -> Result<Entity<Self>> { ) -> Result<Entity<Self>> {
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
let room = cx.new(|cx| { let room = cx.new(|cx| {
@ -346,7 +349,7 @@ impl Room {
cx, cx,
) )
})?; })?;
room.update(&mut cx, |room, cx| { room.update(cx, |room, cx| {
room.leave_when_empty = room.channel_id.is_none(); room.leave_when_empty = room.channel_id.is_none();
room.apply_room_update(room_proto, cx)?; room.apply_room_update(room_proto, cx)?;
anyhow::Ok(()) anyhow::Ok(())
@ -414,7 +417,7 @@ impl Room {
async fn maintain_connection( async fn maintain_connection(
this: WeakEntity<Self>, this: WeakEntity<Self>,
client: Arc<Client>, client: Arc<Client>,
mut cx: AsyncApp, cx: &mut AsyncApp,
) -> Result<()> { ) -> Result<()> {
let mut client_status = client.status(); let mut client_status = client.status();
loop { loop {
@ -426,7 +429,7 @@ impl Room {
this.upgrade() this.upgrade()
.ok_or_else(|| anyhow!("room was dropped"))? .ok_or_else(|| anyhow!("room was dropped"))?
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.status = RoomStatus::Rejoining; this.status = RoomStatus::Rejoining;
cx.notify(); cx.notify();
})?; })?;
@ -442,7 +445,7 @@ impl Room {
log::info!("client reconnected, attempting to rejoin room"); log::info!("client reconnected, attempting to rejoin room");
let Some(this) = this.upgrade() else { break }; let Some(this) = this.upgrade() else { break };
match this.update(&mut cx, |this, cx| this.rejoin(cx)) { match this.update(cx, |this, cx| this.rejoin(cx)) {
Ok(task) => { Ok(task) => {
if task.await.log_err().is_some() { if task.await.log_err().is_some() {
return true; return true;
@ -491,7 +494,7 @@ impl Room {
// we leave the room and return an error. // we leave the room and return an error.
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
log::info!("reconnection failed, leaving room"); log::info!("reconnection failed, leaving room");
this.update(&mut cx, |this, cx| this.leave(cx))?.await?; this.update(cx, |this, cx| this.leave(cx))?.await?;
} }
Err(anyhow!( Err(anyhow!(
"can't reconnect to room: client failed to re-establish connection" "can't reconnect to room: client failed to re-establish connection"
@ -546,12 +549,12 @@ impl Room {
rejoined_projects, rejoined_projects,
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = response.await?; let response = response.await?;
let message_id = response.message_id; let message_id = response.message_id;
let response = response.payload; let response = response.payload;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.status = RoomStatus::Online; this.status = RoomStatus::Online;
this.apply_room_update(room_proto, cx)?; this.apply_room_update(room_proto, cx)?;
@ -633,7 +636,7 @@ impl Room {
let client = self.client.clone(); let client = self.client.clone();
let room_id = self.id; let room_id = self.id;
let role = role.into(); let role = role.into();
cx.spawn(|_, _| async move { cx.spawn(async move |_, _| {
client client
.request(proto::SetRoomParticipantRole { .request(proto::SetRoomParticipantRole {
room_id, room_id,
@ -736,11 +739,11 @@ impl Room {
) )
}); });
self.pending_room_update = Some(cx.spawn(|this, mut cx| async move { self.pending_room_update = Some(cx.spawn(async move |this, cx| {
let (remote_participants, pending_participants) = let (remote_participants, pending_participants) =
futures::join!(remote_participants, pending_participants); futures::join!(remote_participants, pending_participants);
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.participant_user_ids.clear(); this.participant_user_ids.clear();
if let Some(participant) = local_participant { if let Some(participant) = local_participant {
@ -1136,7 +1139,7 @@ impl Room {
let client = self.client.clone(); let client = self.client.clone();
let room_id = self.id; let room_id = self.id;
self.pending_call_count += 1; self.pending_call_count += 1;
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let result = client let result = client
.request(proto::Call { .request(proto::Call {
room_id, room_id,
@ -1144,7 +1147,7 @@ impl Room {
initial_project_id, initial_project_id,
}) })
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.pending_call_count -= 1; this.pending_call_count -= 1;
if this.should_leave() { if this.should_leave() {
this.leave(cx).detach_and_log_err(cx); this.leave(cx).detach_and_log_err(cx);
@ -1165,11 +1168,11 @@ impl Room {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
cx.emit(Event::RemoteProjectJoined { project_id: id }); cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let project = let project =
Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?; Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.joined_projects.retain(|project| { this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() { if let Some(project) = project.upgrade() {
!project.read(cx).is_disconnected(cx) !project.read(cx).is_disconnected(cx)
@ -1198,15 +1201,13 @@ impl Room {
is_ssh_project: project.read(cx).is_via_ssh(), is_ssh_project: project.read(cx).is_via_ssh(),
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = request.await?; let response = request.await?;
project.update(&mut cx, |project, cx| { project.update(cx, |project, cx| project.shared(response.project_id, cx))??;
project.shared(response.project_id, cx)
})??;
// If the user's location is in this project, it changes from UnsharedProject to SharedProject. // If the user's location is in this project, it changes from UnsharedProject to SharedProject.
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.shared_projects.insert(project.downgrade()); this.shared_projects.insert(project.downgrade());
let active_project = this.local_participant.active_project.as_ref(); let active_project = this.local_participant.active_project.as_ref();
if active_project.map_or(false, |location| *location == project) { if active_project.map_or(false, |location| *location == project) {
@ -1348,12 +1349,12 @@ impl Room {
return Task::ready(Err(anyhow!("live-kit was not initialized"))); return Task::ready(Err(anyhow!("live-kit was not initialized")));
}; };
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let publish_track = async { let publish_track = async {
let track = LocalAudioTrack::create(); let track = LocalAudioTrack::create();
this.upgrade() this.upgrade()
.ok_or_else(|| anyhow!("room was dropped"))? .ok_or_else(|| anyhow!("room was dropped"))?
.update(&mut cx, |this, _| { .update(cx, |this, _| {
this.live_kit this.live_kit
.as_ref() .as_ref()
.map(|live_kit| live_kit.room.publish_audio_track(track)) .map(|live_kit| live_kit.room.publish_audio_track(track))
@ -1364,7 +1365,7 @@ impl Room {
let publication = publish_track.await; let publication = publish_track.await;
this.upgrade() this.upgrade()
.ok_or_else(|| anyhow!("room was dropped"))? .ok_or_else(|| anyhow!("room was dropped"))?
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
let live_kit = this let live_kit = this
.live_kit .live_kit
.as_mut() .as_mut()
@ -1424,7 +1425,7 @@ impl Room {
return Task::ready(Err(anyhow!("live-kit was not initialized"))); return Task::ready(Err(anyhow!("live-kit was not initialized")));
}; };
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let publish_track = async { let publish_track = async {
let displays = displays.await?; let displays = displays.await?;
let display = displays let display = displays
@ -1433,7 +1434,7 @@ impl Room {
let track = LocalVideoTrack::screen_share_for_display(display); let track = LocalVideoTrack::screen_share_for_display(display);
this.upgrade() this.upgrade()
.ok_or_else(|| anyhow!("room was dropped"))? .ok_or_else(|| anyhow!("room was dropped"))?
.update(&mut cx, |this, _| { .update(cx, |this, _| {
this.live_kit this.live_kit
.as_ref() .as_ref()
.map(|live_kit| live_kit.room.publish_video_track(track)) .map(|live_kit| live_kit.room.publish_video_track(track))
@ -1445,7 +1446,7 @@ impl Room {
let publication = publish_track.await; let publication = publish_track.await;
this.upgrade() this.upgrade()
.ok_or_else(|| anyhow!("room was dropped"))? .ok_or_else(|| anyhow!("room was dropped"))?
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
let live_kit = this let live_kit = this
.live_kit .live_kit
.as_mut() .as_mut()

View file

@ -47,7 +47,7 @@ impl ChannelBuffer {
client: Arc<Client>, client: Arc<Client>,
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
channel_store: Entity<ChannelStore>, channel_store: Entity<ChannelStore>,
mut cx: AsyncApp, cx: &mut AsyncApp,
) -> Result<Entity<Self>> { ) -> Result<Entity<Self>> {
let response = client let response = client
.request(proto::JoinChannelBuffer { .request(proto::JoinChannelBuffer {
@ -66,7 +66,7 @@ impl ChannelBuffer {
let capability = channel_store.read(cx).channel_capability(channel.id); let capability = channel_store.read(cx).channel_capability(channel.id);
language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text) language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text)
})?; })?;
buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))?; buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
let subscription = client.subscribe_to_entity(channel.id.0)?; let subscription = client.subscribe_to_entity(channel.id.0)?;
@ -208,7 +208,7 @@ impl ChannelBuffer {
let client = self.client.clone(); let client = self.client.clone();
let epoch = self.epoch(); let epoch = self.epoch();
self.acknowledge_task = Some(cx.spawn(move |_, cx| async move { self.acknowledge_task = Some(cx.spawn(async move |_, cx| {
cx.background_executor() cx.background_executor()
.timer(ACKNOWLEDGE_DEBOUNCE_INTERVAL) .timer(ACKNOWLEDGE_DEBOUNCE_INTERVAL)
.await; .await;

View file

@ -106,7 +106,7 @@ impl ChannelChat {
channel_store: Entity<ChannelStore>, channel_store: Entity<ChannelStore>,
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
client: Arc<Client>, client: Arc<Client>,
mut cx: AsyncApp, cx: &mut AsyncApp,
) -> Result<Entity<Self>> { ) -> Result<Entity<Self>> {
let channel_id = channel.id; let channel_id = channel.id;
let subscription = client.subscribe_to_entity(channel_id.0).unwrap(); let subscription = client.subscribe_to_entity(channel_id.0).unwrap();
@ -132,7 +132,7 @@ impl ChannelChat {
last_acknowledged_id: None, last_acknowledged_id: None,
rng: StdRng::from_entropy(), rng: StdRng::from_entropy(),
first_loaded_message_id: None, first_loaded_message_id: None,
_subscription: subscription.set_entity(&cx.entity(), &mut cx.to_async()), _subscription: subscription.set_entity(&cx.entity(), &cx.to_async()),
} }
})?; })?;
Self::handle_loaded_messages( Self::handle_loaded_messages(
@ -141,7 +141,7 @@ impl ChannelChat {
client, client,
response.messages, response.messages,
response.done, response.done,
&mut cx, cx,
) )
.await?; .await?;
Ok(handle) Ok(handle)
@ -205,7 +205,7 @@ impl ChannelChat {
let outgoing_messages_lock = self.outgoing_messages_lock.clone(); let outgoing_messages_lock = self.outgoing_messages_lock.clone();
// todo - handle messages that fail to send (e.g. >1024 chars) // todo - handle messages that fail to send (e.g. >1024 chars)
Ok(cx.spawn(move |this, mut cx| async move { Ok(cx.spawn(async move |this, cx| {
let outgoing_message_guard = outgoing_messages_lock.lock().await; let outgoing_message_guard = outgoing_messages_lock.lock().await;
let request = rpc.request(proto::SendChannelMessage { let request = rpc.request(proto::SendChannelMessage {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -218,8 +218,8 @@ impl ChannelChat {
drop(outgoing_message_guard); drop(outgoing_message_guard);
let response = response.message.ok_or_else(|| anyhow!("invalid message"))?; let response = response.message.ok_or_else(|| anyhow!("invalid message"))?;
let id = response.id; let id = response.id;
let message = ChannelMessage::from_proto(response, &user_store, &mut cx).await?; let message = ChannelMessage::from_proto(response, &user_store, cx).await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.insert_messages(SumTree::from_item(message, &()), cx); this.insert_messages(SumTree::from_item(message, &()), cx);
if this.first_loaded_message_id.is_none() { if this.first_loaded_message_id.is_none() {
this.first_loaded_message_id = Some(id); this.first_loaded_message_id = Some(id);
@ -234,9 +234,9 @@ impl ChannelChat {
channel_id: self.channel_id.0, channel_id: self.channel_id.0,
message_id: id, message_id: id,
}); });
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
response.await?; response.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.message_removed(id, cx); this.message_removed(id, cx);
})?; })?;
Ok(()) Ok(())
@ -266,7 +266,7 @@ impl ChannelChat {
nonce: Some(nonce.into()), nonce: Some(nonce.into()),
mentions: mentions_to_proto(&message.mentions), mentions: mentions_to_proto(&message.mentions),
}); });
Ok(cx.spawn(move |_, _| async move { Ok(cx.spawn(async move |_, _| {
request.await?; request.await?;
Ok(()) Ok(())
})) }))
@ -281,7 +281,7 @@ impl ChannelChat {
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let channel_id = self.channel_id; let channel_id = self.channel_id;
let before_message_id = self.first_loaded_message_id()?; let before_message_id = self.first_loaded_message_id()?;
Some(cx.spawn(move |this, mut cx| { Some(cx.spawn(async move |this, cx| {
async move { async move {
let response = rpc let response = rpc
.request(proto::GetChannelMessages { .request(proto::GetChannelMessages {
@ -295,13 +295,14 @@ impl ChannelChat {
rpc, rpc,
response.messages, response.messages,
response.done, response.done,
&mut cx, cx,
) )
.await?; .await?;
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
})) }))
} }
@ -439,7 +440,7 @@ impl ChannelChat {
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let rpc = self.rpc.clone(); let rpc = self.rpc.clone();
let channel_id = self.channel_id; let channel_id = self.channel_id;
cx.spawn(move |this, mut cx| { cx.spawn(async move |this, cx| {
async move { async move {
let response = rpc let response = rpc
.request(proto::JoinChannelChat { .request(proto::JoinChannelChat {
@ -452,11 +453,11 @@ impl ChannelChat {
rpc.clone(), rpc.clone(),
response.messages, response.messages,
response.done, response.done,
&mut cx, cx,
) )
.await?; .await?;
let pending_messages = this.update(&mut cx, |this, _| { let pending_messages = this.update(cx, |this, _| {
this.pending_messages().cloned().collect::<Vec<_>>() this.pending_messages().cloned().collect::<Vec<_>>()
})?; })?;
@ -472,10 +473,10 @@ impl ChannelChat {
let message = ChannelMessage::from_proto( let message = ChannelMessage::from_proto(
response.message.ok_or_else(|| anyhow!("invalid message"))?, response.message.ok_or_else(|| anyhow!("invalid message"))?,
&user_store, &user_store,
&mut cx, cx,
) )
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.insert_messages(SumTree::from_item(message, &()), cx); this.insert_messages(SumTree::from_item(message, &()), cx);
})?; })?;
} }
@ -483,6 +484,7 @@ impl ChannelChat {
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}) })
.detach(); .detach();
} }

View file

@ -164,22 +164,22 @@ impl ChannelStore {
let mut connection_status = client.status(); let mut connection_status = client.status();
let (update_channels_tx, mut update_channels_rx) = mpsc::unbounded(); let (update_channels_tx, mut update_channels_rx) = mpsc::unbounded();
let watch_connection_status = cx.spawn(|this, mut cx| async move { let watch_connection_status = cx.spawn(async move |this, cx| {
while let Some(status) = connection_status.next().await { while let Some(status) = connection_status.next().await {
let this = this.upgrade()?; let this = this.upgrade()?;
match status { match status {
client::Status::Connected { .. } => { client::Status::Connected { .. } => {
this.update(&mut cx, |this, cx| this.handle_connect(cx)) this.update(cx, |this, cx| this.handle_connect(cx))
.ok()? .ok()?
.await .await
.log_err()?; .log_err()?;
} }
client::Status::SignedOut | client::Status::UpgradeRequired => { client::Status::SignedOut | client::Status::UpgradeRequired => {
this.update(&mut cx, |this, cx| this.handle_disconnect(false, cx)) this.update(cx, |this, cx| this.handle_disconnect(false, cx))
.ok(); .ok();
} }
_ => { _ => {
this.update(&mut cx, |this, cx| this.handle_disconnect(true, cx)) this.update(cx, |this, cx| this.handle_disconnect(true, cx))
.ok(); .ok();
} }
} }
@ -200,13 +200,12 @@ impl ChannelStore {
_rpc_subscriptions: rpc_subscriptions, _rpc_subscriptions: rpc_subscriptions,
_watch_connection_status: watch_connection_status, _watch_connection_status: watch_connection_status,
disconnect_channel_buffers_task: None, disconnect_channel_buffers_task: None,
_update_channels: cx.spawn(|this, mut cx| async move { _update_channels: cx.spawn(async move |this, cx| {
maybe!(async move { maybe!(async move {
while let Some(update_channels) = update_channels_rx.next().await { while let Some(update_channels) = update_channels_rx.next().await {
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
let update_task = this.update(&mut cx, |this, cx| { let update_task = this
this.update_channels(update_channels, cx) .update(cx, |this, cx| this.update_channels(update_channels, cx))?;
})?;
if let Some(update_task) = update_task { if let Some(update_task) = update_task {
update_task.await.log_err(); update_task.await.log_err();
} }
@ -310,7 +309,9 @@ impl ChannelStore {
self.open_channel_resource( self.open_channel_resource(
channel_id, channel_id,
|this| &mut this.opened_buffers, |this| &mut this.opened_buffers,
|channel, cx| ChannelBuffer::new(channel, client, user_store, channel_store, cx), async move |channel, cx| {
ChannelBuffer::new(channel, client, user_store, channel_store, cx).await
},
cx, cx,
) )
} }
@ -328,14 +329,14 @@ impl ChannelStore {
.request(proto::GetChannelMessagesById { message_ids }), .request(proto::GetChannelMessagesById { message_ids }),
) )
}; };
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
if let Some(request) = request { if let Some(request) = request {
let response = request.await?; let response = request.await?;
let this = this let this = this
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("channel store dropped"))?; .ok_or_else(|| anyhow!("channel store dropped"))?;
let user_store = this.update(&mut cx, |this, _| this.user_store.clone())?; let user_store = this.update(cx, |this, _| this.user_store.clone())?;
ChannelMessage::from_proto_vec(response.messages, &user_store, &mut cx).await ChannelMessage::from_proto_vec(response.messages, &user_store, cx).await
} else { } else {
Ok(Vec::new()) Ok(Vec::new())
} }
@ -440,7 +441,7 @@ impl ChannelStore {
self.open_channel_resource( self.open_channel_resource(
channel_id, channel_id,
|this| &mut this.opened_chats, |this| &mut this.opened_chats,
|channel, cx| ChannelChat::new(channel, this, user_store, client, cx), async move |channel, cx| ChannelChat::new(channel, this, user_store, client, cx).await,
cx, cx,
) )
} }
@ -450,7 +451,7 @@ impl ChannelStore {
/// Make sure that the resource is only opened once, even if this method /// Make sure that the resource is only opened once, even if this method
/// is called multiple times with the same channel id while the first task /// is called multiple times with the same channel id while the first task
/// is still running. /// is still running.
fn open_channel_resource<T, F, Fut>( fn open_channel_resource<T, F>(
&mut self, &mut self,
channel_id: ChannelId, channel_id: ChannelId,
get_map: fn(&mut Self) -> &mut HashMap<ChannelId, OpenEntityHandle<T>>, get_map: fn(&mut Self) -> &mut HashMap<ChannelId, OpenEntityHandle<T>>,
@ -458,8 +459,7 @@ impl ChannelStore {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<Result<Entity<T>>> ) -> Task<Result<Entity<T>>>
where where
F: 'static + FnOnce(Arc<Channel>, AsyncApp) -> Fut, F: AsyncFnOnce(Arc<Channel>, &mut AsyncApp) -> Result<Entity<T>> + 'static,
Fut: Future<Output = Result<Entity<T>>>,
T: 'static, T: 'static,
{ {
let task = loop { let task = loop {
@ -479,8 +479,8 @@ impl ChannelStore {
}, },
hash_map::Entry::Vacant(e) => { hash_map::Entry::Vacant(e) => {
let task = cx let task = cx
.spawn(move |this, mut cx| async move { .spawn(async move |this, cx| {
let channel = this.update(&mut cx, |this, _| { let channel = this.update(cx, |this, _| {
this.channel_for_id(channel_id).cloned().ok_or_else(|| { this.channel_for_id(channel_id).cloned().ok_or_else(|| {
Arc::new(anyhow!("no channel for id: {}", channel_id)) Arc::new(anyhow!("no channel for id: {}", channel_id))
}) })
@ -493,9 +493,9 @@ impl ChannelStore {
e.insert(OpenEntityHandle::Loading(task.clone())); e.insert(OpenEntityHandle::Loading(task.clone()));
cx.spawn({ cx.spawn({
let task = task.clone(); let task = task.clone();
move |this, mut cx| async move { async move |this, cx| {
let result = task.await; let result = task.await;
this.update(&mut cx, |this, _| match result { this.update(cx, |this, _| match result {
Ok(model) => { Ok(model) => {
get_map(this).insert( get_map(this).insert(
channel_id, channel_id,
@ -570,7 +570,7 @@ impl ChannelStore {
) -> Task<Result<ChannelId>> { ) -> Task<Result<ChannelId>> {
let client = self.client.clone(); let client = self.client.clone();
let name = name.trim_start_matches('#').to_owned(); let name = name.trim_start_matches('#').to_owned();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = client let response = client
.request(proto::CreateChannel { .request(proto::CreateChannel {
name, name,
@ -583,7 +583,7 @@ impl ChannelStore {
.ok_or_else(|| anyhow!("missing channel in response"))?; .ok_or_else(|| anyhow!("missing channel in response"))?;
let channel_id = ChannelId(channel.id); let channel_id = ChannelId(channel.id);
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let task = this.update_channels( let task = this.update_channels(
proto::UpdateChannels { proto::UpdateChannels {
channels: vec![channel], channels: vec![channel],
@ -611,7 +611,7 @@ impl ChannelStore {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(move |_, _| async move { cx.spawn(async move |_, _| {
let _ = client let _ = client
.request(proto::MoveChannel { .request(proto::MoveChannel {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -630,7 +630,7 @@ impl ChannelStore {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(move |_, _| async move { cx.spawn(async move |_, _| {
let _ = client let _ = client
.request(proto::SetChannelVisibility { .request(proto::SetChannelVisibility {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -655,7 +655,7 @@ impl ChannelStore {
cx.notify(); cx.notify();
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let result = client let result = client
.request(proto::InviteChannelMember { .request(proto::InviteChannelMember {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -664,7 +664,7 @@ impl ChannelStore {
}) })
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.outgoing_invites.remove(&(channel_id, user_id)); this.outgoing_invites.remove(&(channel_id, user_id));
cx.notify(); cx.notify();
})?; })?;
@ -687,7 +687,7 @@ impl ChannelStore {
cx.notify(); cx.notify();
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let result = client let result = client
.request(proto::RemoveChannelMember { .request(proto::RemoveChannelMember {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -695,7 +695,7 @@ impl ChannelStore {
}) })
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.outgoing_invites.remove(&(channel_id, user_id)); this.outgoing_invites.remove(&(channel_id, user_id));
cx.notify(); cx.notify();
})?; })?;
@ -717,7 +717,7 @@ impl ChannelStore {
cx.notify(); cx.notify();
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let result = client let result = client
.request(proto::SetChannelMemberRole { .request(proto::SetChannelMemberRole {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -726,7 +726,7 @@ impl ChannelStore {
}) })
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.outgoing_invites.remove(&(channel_id, user_id)); this.outgoing_invites.remove(&(channel_id, user_id));
cx.notify(); cx.notify();
})?; })?;
@ -744,7 +744,7 @@ impl ChannelStore {
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let client = self.client.clone(); let client = self.client.clone();
let name = new_name.to_string(); let name = new_name.to_string();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let channel = client let channel = client
.request(proto::RenameChannel { .request(proto::RenameChannel {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -753,7 +753,7 @@ impl ChannelStore {
.await? .await?
.channel .channel
.ok_or_else(|| anyhow!("missing channel in response"))?; .ok_or_else(|| anyhow!("missing channel in response"))?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let task = this.update_channels( let task = this.update_channels(
proto::UpdateChannels { proto::UpdateChannels {
channels: vec![channel], channels: vec![channel],
@ -799,7 +799,7 @@ impl ChannelStore {
) -> Task<Result<Vec<ChannelMembership>>> { ) -> Task<Result<Vec<ChannelMembership>>> {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.downgrade(); let user_store = self.user_store.downgrade();
cx.spawn(move |_, mut cx| async move { cx.spawn(async move |_, cx| {
let response = client let response = client
.request(proto::GetChannelMembers { .request(proto::GetChannelMembers {
channel_id: channel_id.0, channel_id: channel_id.0,
@ -807,7 +807,7 @@ impl ChannelStore {
limit: limit as u64, limit: limit as u64,
}) })
.await?; .await?;
user_store.update(&mut cx, |user_store, _| { user_store.update(cx, |user_store, _| {
user_store.insert(response.users); user_store.insert(response.users);
response response
.members .members
@ -931,10 +931,10 @@ impl ChannelStore {
buffers: buffer_versions, buffers: buffer_versions,
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let mut response = response.await?; let mut response = response.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.opened_buffers.retain(|_, buffer| match buffer { this.opened_buffers.retain(|_, buffer| match buffer {
OpenEntityHandle::Open(channel_buffer) => { OpenEntityHandle::Open(channel_buffer) => {
let Some(channel_buffer) = channel_buffer.upgrade() else { let Some(channel_buffer) = channel_buffer.upgrade() else {
@ -1006,13 +1006,13 @@ impl ChannelStore {
cx.notify(); cx.notify();
self.did_subscribe = false; self.did_subscribe = false;
self.disconnect_channel_buffers_task.get_or_insert_with(|| { self.disconnect_channel_buffers_task.get_or_insert_with(|| {
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
if wait_for_reconnect { if wait_for_reconnect {
cx.background_executor().timer(RECONNECT_TIMEOUT).await; cx.background_executor().timer(RECONNECT_TIMEOUT).await;
} }
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
for (_, buffer) in this.opened_buffers.drain() { for (_, buffer) in this.opened_buffers.drain() {
if let OpenEntityHandle::Open(buffer) = buffer { if let OpenEntityHandle::Open(buffer) = buffer {
if let Some(buffer) = buffer.upgrade() { if let Some(buffer) = buffer.upgrade() {
@ -1136,10 +1136,10 @@ impl ChannelStore {
let users = self let users = self
.user_store .user_store
.update(cx, |user_store, cx| user_store.get_users(all_user_ids, cx)); .update(cx, |user_store, cx| user_store.get_users(all_user_ids, cx));
Some(cx.spawn(|this, mut cx| async move { Some(cx.spawn(async move |this, cx| {
let users = users.await?; let users = users.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
for entry in &channel_participants { for entry in &channel_participants {
let mut participants: Vec<_> = entry let mut participants: Vec<_> = entry
.participant_user_ids .participant_user_ids

View file

@ -144,9 +144,9 @@ pub fn init(client: &Arc<Client>, cx: &mut App) {
let client = client.clone(); let client = client.clone();
move |_: &SignIn, cx| { move |_: &SignIn, cx| {
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
cx.spawn( cx.spawn(async move |cx| {
|cx| async move { client.authenticate_and_connect(true, &cx).log_err().await }, client.authenticate_and_connect(true, &cx).log_err().await
) })
.detach(); .detach();
} }
} }
@ -156,7 +156,7 @@ pub fn init(client: &Arc<Client>, cx: &mut App) {
let client = client.clone(); let client = client.clone();
move |_: &SignOut, cx| { move |_: &SignOut, cx| {
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
client.sign_out(&cx).await; client.sign_out(&cx).await;
}) })
.detach(); .detach();
@ -168,7 +168,7 @@ pub fn init(client: &Arc<Client>, cx: &mut App) {
let client = client.clone(); let client = client.clone();
move |_: &Reconnect, cx| { move |_: &Reconnect, cx| {
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
client.reconnect(&cx); client.reconnect(&cx);
}) })
.detach(); .detach();
@ -640,7 +640,7 @@ impl Client {
} }
Status::ConnectionLost => { Status::ConnectionLost => {
let this = self.clone(); let this = self.clone();
state._reconnect_task = Some(cx.spawn(move |cx| async move { state._reconnect_task = Some(cx.spawn(async move |cx| {
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
let mut rng = StdRng::seed_from_u64(0); let mut rng = StdRng::seed_from_u64(0);
#[cfg(not(any(test, feature = "test-support")))] #[cfg(not(any(test, feature = "test-support")))]
@ -964,13 +964,11 @@ impl Client {
cx.spawn({ cx.spawn({
let this = self.clone(); let this = self.clone();
|cx| { async move |cx| {
async move { while let Some(message) = incoming.next().await {
while let Some(message) = incoming.next().await { this.handle_message(message, &cx);
this.handle_message(message, &cx); // Don't starve the main thread when receiving lots of messages at once.
// Don't starve the main thread when receiving lots of messages at once. smol::future::yield_now().await;
smol::future::yield_now().await;
}
} }
} }
}) })
@ -978,23 +976,21 @@ impl Client {
cx.spawn({ cx.spawn({
let this = self.clone(); let this = self.clone();
move |cx| async move { async move |cx| match handle_io.await {
match handle_io.await { Ok(()) => {
Ok(()) => { if *this.status().borrow()
if *this.status().borrow() == (Status::Connected {
== (Status::Connected { connection_id,
connection_id, peer_id,
peer_id, })
}) {
{ this.set_status(Status::SignedOut, &cx);
this.set_status(Status::SignedOut, &cx);
}
}
Err(err) => {
log::error!("connection error: {:?}", err);
this.set_status(Status::ConnectionLost, &cx);
} }
} }
Err(err) => {
log::error!("connection error: {:?}", err);
this.set_status(Status::ConnectionLost, &cx);
}
} }
}) })
.detach(); .detach();
@ -1178,12 +1174,12 @@ impl Client {
pub fn authenticate_with_browser(self: &Arc<Self>, cx: &AsyncApp) -> Task<Result<Credentials>> { pub fn authenticate_with_browser(self: &Arc<Self>, cx: &AsyncApp) -> Task<Result<Credentials>> {
let http = self.http.clone(); let http = self.http.clone();
let this = self.clone(); let this = self.clone();
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
let background = cx.background_executor().clone(); let background = cx.background_executor().clone();
let (open_url_tx, open_url_rx) = oneshot::channel::<String>(); let (open_url_tx, open_url_rx) = oneshot::channel::<String>();
cx.update(|cx| { cx.update(|cx| {
cx.spawn(move |cx| async move { cx.spawn(async move |cx| {
let url = open_url_rx.await?; let url = open_url_rx.await?;
cx.update(|cx| cx.open_url(&url)) cx.update(|cx| cx.open_url(&url))
}) })
@ -1545,25 +1541,23 @@ impl Client {
original_sender_id, original_sender_id,
type_name type_name
); );
cx.spawn(move |_| async move { cx.spawn(async move |_| match future.await {
match future.await { Ok(()) => {
Ok(()) => { log::debug!(
log::debug!( "rpc message handled. client_id:{}, sender_id:{:?}, type:{}",
"rpc message handled. client_id:{}, sender_id:{:?}, type:{}", client_id,
client_id, original_sender_id,
original_sender_id, type_name
type_name );
); }
} Err(error) => {
Err(error) => { log::error!(
log::error!( "error handling message. client_id:{}, sender_id:{:?}, type:{}, error:{:?}",
"error handling message. client_id:{}, sender_id:{:?}, type:{}, error:{:?}", client_id,
client_id, original_sender_id,
original_sender_id, type_name,
type_name, error
error );
);
}
} }
}) })
.detach(); .detach();

View file

@ -44,7 +44,7 @@ impl FakeServer {
let state = Arc::downgrade(&server.state); let state = Arc::downgrade(&server.state);
move |cx| { move |cx| {
let state = state.clone(); let state = state.clone();
cx.spawn(move |_| async move { cx.spawn(async move |_| {
let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?; let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?;
let mut state = state.lock(); let mut state = state.lock();
state.auth_count += 1; state.auth_count += 1;
@ -63,7 +63,7 @@ impl FakeServer {
let peer = peer.clone(); let peer = peer.clone();
let state = state.clone(); let state = state.clone();
let credentials = credentials.clone(); let credentials = credentials.clone();
cx.spawn(move |cx| async move { cx.spawn(async move |cx| {
let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?; let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?;
let peer = peer.upgrade().ok_or_else(|| anyhow!("server dropped"))?; let peer = peer.upgrade().ok_or_else(|| anyhow!("server dropped"))?;
if state.lock().forbid_connections { if state.lock().forbid_connections {

View file

@ -168,11 +168,10 @@ impl UserStore {
invite_info: None, invite_info: None,
client: Arc::downgrade(&client), client: Arc::downgrade(&client),
update_contacts_tx, update_contacts_tx,
_maintain_contacts: cx.spawn(|this, mut cx| async move { _maintain_contacts: cx.spawn(async move |this, cx| {
let _subscriptions = rpc_subscriptions; let _subscriptions = rpc_subscriptions;
while let Some(message) = update_contacts_rx.next().await { while let Some(message) = update_contacts_rx.next().await {
if let Ok(task) = if let Ok(task) = this.update(cx, |this, cx| this.update_contacts(message, cx))
this.update(&mut cx, |this, cx| this.update_contacts(message, cx))
{ {
task.log_err().await; task.log_err().await;
} else { } else {
@ -180,7 +179,7 @@ impl UserStore {
} }
} }
}), }),
_maintain_current_user: cx.spawn(|this, mut cx| async move { _maintain_current_user: cx.spawn(async move |this, cx| {
let mut status = client.status(); let mut status = client.status();
let weak = Arc::downgrade(&client); let weak = Arc::downgrade(&client);
drop(client); drop(client);
@ -192,10 +191,9 @@ impl UserStore {
match status { match status {
Status::Connected { .. } => { Status::Connected { .. } => {
if let Some(user_id) = client.user_id() { if let Some(user_id) = client.user_id() {
let fetch_user = if let Ok(fetch_user) = this let fetch_user = if let Ok(fetch_user) =
.update(&mut cx, |this, cx| { this.update(cx, |this, cx| this.get_user(user_id, cx).log_err())
this.get_user(user_id, cx).log_err() {
}) {
fetch_user fetch_user
} else { } else {
break; break;
@ -239,12 +237,12 @@ impl UserStore {
current_user_tx.send(user).await.ok(); current_user_tx.send(user).await.ok();
this.update(&mut cx, |_, cx| cx.notify())?; this.update(cx, |_, cx| cx.notify())?;
} }
} }
Status::SignedOut => { Status::SignedOut => {
current_user_tx.send(None).await.ok(); current_user_tx.send(None).await.ok();
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.accepted_tos_at = None; this.accepted_tos_at = None;
cx.emit(Event::PrivateUserInfoUpdated); cx.emit(Event::PrivateUserInfoUpdated);
cx.notify(); cx.notify();
@ -253,7 +251,7 @@ impl UserStore {
.await; .await;
} }
Status::ConnectionLost => { Status::ConnectionLost => {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
cx.notify(); cx.notify();
this.clear_contacts() this.clear_contacts()
})? })?
@ -350,7 +348,7 @@ impl UserStore {
user_ids.extend(message.outgoing_requests.iter()); user_ids.extend(message.outgoing_requests.iter());
let load_users = self.get_users(user_ids.into_iter().collect(), cx); let load_users = self.get_users(user_ids.into_iter().collect(), cx);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
load_users.await?; load_users.await?;
// Users are fetched in parallel above and cached in call to get_users // Users are fetched in parallel above and cached in call to get_users
@ -360,25 +358,22 @@ impl UserStore {
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("can't upgrade user store handle"))?; .ok_or_else(|| anyhow!("can't upgrade user store handle"))?;
for contact in message.contacts { for contact in message.contacts {
updated_contacts.push(Arc::new( updated_contacts
Contact::from_proto(contact, &this, &mut cx).await?, .push(Arc::new(Contact::from_proto(contact, &this, cx).await?));
));
} }
let mut incoming_requests = Vec::new(); let mut incoming_requests = Vec::new();
for request in message.incoming_requests { for request in message.incoming_requests {
incoming_requests.push({ incoming_requests.push({
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| this.get_user(request.requester_id, cx))?
this.get_user(request.requester_id, cx) .await?
})?
.await?
}); });
} }
let mut outgoing_requests = Vec::new(); let mut outgoing_requests = Vec::new();
for requested_user_id in message.outgoing_requests { for requested_user_id in message.outgoing_requests {
outgoing_requests.push( outgoing_requests.push(
this.update(&mut cx, |this, cx| this.get_user(requested_user_id, cx))? this.update(cx, |this, cx| this.get_user(requested_user_id, cx))?
.await?, .await?,
); );
} }
@ -390,7 +385,7 @@ impl UserStore {
let removed_outgoing_requests = let removed_outgoing_requests =
HashSet::<u64>::from_iter(message.remove_outgoing_requests.iter().copied()); HashSet::<u64>::from_iter(message.remove_outgoing_requests.iter().copied());
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
// Remove contacts // Remove contacts
this.contacts this.contacts
.retain(|contact| !removed_contacts.contains(&contact.user.id)); .retain(|contact| !removed_contacts.contains(&contact.user.id));
@ -543,7 +538,7 @@ impl UserStore {
cx: &Context<Self>, cx: &Context<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let client = self.client.upgrade(); let client = self.client.upgrade();
cx.spawn(move |_, _| async move { cx.spawn(async move |_, _| {
client client
.ok_or_else(|| anyhow!("can't upgrade client reference"))? .ok_or_else(|| anyhow!("can't upgrade client reference"))?
.request(proto::RespondToContactRequest { .request(proto::RespondToContactRequest {
@ -565,12 +560,12 @@ impl UserStore {
*self.pending_contact_requests.entry(user_id).or_insert(0) += 1; *self.pending_contact_requests.entry(user_id).or_insert(0) += 1;
cx.notify(); cx.notify();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
let response = client let response = client
.ok_or_else(|| anyhow!("can't upgrade client reference"))? .ok_or_else(|| anyhow!("can't upgrade client reference"))?
.request(request) .request(request)
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Entry::Occupied(mut request_count) = if let Entry::Occupied(mut request_count) =
this.pending_contact_requests.entry(user_id) this.pending_contact_requests.entry(user_id)
{ {
@ -614,9 +609,9 @@ impl UserStore {
let mut user_ids_to_fetch = user_ids.clone(); let mut user_ids_to_fetch = user_ids.clone();
user_ids_to_fetch.retain(|id| !self.users.contains_key(id)); user_ids_to_fetch.retain(|id| !self.users.contains_key(id));
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
if !user_ids_to_fetch.is_empty() { if !user_ids_to_fetch.is_empty() {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.load_users( this.load_users(
proto::GetUsers { proto::GetUsers {
user_ids: user_ids_to_fetch, user_ids: user_ids_to_fetch,
@ -627,7 +622,7 @@ impl UserStore {
.await?; .await?;
} }
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
user_ids user_ids
.iter() .iter()
.map(|user_id| { .map(|user_id| {
@ -668,9 +663,9 @@ impl UserStore {
} }
let load_users = self.get_users(vec![user_id], cx); let load_users = self.get_users(vec![user_id], cx);
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
load_users.await?; load_users.await?;
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.users this.users
.get(&user_id) .get(&user_id)
.cloned() .cloned()
@ -708,14 +703,14 @@ impl UserStore {
}; };
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
let response = client let response = client
.request(proto::AcceptTermsOfService {}) .request(proto::AcceptTermsOfService {})
.await .await
.context("error accepting tos")?; .context("error accepting tos")?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at)); this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at));
cx.emit(Event::PrivateUserInfoUpdated); cx.emit(Event::PrivateUserInfoUpdated);
}) })
@ -737,12 +732,12 @@ impl UserStore {
cx: &Context<Self>, cx: &Context<Self>,
) -> Task<Result<Vec<Arc<User>>>> { ) -> Task<Result<Vec<Arc<User>>>> {
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
if let Some(rpc) = client.upgrade() { if let Some(rpc) = client.upgrade() {
let response = rpc.request(request).await.context("error loading users")?; let response = rpc.request(request).await.context("error loading users")?;
let users = response.users; let users = response.users;
this.update(&mut cx, |this, _| this.insert(users)) this.update(cx, |this, _| this.insert(users))
} else { } else {
Ok(Vec::new()) Ok(Vec::new())
} }
@ -796,8 +791,8 @@ impl UserStore {
} }
if !missing_user_ids.is_empty() { if !missing_user_ids.is_empty() {
let this = self.weak_self.clone(); let this = self.weak_self.clone();
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
this.update(&mut cx, |this, cx| this.get_users(missing_user_ids, cx))? this.update(cx, |this, cx| this.get_users(missing_user_ids, cx))?
.await .await
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);

View file

@ -562,7 +562,7 @@ async fn test_channel_buffers_and_server_restarts(
deterministic.run_until_parked(); deterministic.run_until_parked();
// Client C can't reconnect. // Client C can't reconnect.
client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); client_c.override_establish_connection(|_, cx| cx.spawn(async |_| future::pending().await));
// Server stops. // Server stops.
server.reset().await; server.reset().await;

View file

@ -983,7 +983,7 @@ async fn test_server_restarts(
server.reset().await; server.reset().await;
// Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room. // Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room.
client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); client_c.override_establish_connection(|_, cx| cx.spawn(async |_| future::pending().await));
executor.advance_clock(RECONNECT_TIMEOUT); executor.advance_clock(RECONNECT_TIMEOUT);
assert_eq!( assert_eq!(
room_participants(&room_a, cx_a), room_participants(&room_a, cx_a),
@ -1156,9 +1156,9 @@ async fn test_server_restarts(
server.reset().await; server.reset().await;
// Users A and B have troubles reconnecting, so they leave the room. // Users A and B have troubles reconnecting, so they leave the room.
client_a.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); client_a.override_establish_connection(|_, cx| cx.spawn(async |_| future::pending().await));
client_b.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); client_b.override_establish_connection(|_, cx| cx.spawn(async |_| future::pending().await));
client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); client_c.override_establish_connection(|_, cx| cx.spawn(async |_| future::pending().await));
executor.advance_clock(RECONNECT_TIMEOUT); executor.advance_clock(RECONNECT_TIMEOUT);
assert_eq!( assert_eq!(
room_participants(&room_a, cx_a), room_participants(&room_a, cx_a),

View file

@ -208,8 +208,8 @@ impl TestServer {
.unwrap() .unwrap()
.set_id(user_id.to_proto()) .set_id(user_id.to_proto())
.override_authenticate(move |cx| { .override_authenticate(move |cx| {
cx.spawn(|_| async move { let access_token = "the-token".to_string();
let access_token = "the-token".to_string(); cx.spawn(async move |_| {
Ok(Credentials { Ok(Credentials {
user_id: user_id.to_proto(), user_id: user_id.to_proto(),
access_token, access_token,
@ -230,7 +230,7 @@ impl TestServer {
let connection_killers = connection_killers.clone(); let connection_killers = connection_killers.clone();
let forbid_connections = forbid_connections.clone(); let forbid_connections = forbid_connections.clone();
let client_name = client_name.clone(); let client_name = client_name.clone();
cx.spawn(move |cx| async move { cx.spawn(async move |cx| {
if forbid_connections.load(SeqCst) { if forbid_connections.load(SeqCst) {
Err(EstablishConnectionError::other(anyhow!( Err(EstablishConnectionError::other(anyhow!(
"server is forbidding connections" "server is forbidding connections"

View file

@ -64,9 +64,9 @@ impl ChannelView {
window, window,
cx, cx,
); );
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let channel_view = channel_view.await?; let channel_view = channel_view.await?;
pane.update_in(&mut cx, |pane, window, cx| { pane.update_in(cx, |pane, window, cx| {
telemetry::event!( telemetry::event!(
"Channel Notes Opened", "Channel Notes Opened",
channel_id, channel_id,
@ -90,10 +90,10 @@ impl ChannelView {
cx: &mut App, cx: &mut App,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
let channel_view = Self::load(channel_id, workspace, window, cx); let channel_view = Self::load(channel_id, workspace, window, cx);
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let channel_view = channel_view.await?; let channel_view = channel_view.await?;
pane.update_in(&mut cx, |pane, window, cx| { pane.update_in(cx, |pane, window, cx| {
let buffer_id = channel_view.read(cx).channel_buffer.read(cx).remote_id(cx); let buffer_id = channel_view.read(cx).channel_buffer.read(cx).remote_id(cx);
let existing_view = pane let existing_view = pane
@ -166,11 +166,11 @@ impl ChannelView {
let channel_buffer = let channel_buffer =
channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx)); channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx));
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let channel_buffer = channel_buffer.await?; let channel_buffer = channel_buffer.await?;
let markdown = markdown.await.log_err(); let markdown = markdown.await.log_err();
channel_buffer.update(&mut cx, |channel_buffer, cx| { channel_buffer.update(cx, |channel_buffer, cx| {
channel_buffer.buffer().update(cx, |buffer, cx| { channel_buffer.buffer().update(cx, |buffer, cx| {
buffer.set_language_registry(language_registry); buffer.set_language_registry(language_registry);
let Some(markdown) = markdown else { let Some(markdown) = markdown else {
@ -583,10 +583,10 @@ impl FollowableItem for ChannelView {
let open = ChannelView::load(ChannelId(state.channel_id), workspace, window, cx); let open = ChannelView::load(ChannelId(state.channel_id), workspace, window, cx);
Some(window.spawn(cx, |mut cx| async move { Some(window.spawn(cx, async move |cx| {
let this = open.await?; let this = open.await?;
let task = this.update_in(&mut cx, |this, window, cx| { let task = this.update_in(cx, |this, window, cx| {
this.remote_id = Some(remote_id); this.remote_id = Some(remote_id);
if let Some(state) = state.editor { if let Some(state) = state.editor {

View file

@ -199,7 +199,7 @@ impl ChatPanel {
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
cx: AsyncWindowContext, cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let serialized_panel = if let Some(panel) = cx let serialized_panel = if let Some(panel) = cx
.background_spawn(async move { KEY_VALUE_STORE.read_kvp(CHAT_PANEL_KEY) }) .background_spawn(async move { KEY_VALUE_STORE.read_kvp(CHAT_PANEL_KEY) })
.await .await
@ -211,7 +211,7 @@ impl ChatPanel {
None None
}; };
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
let panel = Self::new(workspace, window, cx); let panel = Self::new(workspace, window, cx);
if let Some(serialized_panel) = serialized_panel { if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| { panel.update(cx, |panel, cx| {
@ -867,10 +867,10 @@ impl ChatPanel {
}) })
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let chat = open_chat.await?; let chat = open_chat.await?;
let highlight_message_id = scroll_to_message_id; let highlight_message_id = scroll_to_message_id;
let scroll_to_message_id = this.update(&mut cx, |this, cx| { let scroll_to_message_id = this.update(cx, |this, cx| {
this.set_active_chat(chat.clone(), cx); this.set_active_chat(chat.clone(), cx);
scroll_to_message_id.or(this.last_acknowledged_message_id) scroll_to_message_id.or(this.last_acknowledged_message_id)
@ -881,11 +881,11 @@ impl ChatPanel {
ChannelChat::load_history_since_message(chat.clone(), message_id, cx.clone()) ChannelChat::load_history_since_message(chat.clone(), message_id, cx.clone())
.await .await
{ {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let Some(highlight_message_id) = highlight_message_id { if let Some(highlight_message_id) = highlight_message_id {
let task = cx.spawn(|this, mut cx| async move { let task = cx.spawn(async move |this, cx| {
cx.background_executor().timer(Duration::from_secs(2)).await; cx.background_executor().timer(Duration::from_secs(2)).await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.highlighted_message.take(); this.highlighted_message.take();
cx.notify(); cx.notify();
}) })

View file

@ -137,11 +137,9 @@ impl MessageEditor {
.detach(); .detach();
let markdown = language_registry.language_for_name("Markdown"); let markdown = language_registry.language_for_name("Markdown");
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
let markdown = markdown.await.context("failed to load Markdown language")?; let markdown = markdown.await.context("failed to load Markdown language")?;
buffer.update(&mut cx, |buffer, cx| { buffer.update(cx, |buffer, cx| buffer.set_language(Some(markdown), cx))
buffer.set_language(Some(markdown), cx)
})
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);
@ -232,7 +230,7 @@ impl MessageEditor {
) { ) {
if let language::BufferEvent::Reparsed | language::BufferEvent::Edited = event { if let language::BufferEvent::Reparsed | language::BufferEvent::Edited = event {
let buffer = buffer.read(cx).snapshot(); let buffer = buffer.read(cx).snapshot();
self.mentions_task = Some(cx.spawn_in(window, |this, cx| async move { self.mentions_task = Some(cx.spawn_in(window, async move |this, cx| {
cx.background_executor() cx.background_executor()
.timer(MENTIONS_DEBOUNCE_INTERVAL) .timer(MENTIONS_DEBOUNCE_INTERVAL)
.await; .await;
@ -251,7 +249,7 @@ impl MessageEditor {
self.collect_mention_candidates(buffer, end_anchor, cx) self.collect_mention_candidates(buffer, end_anchor, cx)
{ {
if !candidates.is_empty() { if !candidates.is_empty() {
return cx.spawn(|_, cx| async move { return cx.spawn(async move |_, cx| {
Ok(Some( Ok(Some(
Self::resolve_completions_for_candidates( Self::resolve_completions_for_candidates(
&cx, &cx,
@ -270,7 +268,7 @@ impl MessageEditor {
self.collect_emoji_candidates(buffer, end_anchor, cx) self.collect_emoji_candidates(buffer, end_anchor, cx)
{ {
if !candidates.is_empty() { if !candidates.is_empty() {
return cx.spawn(|_, cx| async move { return cx.spawn(async move |_, cx| {
Ok(Some( Ok(Some(
Self::resolve_completions_for_candidates( Self::resolve_completions_for_candidates(
&cx, &cx,
@ -453,7 +451,7 @@ impl MessageEditor {
async fn find_mentions( async fn find_mentions(
this: WeakEntity<MessageEditor>, this: WeakEntity<MessageEditor>,
buffer: BufferSnapshot, buffer: BufferSnapshot,
mut cx: AsyncWindowContext, cx: &mut AsyncWindowContext,
) { ) {
let (buffer, ranges) = cx let (buffer, ranges) = cx
.background_spawn(async move { .background_spawn(async move {
@ -462,7 +460,7 @@ impl MessageEditor {
}) })
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let mut anchor_ranges = Vec::new(); let mut anchor_ranges = Vec::new();
let mut mentioned_user_ids = Vec::new(); let mut mentioned_user_ids = Vec::new();
let mut text = String::new(); let mut text = String::new();

View file

@ -1569,9 +1569,9 @@ impl CollabPanel {
channel_store.create_channel(&channel_name, *location, cx) channel_store.create_channel(&channel_name, *location, cx)
}); });
if location.is_none() { if location.is_none() {
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let channel_id = create.await?; let channel_id = create.await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.show_channel_modal( this.show_channel_modal(
channel_id, channel_id,
channel_modal::Mode::InviteMembers, channel_modal::Mode::InviteMembers,
@ -1944,8 +1944,8 @@ impl CollabPanel {
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let channel_store = self.channel_store.clone(); let channel_store = self.channel_store.clone();
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
workspace.toggle_modal(window, cx, |window, cx| { workspace.toggle_modal(window, cx, |window, cx| {
ChannelModal::new( ChannelModal::new(
user_store.clone(), user_store.clone(),
@ -1976,11 +1976,11 @@ impl CollabPanel {
&["Leave", "Cancel"], &["Leave", "Cancel"],
cx, cx,
); );
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
if answer.await? != 0 { if answer.await? != 0 {
return Ok(()); return Ok(());
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.channel_store.update(cx, |channel_store, cx| { this.channel_store.update(cx, |channel_store, cx| {
channel_store.remove_member(channel_id, user_id, cx) channel_store.remove_member(channel_id, user_id, cx)
}) })
@ -2009,13 +2009,13 @@ impl CollabPanel {
&["Remove", "Cancel"], &["Remove", "Cancel"],
cx, cx,
); );
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
if answer.await? == 0 { if answer.await? == 0 {
channel_store channel_store
.update(&mut cx, |channels, _| channels.remove_channel(channel_id))? .update(cx, |channels, _| channels.remove_channel(channel_id))?
.await .await
.notify_async_err(&mut cx); .notify_async_err(cx);
this.update_in(&mut cx, |_, window, cx| cx.focus_self(window)) this.update_in(cx, |_, window, cx| cx.focus_self(window))
.ok(); .ok();
} }
anyhow::Ok(()) anyhow::Ok(())
@ -2043,12 +2043,12 @@ impl CollabPanel {
&["Remove", "Cancel"], &["Remove", "Cancel"],
cx, cx,
); );
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
if answer.await? == 0 { if answer.await? == 0 {
user_store user_store
.update(&mut cx, |store, cx| store.remove_contact(user_id, cx))? .update(cx, |store, cx| store.remove_contact(user_id, cx))?
.await .await
.notify_async_err(&mut cx); .notify_async_err(cx);
} }
anyhow::Ok(()) anyhow::Ok(())
}) })
@ -2161,11 +2161,11 @@ impl CollabPanel {
.full_width() .full_width()
.on_click(cx.listener(|this, _, window, cx| { .on_click(cx.listener(|this, _, window, cx| {
let client = this.client.clone(); let client = this.client.clone();
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
client client
.authenticate_and_connect(true, &cx) .authenticate_and_connect(true, &cx)
.await .await
.notify_async_err(&mut cx); .notify_async_err(cx);
}) })
.detach() .detach()
})), })),

View file

@ -300,9 +300,9 @@ impl PickerDelegate for ChannelModalDelegate {
cx.background_executor().clone(), cx.background_executor().clone(),
)); ));
cx.spawn_in(window, |picker, mut cx| async move { cx.spawn_in(window, async move |picker, cx| {
picker picker
.update(&mut cx, |picker, cx| { .update(cx, |picker, cx| {
let delegate = &mut picker.delegate; let delegate = &mut picker.delegate;
delegate.matching_member_indices.clear(); delegate.matching_member_indices.clear();
delegate delegate
@ -316,10 +316,10 @@ impl PickerDelegate for ChannelModalDelegate {
let search_members = self.channel_store.update(cx, |store, cx| { let search_members = self.channel_store.update(cx, |store, cx| {
store.fuzzy_search_members(self.channel_id, query.clone(), 100, cx) store.fuzzy_search_members(self.channel_id, query.clone(), 100, cx)
}); });
cx.spawn_in(window, |picker, mut cx| async move { cx.spawn_in(window, async move |picker, cx| {
async { async {
let members = search_members.await?; let members = search_members.await?;
picker.update(&mut cx, |picker, cx| { picker.update(cx, |picker, cx| {
picker.delegate.has_all_members = picker.delegate.has_all_members =
query.is_empty() && members.len() < 100; query.is_empty() && members.len() < 100;
picker.delegate.matching_member_indices = picker.delegate.matching_member_indices =
@ -338,10 +338,10 @@ impl PickerDelegate for ChannelModalDelegate {
let search_users = self let search_users = self
.user_store .user_store
.update(cx, |store, cx| store.fuzzy_search_users(query, cx)); .update(cx, |store, cx| store.fuzzy_search_users(query, cx));
cx.spawn_in(window, |picker, mut cx| async move { cx.spawn_in(window, async move |picker, cx| {
async { async {
let users = search_users.await?; let users = search_users.await?;
picker.update(&mut cx, |picker, cx| { picker.update(cx, |picker, cx| {
picker.delegate.matching_users = users; picker.delegate.matching_users = users;
cx.notify(); cx.notify();
})?; })?;
@ -489,9 +489,9 @@ impl ChannelModalDelegate {
let update = self.channel_store.update(cx, |store, cx| { let update = self.channel_store.update(cx, |store, cx| {
store.set_member_role(self.channel_id, user_id, new_role, cx) store.set_member_role(self.channel_id, user_id, new_role, cx)
}); });
cx.spawn_in(window, |picker, mut cx| async move { cx.spawn_in(window, async move |picker, cx| {
update.await?; update.await?;
picker.update_in(&mut cx, |picker, window, cx| { picker.update_in(cx, |picker, window, cx| {
let this = &mut picker.delegate; let this = &mut picker.delegate;
if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user_id) { if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user_id) {
member.role = new_role; member.role = new_role;
@ -513,9 +513,9 @@ impl ChannelModalDelegate {
let update = self.channel_store.update(cx, |store, cx| { let update = self.channel_store.update(cx, |store, cx| {
store.remove_member(self.channel_id, user_id, cx) store.remove_member(self.channel_id, user_id, cx)
}); });
cx.spawn_in(window, |picker, mut cx| async move { cx.spawn_in(window, async move |picker, cx| {
update.await?; update.await?;
picker.update_in(&mut cx, |picker, window, cx| { picker.update_in(cx, |picker, window, cx| {
let this = &mut picker.delegate; let this = &mut picker.delegate;
if let Some(ix) = this.members.iter_mut().position(|m| m.user.id == user_id) { if let Some(ix) = this.members.iter_mut().position(|m| m.user.id == user_id) {
this.members.remove(ix); this.members.remove(ix);
@ -551,10 +551,10 @@ impl ChannelModalDelegate {
store.invite_member(self.channel_id, user.id, ChannelRole::Member, cx) store.invite_member(self.channel_id, user.id, ChannelRole::Member, cx)
}); });
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
invite_member.await?; invite_member.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let new_member = ChannelMembership { let new_member = ChannelMembership {
user, user,
kind: proto::channel_member::Kind::Invitee, kind: proto::channel_member::Kind::Invitee,

View file

@ -102,10 +102,10 @@ impl PickerDelegate for ContactFinderDelegate {
.user_store .user_store
.update(cx, |store, cx| store.fuzzy_search_users(query, cx)); .update(cx, |store, cx| store.fuzzy_search_users(query, cx));
cx.spawn_in(window, |picker, mut cx| async move { cx.spawn_in(window, async move |picker, cx| {
async { async {
let potential_contacts = search_users.await?; let potential_contacts = search_users.await?;
picker.update(&mut cx, |picker, cx| { picker.update(cx, |picker, cx| {
picker.delegate.potential_contacts = potential_contacts.into(); picker.delegate.potential_contacts = potential_contacts.into();
cx.notify(); cx.notify();
})?; })?;

View file

@ -96,10 +96,10 @@ impl NotificationPanel {
cx.new(|cx| { cx.new(|cx| {
let mut status = client.status(); let mut status = client.status();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
while (status.next().await).is_some() { while (status.next().await).is_some() {
if this if this
.update(&mut cx, |_: &mut Self, cx| { .update(cx, |_: &mut Self, cx| {
cx.notify(); cx.notify();
}) })
.is_err() .is_err()
@ -181,7 +181,7 @@ impl NotificationPanel {
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
cx: AsyncWindowContext, cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let serialized_panel = if let Some(panel) = cx let serialized_panel = if let Some(panel) = cx
.background_spawn(async move { KEY_VALUE_STORE.read_kvp(NOTIFICATION_PANEL_KEY) }) .background_spawn(async move { KEY_VALUE_STORE.read_kvp(NOTIFICATION_PANEL_KEY) })
.await .await
@ -193,7 +193,7 @@ impl NotificationPanel {
None None
}; };
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
let panel = Self::new(workspace, window, cx); let panel = Self::new(workspace, window, cx);
if let Some(serialized_panel) = serialized_panel { if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| { panel.update(cx, |panel, cx| {
@ -445,12 +445,12 @@ impl NotificationPanel {
.entry(notification_id) .entry(notification_id)
.or_insert_with(|| { .or_insert_with(|| {
let client = self.client.clone(); let client = self.client.clone();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
cx.background_executor().timer(MARK_AS_READ_DELAY).await; cx.background_executor().timer(MARK_AS_READ_DELAY).await;
client client
.request(proto::MarkNotificationRead { notification_id }) .request(proto::MarkNotificationRead { notification_id })
.await?; .await?;
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.mark_as_read_tasks.remove(&notification_id); this.mark_as_read_tasks.remove(&notification_id);
})?; })?;
Ok(()) Ok(())
@ -556,9 +556,9 @@ impl NotificationPanel {
let notification_id = entry.id; let notification_id = entry.id;
self.current_notification_toast = Some(( self.current_notification_toast = Some((
notification_id, notification_id,
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
cx.background_executor().timer(TOAST_DURATION).await; cx.background_executor().timer(TOAST_DURATION).await;
this.update(&mut cx, |this, cx| this.remove_toast(notification_id, cx)) this.update(cx, |this, cx| this.remove_toast(notification_id, cx))
.ok(); .ok();
}), }),
)); ));
@ -643,7 +643,7 @@ impl Render for NotificationPanel {
move |_, window, cx| { move |_, window, cx| {
let client = client.clone(); let client = client.clone();
window window
.spawn(cx, move |cx| async move { .spawn(cx, async move |cx| {
client client
.authenticate_and_connect(true, &cx) .authenticate_and_connect(true, &cx)
.log_err() .log_err()

View file

@ -12,12 +12,12 @@ use workspace::AppState;
pub fn init(app_state: &Arc<AppState>, cx: &mut App) { pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
let app_state = Arc::downgrade(app_state); let app_state = Arc::downgrade(app_state);
let mut incoming_call = ActiveCall::global(cx).read(cx).incoming(); let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new(); let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new();
while let Some(incoming_call) = incoming_call.next().await { while let Some(incoming_call) = incoming_call.next().await {
for window in notification_windows.drain(..) { for window in notification_windows.drain(..) {
window window
.update(&mut cx, |_, window, _| { .update(cx, |_, window, _| {
window.remove_window(); window.remove_window();
}) })
.log_err(); .log_err();
@ -75,7 +75,7 @@ impl IncomingCallNotificationState {
let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id); let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
let app_state = self.app_state.clone(); let app_state = self.app_state.clone();
let cx: &mut App = cx; let cx: &mut App = cx;
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
join.await?; join.await?;
if let Some(project_id) = initial_project_id { if let Some(project_id) = initial_project_id {
cx.update(|cx| { cx.update(|cx| {

View file

@ -327,13 +327,13 @@ impl PickerDelegate for CommandPaletteDelegate {
}); });
self.updating_matches = Some((task, rx.clone())); self.updating_matches = Some((task, rx.clone()));
cx.spawn_in(window, move |picker, mut cx| async move { cx.spawn_in(window, async move |picker, cx| {
let Some((commands, matches)) = rx.recv().await else { let Some((commands, matches)) = rx.recv().await else {
return; return;
}; };
picker picker
.update(&mut cx, |picker, cx| { .update(cx, |picker, cx| {
picker picker
.delegate .delegate
.matches_updated(query, commands, matches, cx) .matches_updated(query, commands, matches, cx)

View file

@ -560,7 +560,7 @@ impl SerializableItem for ComponentPreview {
let user_store = project.read(cx).user_store().clone(); let user_store = project.read(cx).user_store().clone();
let language_registry = project.read(cx).languages().clone(); let language_registry = project.read(cx).languages().clone();
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let user_store = user_store.clone(); let user_store = user_store.clone();
let language_registry = language_registry.clone(); let language_registry = language_registry.clone();
let weak_workspace = workspace.clone(); let weak_workspace = workspace.clone();

View file

@ -171,13 +171,17 @@ impl Client {
let notification_handlers = notification_handlers.clone(); let notification_handlers = notification_handlers.clone();
let response_handlers = response_handlers.clone(); let response_handlers = response_handlers.clone();
let transport = transport.clone(); let transport = transport.clone();
move |cx| { async move |cx| {
Self::handle_input(transport, notification_handlers, response_handlers, cx) Self::handle_input(transport, notification_handlers, response_handlers, cx)
.log_err() .log_err()
.await
} }
}); });
let stderr_input_task = cx.spawn(|_| Self::handle_stderr(transport.clone()).log_err()); let stderr_input_task = cx.spawn({
let input_task = cx.spawn(|_| async move { let transport = transport.clone();
async move |_| Self::handle_stderr(transport).log_err().await
});
let input_task = cx.spawn(async move |_| {
let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task); let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
stdout.or(stderr) stdout.or(stderr)
}); });
@ -217,7 +221,7 @@ impl Client {
transport: Arc<dyn Transport>, transport: Arc<dyn Transport>,
notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>, notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>, response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
cx: AsyncApp, cx: &mut AsyncApp,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut receiver = transport.receive(); let mut receiver = transport.receive();

View file

@ -41,16 +41,15 @@ impl ExtensionContextServerProxy for ContextServerFactoryRegistryProxy {
let id = id.clone(); let id = id.clone();
let extension = extension.clone(); let extension = extension.clone();
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let extension_project = let extension_project = project.update(cx, |project, cx| {
project.update(&mut cx, |project, cx| { Arc::new(ExtensionProject {
Arc::new(ExtensionProject { worktree_ids: project
worktree_ids: project .visible_worktrees(cx)
.visible_worktrees(cx) .map(|worktree| worktree.read(cx).id().to_proto())
.map(|worktree| worktree.read(cx).id().to_proto()) .collect(),
.collect(), })
}) })?;
})?;
let command = extension let command = extension
.context_server_command(id.clone(), extension_project) .context_server_command(id.clone(), extension_project)

View file

@ -147,14 +147,14 @@ impl ContextServerManager {
if self.update_servers_task.is_some() { if self.update_servers_task.is_some() {
self.needs_server_update = true; self.needs_server_update = true;
} else { } else {
self.update_servers_task = Some(cx.spawn(|this, mut cx| async move { self.update_servers_task = Some(cx.spawn(async move |this, cx| {
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.needs_server_update = false; this.needs_server_update = false;
})?; })?;
Self::maintain_servers(this.clone(), cx.clone()).await?; Self::maintain_servers(this.clone(), cx).await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let has_any_context_servers = !this.servers().is_empty(); let has_any_context_servers = !this.servers().is_empty();
if has_any_context_servers { if has_any_context_servers {
CommandPaletteFilter::update_global(cx, |filter, _cx| { CommandPaletteFilter::update_global(cx, |filter, _cx| {
@ -186,13 +186,13 @@ impl ContextServerManager {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<anyhow::Result<()>> { ) -> Task<anyhow::Result<()>> {
let id = id.clone(); let id = id.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
if let Some(server) = this.update(&mut cx, |this, _cx| this.servers.remove(&id))? { if let Some(server) = this.update(cx, |this, _cx| this.servers.remove(&id))? {
server.stop()?; server.stop()?;
let config = server.config(); let config = server.config();
let new_server = Arc::new(ContextServer::new(id.clone(), config)); let new_server = Arc::new(ContextServer::new(id.clone(), config));
new_server.clone().start(&cx).await?; new_server.clone().start(&cx).await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.servers.insert(id.clone(), new_server); this.servers.insert(id.clone(), new_server);
cx.emit(Event::ServerStopped { cx.emit(Event::ServerStopped {
server_id: id.clone(), server_id: id.clone(),
@ -214,10 +214,10 @@ impl ContextServerManager {
.collect() .collect()
} }
async fn maintain_servers(this: WeakEntity<Self>, mut cx: AsyncApp) -> Result<()> { async fn maintain_servers(this: WeakEntity<Self>, cx: &mut AsyncApp) -> Result<()> {
let mut desired_servers = HashMap::default(); let mut desired_servers = HashMap::default();
let (registry, project) = this.update(&mut cx, |this, cx| { let (registry, project) = this.update(cx, |this, cx| {
let location = this.project.read(cx).worktrees(cx).next().map(|worktree| { let location = this.project.read(cx).worktrees(cx).next().map(|worktree| {
settings::SettingsLocation { settings::SettingsLocation {
worktree_id: worktree.read(cx).id(), worktree_id: worktree.read(cx).id(),
@ -231,7 +231,7 @@ impl ContextServerManager {
})?; })?;
for (id, factory) in for (id, factory) in
registry.read_with(&cx, |registry, _| registry.context_server_factories())? registry.read_with(cx, |registry, _| registry.context_server_factories())?
{ {
let config = desired_servers.entry(id).or_default(); let config = desired_servers.entry(id).or_default();
if config.command.is_none() { if config.command.is_none() {
@ -244,7 +244,7 @@ impl ContextServerManager {
let mut servers_to_start = HashMap::default(); let mut servers_to_start = HashMap::default();
let mut servers_to_stop = HashMap::default(); let mut servers_to_stop = HashMap::default();
this.update(&mut cx, |this, _cx| { this.update(cx, |this, _cx| {
this.servers.retain(|id, server| { this.servers.retain(|id, server| {
if desired_servers.contains_key(id) { if desired_servers.contains_key(id) {
true true
@ -270,16 +270,12 @@ impl ContextServerManager {
for (id, server) in servers_to_stop { for (id, server) in servers_to_stop {
server.stop().log_err(); server.stop().log_err();
this.update(&mut cx, |_, cx| { this.update(cx, |_, cx| cx.emit(Event::ServerStopped { server_id: id }))?;
cx.emit(Event::ServerStopped { server_id: id })
})?;
} }
for (id, server) in servers_to_start { for (id, server) in servers_to_start {
if server.start(&cx).await.log_err().is_some() { if server.start(&cx).await.log_err().is_some() {
this.update(&mut cx, |_, cx| { this.update(cx, |_, cx| cx.emit(Event::ServerStarted { server_id: id }))?;
cx.emit(Event::ServerStarted { server_id: id })
})?;
} }
} }

View file

@ -47,13 +47,13 @@ impl StdioTransport {
let (stdout_sender, stdout_receiver) = channel::unbounded::<String>(); let (stdout_sender, stdout_receiver) = channel::unbounded::<String>();
let (stderr_sender, stderr_receiver) = channel::unbounded::<String>(); let (stderr_sender, stderr_receiver) = channel::unbounded::<String>();
cx.spawn(|_| Self::handle_output(stdin, stdout_receiver).log_err()) cx.spawn(async move |_| Self::handle_output(stdin, stdout_receiver).log_err().await)
.detach(); .detach();
cx.spawn(|_| async move { Self::handle_input(stdout, stdin_sender).await }) cx.spawn(async move |_| Self::handle_input(stdout, stdin_sender).await)
.detach(); .detach();
cx.spawn(|_| async move { Self::handle_err(stderr, stderr_sender).await }) cx.spawn(async move |_| Self::handle_err(stderr, stderr_sender).await)
.detach(); .detach();
Ok(Self { Ok(Self {

View file

@ -226,17 +226,17 @@ impl RegisteredBuffer {
let id = buffer.entity_id(); let id = buffer.entity_id();
let prev_pending_change = let prev_pending_change =
mem::replace(&mut self.pending_buffer_change, Task::ready(None)); mem::replace(&mut self.pending_buffer_change, Task::ready(None));
self.pending_buffer_change = cx.spawn(move |copilot, mut cx| async move { self.pending_buffer_change = cx.spawn(async move |copilot, cx| {
prev_pending_change.await; prev_pending_change.await;
let old_version = copilot let old_version = copilot
.update(&mut cx, |copilot, _| { .update(cx, |copilot, _| {
let server = copilot.server.as_authenticated().log_err()?; let server = copilot.server.as_authenticated().log_err()?;
let buffer = server.registered_buffers.get_mut(&id)?; let buffer = server.registered_buffers.get_mut(&id)?;
Some(buffer.snapshot.version.clone()) Some(buffer.snapshot.version.clone())
}) })
.ok()??; .ok()??;
let new_snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot()).ok()?; let new_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()).ok()?;
let content_changes = cx let content_changes = cx
.background_spawn({ .background_spawn({
@ -265,7 +265,7 @@ impl RegisteredBuffer {
.await; .await;
copilot copilot
.update(&mut cx, |copilot, _| { .update(cx, |copilot, _| {
let server = copilot.server.as_authenticated().log_err()?; let server = copilot.server.as_authenticated().log_err()?;
let buffer = server.registered_buffers.get_mut(&id)?; let buffer = server.registered_buffers.get_mut(&id)?;
if !content_changes.is_empty() { if !content_changes.is_empty() {
@ -388,7 +388,7 @@ impl Copilot {
let node_runtime = self.node_runtime.clone(); let node_runtime = self.node_runtime.clone();
let env = self.build_env(&language_settings.edit_predictions.copilot); let env = self.build_env(&language_settings.edit_predictions.copilot);
let start_task = cx let start_task = cx
.spawn(move |this, cx| { .spawn(async move |this, cx| {
Self::start_language_server( Self::start_language_server(
server_id, server_id,
http, http,
@ -398,6 +398,7 @@ impl Copilot {
awaiting_sign_in_after_start, awaiting_sign_in_after_start,
cx, cx,
) )
.await
}) })
.shared(); .shared();
self.server = CopilotServer::Starting { task: start_task }; self.server = CopilotServer::Starting { task: start_task };
@ -442,7 +443,7 @@ impl Copilot {
}, },
"copilot".into(), "copilot".into(),
Default::default(), Default::default(),
cx.to_async(), &mut cx.to_async(),
); );
let http = http_client::FakeHttpClient::create(|_| async { unreachable!() }); let http = http_client::FakeHttpClient::create(|_| async { unreachable!() });
let node_runtime = NodeRuntime::unavailable(); let node_runtime = NodeRuntime::unavailable();
@ -468,7 +469,7 @@ impl Copilot {
env: Option<HashMap<String, String>>, env: Option<HashMap<String, String>>,
this: WeakEntity<Self>, this: WeakEntity<Self>,
awaiting_sign_in_after_start: bool, awaiting_sign_in_after_start: bool,
mut cx: AsyncApp, cx: &mut AsyncApp,
) { ) {
let start_language_server = async { let start_language_server = async {
let server_path = get_copilot_lsp(http).await?; let server_path = get_copilot_lsp(http).await?;
@ -495,7 +496,7 @@ impl Copilot {
root_path, root_path,
None, None,
Default::default(), Default::default(),
cx.clone(), cx,
)?; )?;
server server
@ -535,7 +536,7 @@ impl Copilot {
}; };
let server = start_language_server.await; let server = start_language_server.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
cx.notify(); cx.notify();
match server { match server {
Ok((server, status)) => { Ok((server, status)) => {
@ -569,7 +570,7 @@ impl Copilot {
SignInStatus::SignedOut { .. } | SignInStatus::Unauthorized { .. } => { SignInStatus::SignedOut { .. } | SignInStatus::Unauthorized { .. } => {
let lsp = server.lsp.clone(); let lsp = server.lsp.clone();
let task = cx let task = cx
.spawn(|this, mut cx| async move { .spawn(async move |this, cx| {
let sign_in = async { let sign_in = async {
let sign_in = lsp let sign_in = lsp
.request::<request::SignInInitiate>( .request::<request::SignInInitiate>(
@ -581,7 +582,7 @@ impl Copilot {
Ok(request::SignInStatus::Ok { user: Some(user) }) Ok(request::SignInStatus::Ok { user: Some(user) })
} }
request::SignInInitiateResult::PromptUserDeviceFlow(flow) => { request::SignInInitiateResult::PromptUserDeviceFlow(flow) => {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if let CopilotServer::Running(RunningCopilotServer { if let CopilotServer::Running(RunningCopilotServer {
sign_in_status: status, sign_in_status: status,
.. ..
@ -610,7 +611,7 @@ impl Copilot {
}; };
let sign_in = sign_in.await; let sign_in = sign_in.await;
this.update(&mut cx, |this, cx| match sign_in { this.update(cx, |this, cx| match sign_in {
Ok(status) => { Ok(status) => {
this.update_sign_in_status(status, cx); this.update_sign_in_status(status, cx);
Ok(()) Ok(())
@ -670,7 +671,7 @@ impl Copilot {
let http = self.http.clone(); let http = self.http.clone();
let node_runtime = self.node_runtime.clone(); let node_runtime = self.node_runtime.clone();
let server_id = self.server_id; let server_id = self.server_id;
move |this, cx| async move { async move |this, cx| {
clear_copilot_dir().await; clear_copilot_dir().await;
Self::start_language_server(server_id, http, node_runtime, env, this, false, cx) Self::start_language_server(server_id, http, node_runtime, env, this, false, cx)
.await .await

View file

@ -241,7 +241,7 @@ impl CopilotChat {
let config_paths: HashSet<PathBuf> = copilot_chat_config_paths().into_iter().collect(); let config_paths: HashSet<PathBuf> = copilot_chat_config_paths().into_iter().collect();
let dir_path = copilot_chat_config_dir(); let dir_path = copilot_chat_config_dir();
cx.spawn(|cx| async move { cx.spawn(async move |cx| {
let mut parent_watch_rx = watch_config_dir( let mut parent_watch_rx = watch_config_dir(
cx.background_executor(), cx.background_executor(),
fs.clone(), fs.clone(),

View file

@ -83,7 +83,7 @@ impl EditPredictionProvider for CopilotCompletionProvider {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let copilot = self.copilot.clone(); let copilot = self.copilot.clone();
self.pending_refresh = Some(cx.spawn(|this, mut cx| async move { self.pending_refresh = Some(cx.spawn(async move |this, cx| {
if debounce { if debounce {
cx.background_executor() cx.background_executor()
.timer(COPILOT_DEBOUNCE_TIMEOUT) .timer(COPILOT_DEBOUNCE_TIMEOUT)
@ -91,12 +91,12 @@ impl EditPredictionProvider for CopilotCompletionProvider {
} }
let completions = copilot let completions = copilot
.update(&mut cx, |copilot, cx| { .update(cx, |copilot, cx| {
copilot.completions(&buffer, cursor_position, cx) copilot.completions(&buffer, cursor_position, cx)
})? })?
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if !completions.is_empty() { if !completions.is_empty() {
this.cycled = false; this.cycled = false;
this.pending_refresh = None; this.pending_refresh = None;
@ -153,14 +153,14 @@ impl EditPredictionProvider for CopilotCompletionProvider {
cx.notify(); cx.notify();
} else { } else {
let copilot = self.copilot.clone(); let copilot = self.copilot.clone();
self.pending_cycling_refresh = Some(cx.spawn(|this, mut cx| async move { self.pending_cycling_refresh = Some(cx.spawn(async move |this, cx| {
let completions = copilot let completions = copilot
.update(&mut cx, |copilot, cx| { .update(cx, |copilot, cx| {
copilot.completions_cycling(&buffer, cursor_position, cx) copilot.completions_cycling(&buffer, cursor_position, cx)
})? })?
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.cycled = true; this.cycled = true;
this.file_extension = buffer.read(cx).file().and_then(|file| { this.file_extension = buffer.read(cx).file().and_then(|file| {
Some( Some(

View file

@ -37,11 +37,11 @@ pub fn initiate_sign_in(window: &mut Window, cx: &mut App) {
}); });
let workspace = workspace.downgrade(); let workspace = workspace.downgrade();
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
task.await; task.await;
if let Some(copilot) = cx.update(|cx| Copilot::global(cx)).ok().flatten() { if let Some(copilot) = cx.update(|cx| Copilot::global(cx)).ok().flatten() {
workspace workspace
.update(&mut cx, |workspace, cx| match copilot.read(cx).status() { .update(cx, |workspace, cx| match copilot.read(cx).status() {
Status::Authorized => workspace.show_toast( Status::Authorized => workspace.show_toast(
Toast::new( Toast::new(
NotificationId::unique::<CopilotStartingToast>(), NotificationId::unique::<CopilotStartingToast>(),

View file

@ -583,7 +583,7 @@ impl TcpTransport {
_ = cx.background_executor().timer(Duration::from_millis(timeout)).fuse() => { _ = cx.background_executor().timer(Duration::from_millis(timeout)).fuse() => {
return Err(anyhow!(format!("Connection to TCP DAP timeout {}:{}", host, port))) return Err(anyhow!(format!("Connection to TCP DAP timeout {}:{}", host, port)))
}, },
result = cx.spawn(|cx| async move { result = cx.spawn(async move |cx| {
loop { loop {
match TcpStream::connect(address).await { match TcpStream::connect(address).await {
Ok(stream) => return stream.split(), Ok(stream) => return stream.split(),

View file

@ -103,10 +103,10 @@ impl DebugAdapterState {
impl LogStore { impl LogStore {
fn new(cx: &Context<Self>) -> Self { fn new(cx: &Context<Self>) -> Self {
let (rpc_tx, mut rpc_rx) = unbounded::<(SessionId, IoKind, String)>(); let (rpc_tx, mut rpc_rx) = unbounded::<(SessionId, IoKind, String)>();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
while let Some((client_id, io_kind, message)) = rpc_rx.next().await { while let Some((client_id, io_kind, message)) = rpc_rx.next().await {
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.on_rpc_log(client_id, io_kind, &message, cx); this.on_rpc_log(client_id, io_kind, &message, cx);
})?; })?;
} }
@ -118,10 +118,10 @@ impl LogStore {
.detach_and_log_err(cx); .detach_and_log_err(cx);
let (adapter_log_tx, mut adapter_log_rx) = unbounded::<(SessionId, IoKind, String)>(); let (adapter_log_tx, mut adapter_log_rx) = unbounded::<(SessionId, IoKind, String)>();
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
while let Some((client_id, io_kind, message)) = adapter_log_rx.next().await { while let Some((client_id, io_kind, message)) = adapter_log_rx.next().await {
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.on_adapter_log(client_id, io_kind, &message, cx); this.on_adapter_log(client_id, io_kind, &message, cx);
})?; })?;
} }
@ -604,9 +604,9 @@ impl DapLogView {
.update(cx, |_, cx| { .update(cx, |_, cx| {
cx.spawn({ cx.spawn({
let buffer = cx.entity(); let buffer = cx.entity();
|_, mut cx| async move { async move |_, cx| {
let language = language.await.ok(); let language = language.await.ok();
buffer.update(&mut cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.set_language(language, cx); buffer.set_language(language, cx);
}) })
} }

View file

@ -114,9 +114,9 @@ impl PickerDelegate for AttachModalDelegate {
_window: &mut Window, _window: &mut Window,
cx: &mut Context<Picker<Self>>, cx: &mut Context<Picker<Self>>,
) -> gpui::Task<()> { ) -> gpui::Task<()> {
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
let Some(processes) = this let Some(processes) = this
.update(&mut cx, |this, _| { .update(cx, |this, _| {
if let Some(processes) = this.delegate.candidates.clone() { if let Some(processes) = this.delegate.candidates.clone() {
processes processes
} else { } else {
@ -172,7 +172,7 @@ impl PickerDelegate for AttachModalDelegate {
) )
.await; .await;
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
let delegate = &mut this.delegate; let delegate = &mut this.delegate;
delegate.matches = matches; delegate.matches = matches;

View file

@ -150,8 +150,8 @@ impl DebugPanel {
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
cx: AsyncWindowContext, cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> { ) -> Task<Result<Entity<Self>>> {
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
let debug_panel = DebugPanel::new(workspace, window, cx); let debug_panel = DebugPanel::new(workspace, window, cx);
cx.observe(&debug_panel, |_, debug_panel, cx| { cx.observe(&debug_panel, |_, debug_panel, cx| {
@ -349,11 +349,11 @@ impl DebugPanel {
cx, cx,
); );
cx.spawn(|_, mut cx| async move { cx.spawn(async move |_, cx| {
let pid_task = async move { let pid_task = async move {
let terminal = terminal_task.await?; let terminal = terminal_task.await?;
terminal.read_with(&mut cx, |terminal, _| terminal.pty_info.pid()) terminal.read_with(cx, |terminal, _| terminal.pty_info.pid())
}; };
pid_task.await pid_task.await

View file

@ -213,8 +213,8 @@ impl Render for Console {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let session = self.session.clone(); let session = self.session.clone();
let token = self.last_token; let token = self.last_token;
self.update_output_task = cx.spawn_in(window, move |this, mut cx| async move { self.update_output_task = cx.spawn_in(window, async move |this, cx| {
_ = session.update_in(&mut cx, move |session, window, cx| { _ = session.update_in(cx, move |session, window, cx| {
let (output, last_processed_token) = session.output(token); let (output, last_processed_token) = session.output(token);
_ = this.update(cx, |this, cx| { _ = this.update(cx, |this, cx| {
@ -342,7 +342,7 @@ impl ConsoleQueryBarCompletionProvider {
let query = buffer.read(cx).text(); let query = buffer.read(cx).text();
cx.spawn(|_, cx| async move { cx.spawn(async move |_, cx| {
let matches = fuzzy::match_strings( let matches = fuzzy::match_strings(
&string_matches, &string_matches,
&query, &query,

View file

@ -60,9 +60,9 @@ impl ModuleList {
} }
fn open_module(&mut self, path: Arc<Path>, window: &mut Window, cx: &mut Context<Self>) { fn open_module(&mut self, path: Arc<Path>, window: &mut Window, cx: &mut Context<Self>) {
cx.spawn_in(window, move |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let (worktree, relative_path) = this let (worktree, relative_path) = this
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.workspace.update(cx, |workspace, cx| { this.workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |this, cx| { workspace.project().update(cx, |this, cx| {
this.find_or_create_worktree(&path, false, cx) this.find_or_create_worktree(&path, false, cx)
@ -72,7 +72,7 @@ impl ModuleList {
.await?; .await?;
let buffer = this let buffer = this
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.workspace.update(cx, |this, cx| { this.workspace.update(cx, |this, cx| {
this.project().update(cx, |this, cx| { this.project().update(cx, |this, cx| {
let worktree_id = worktree.read(cx).id(); let worktree_id = worktree.read(cx).id();
@ -88,7 +88,7 @@ impl ModuleList {
})?? })??
.await?; .await?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.workspace.update(cx, |workspace, cx| { this.workspace.update(cx, |workspace, cx| {
let project_path = buffer.read(cx).project_path(cx).ok_or_else(|| { let project_path = buffer.read(cx).project_path(cx).ok_or_else(|| {
anyhow!("Could not select a stack frame for unnamed buffer") anyhow!("Could not select a stack frame for unnamed buffer")

View file

@ -235,9 +235,9 @@ impl StackFrameList {
return Task::ready(Err(anyhow!("Project path not found"))); return Task::ready(Err(anyhow!("Project path not found")));
}; };
cx.spawn_in(window, move |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let (worktree, relative_path) = this let (worktree, relative_path) = this
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.workspace.update(cx, |workspace, cx| { this.workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |this, cx| { workspace.project().update(cx, |this, cx| {
this.find_or_create_worktree(&abs_path, false, cx) this.find_or_create_worktree(&abs_path, false, cx)
@ -246,7 +246,7 @@ impl StackFrameList {
})?? })??
.await?; .await?;
let buffer = this let buffer = this
.update(&mut cx, |this, cx| { .update(cx, |this, cx| {
this.workspace.update(cx, |this, cx| { this.workspace.update(cx, |this, cx| {
this.project().update(cx, |this, cx| { this.project().update(cx, |this, cx| {
let worktree_id = worktree.read(cx).id(); let worktree_id = worktree.read(cx).id();
@ -261,10 +261,10 @@ impl StackFrameList {
}) })
})?? })??
.await?; .await?;
let position = buffer.update(&mut cx, |this, _| { let position = buffer.update(cx, |this, _| {
this.snapshot().anchor_after(PointUtf16::new(row, 0)) this.snapshot().anchor_after(PointUtf16::new(row, 0))
})?; })?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.workspace.update(cx, |workspace, cx| { this.workspace.update(cx, |workspace, cx| {
let project_path = buffer.read(cx).project_path(cx).ok_or_else(|| { let project_path = buffer.read(cx).project_path(cx).ok_or_else(|| {
anyhow!("Could not select a stack frame for unnamed buffer") anyhow!("Could not select a stack frame for unnamed buffer")
@ -282,7 +282,7 @@ impl StackFrameList {
})??? })???
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.workspace.update(cx, |workspace, cx| { this.workspace.update(cx, |workspace, cx| {
let breakpoint_store = workspace.project().read(cx).breakpoint_store(); let breakpoint_store = workspace.project().read(cx).breakpoint_store();

View file

@ -29,10 +29,10 @@ impl StartingState {
task: Task<Result<Entity<Session>>>, task: Task<Result<Entity<Session>>>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Self { ) -> Self {
let _notify_parent = cx.spawn(move |this, mut cx| async move { let _notify_parent = cx.spawn(async move |this, cx| {
let entity = task.await; let entity = task.await;
this.update(&mut cx, |_, cx| { this.update(cx, |_, cx| {
if let Ok(entity) = entity { if let Ok(entity) = entity {
cx.emit(StartingEvent::Finished(entity)) cx.emit(StartingEvent::Finished(entity))
} else { } else {

View file

@ -40,13 +40,21 @@ pub async fn init_test_workspace(
cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
let debugger_panel = workspace_handle let debugger_panel = workspace_handle
.update(cx, |_, window, cx| cx.spawn_in(window, DebugPanel::load)) .update(cx, |_, window, cx| {
cx.spawn_in(window, async move |this, cx| {
DebugPanel::load(this, cx.clone()).await
})
})
.unwrap() .unwrap()
.await .await
.expect("Failed to load debug panel"); .expect("Failed to load debug panel");
let terminal_panel = workspace_handle let terminal_panel = workspace_handle
.update(cx, |_, window, cx| cx.spawn_in(window, TerminalPanel::load)) .update(cx, |_, window, cx| {
cx.spawn_in(window, async |this, cx| {
TerminalPanel::load(this, cx.clone()).await
})
})
.unwrap() .unwrap()
.await .await
.expect("Failed to load terminal panel"); .expect("Failed to load terminal panel");

View file

@ -251,12 +251,12 @@ impl ProjectDiagnosticsEditor {
return; return;
} }
let project_handle = self.project.clone(); let project_handle = self.project.clone();
self.update_excerpts_task = Some(cx.spawn_in(window, |this, mut cx| async move { self.update_excerpts_task = Some(cx.spawn_in(window, async move |this, cx| {
cx.background_executor() cx.background_executor()
.timer(DIAGNOSTICS_UPDATE_DEBOUNCE) .timer(DIAGNOSTICS_UPDATE_DEBOUNCE)
.await; .await;
loop { loop {
let Some((path, language_server_id)) = this.update(&mut cx, |this, _| { let Some((path, language_server_id)) = this.update(cx, |this, _| {
let Some((path, language_server_id)) = this.paths_to_update.pop_first() else { let Some((path, language_server_id)) = this.paths_to_update.pop_first() else {
this.update_excerpts_task.take(); this.update_excerpts_task.take();
return None; return None;
@ -268,11 +268,11 @@ impl ProjectDiagnosticsEditor {
}; };
if let Some(buffer) = project_handle if let Some(buffer) = project_handle
.update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))? .update(cx, |project, cx| project.open_buffer(path.clone(), cx))?
.await .await
.log_err() .log_err()
{ {
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.update_excerpts(path, language_server_id, buffer, window, cx) this.update_excerpts(path, language_server_id, buffer, window, cx)
})? })?
.await?; .await?;
@ -419,9 +419,9 @@ impl ProjectDiagnosticsEditor {
let excerpts = self.excerpts.clone().downgrade(); let excerpts = self.excerpts.clone().downgrade();
let context = self.context; let context = self.context;
let editor = self.editor.clone().downgrade(); let editor = self.editor.clone().downgrade();
cx.spawn_in(window, move |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let mut old_groups = this let mut old_groups = this
.update(&mut cx, |this, _| { .update(cx, |this, _| {
mem::take(&mut this.path_states[path_ix].diagnostic_groups) mem::take(&mut this.path_states[path_ix].diagnostic_groups)
})? })?
.into_iter() .into_iter()
@ -491,7 +491,7 @@ impl ProjectDiagnosticsEditor {
entry.range.clone(), entry.range.clone(),
context, context,
snapshot.clone(), snapshot.clone(),
(*cx).clone(), (**cx).clone(),
) )
.await, .await,
) )
@ -507,7 +507,7 @@ impl ProjectDiagnosticsEditor {
} }
} }
let excerpt_id = excerpts.update(&mut cx, |excerpts, cx| { let excerpt_id = excerpts.update(cx, |excerpts, cx| {
excerpts excerpts
.insert_excerpts_after( .insert_excerpts_after(
prev_excerpt_id, prev_excerpt_id,
@ -575,14 +575,14 @@ impl ProjectDiagnosticsEditor {
} }
} }
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
new_group_ixs.push(this.path_states[path_ix].diagnostic_groups.len()); new_group_ixs.push(this.path_states[path_ix].diagnostic_groups.len());
this.path_states[path_ix] this.path_states[path_ix]
.diagnostic_groups .diagnostic_groups
.push(group_state); .push(group_state);
})?; })?;
} else if let Some((_, group_state)) = to_remove { } else if let Some((_, group_state)) = to_remove {
excerpts.update(&mut cx, |excerpts, cx| { excerpts.update(cx, |excerpts, cx| {
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx) excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx)
})?; })?;
blocks_to_remove.extend(group_state.blocks.iter().copied()); blocks_to_remove.extend(group_state.blocks.iter().copied());
@ -590,7 +590,7 @@ impl ProjectDiagnosticsEditor {
prev_excerpt_id = *group_state.excerpts.last().unwrap(); prev_excerpt_id = *group_state.excerpts.last().unwrap();
first_excerpt_id.get_or_insert(prev_excerpt_id); first_excerpt_id.get_or_insert(prev_excerpt_id);
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.path_states[path_ix] this.path_states[path_ix]
.diagnostic_groups .diagnostic_groups
.push(group_state) .push(group_state)
@ -598,9 +598,8 @@ impl ProjectDiagnosticsEditor {
} }
} }
let excerpts_snapshot = let excerpts_snapshot = excerpts.update(cx, |excerpts, cx| excerpts.snapshot(cx))?;
excerpts.update(&mut cx, |excerpts, cx| excerpts.snapshot(cx))?; editor.update(cx, |editor, cx| {
editor.update(&mut cx, |editor, cx| {
editor.remove_blocks(blocks_to_remove, None, cx); editor.remove_blocks(blocks_to_remove, None, cx);
let block_ids = editor.insert_blocks( let block_ids = editor.insert_blocks(
blocks_to_add.into_iter().flat_map(|block| { blocks_to_add.into_iter().flat_map(|block| {
@ -644,7 +643,7 @@ impl ProjectDiagnosticsEditor {
Result::<(), anyhow::Error>::Ok(()) Result::<(), anyhow::Error>::Ok(())
})??; })??;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
if this.path_states[path_ix].diagnostic_groups.is_empty() { if this.path_states[path_ix].diagnostic_groups.is_empty() {
this.path_states.remove(path_ix); this.path_states.remove(path_ix);
} }
@ -709,7 +708,7 @@ impl ProjectDiagnosticsEditor {
}); });
})?; })?;
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
if this.path_states.is_empty() { if this.path_states.is_empty() {
if this.editor.focus_handle(cx).is_focused(window) { if this.editor.focus_handle(cx).is_focused(window) {
window.focus(&this.focus_handle); window.focus(&this.focus_handle);
@ -1053,7 +1052,7 @@ fn context_range_for_entry(
snapshot: BufferSnapshot, snapshot: BufferSnapshot,
cx: AsyncApp, cx: AsyncApp,
) -> Task<Range<Point>> { ) -> Task<Range<Point>> {
cx.spawn(move |cx| async move { cx.spawn(async move |cx| {
if let Some(rows) = heuristic_syntactic_expand( if let Some(rows) = heuristic_syntactic_expand(
range.clone(), range.clone(),
DIAGNOSTIC_EXPANSION_ROW_LIMIT, DIAGNOSTIC_EXPANSION_ROW_LIMIT,
@ -1083,7 +1082,7 @@ async fn heuristic_syntactic_expand(
input_range: Range<Point>, input_range: Range<Point>,
max_row_count: u32, max_row_count: u32,
snapshot: BufferSnapshot, snapshot: BufferSnapshot,
cx: AsyncApp, cx: &mut AsyncApp,
) -> Option<RangeInclusive<BufferRow>> { ) -> Option<RangeInclusive<BufferRow>> {
let input_row_count = input_range.end.row - input_range.start.row; let input_row_count = input_range.end.row - input_range.start.row;
if input_row_count > max_row_count { if input_row_count > max_row_count {

View file

@ -163,12 +163,12 @@ impl DiagnosticIndicator {
.map(|entry| entry.diagnostic); .map(|entry| entry.diagnostic);
if new_diagnostic != self.current_diagnostic { if new_diagnostic != self.current_diagnostic {
self.diagnostics_update = self.diagnostics_update =
cx.spawn_in(window, |diagnostics_indicator, mut cx| async move { cx.spawn_in(window, async move |diagnostics_indicator, cx| {
cx.background_executor() cx.background_executor()
.timer(Duration::from_millis(50)) .timer(Duration::from_millis(50))
.await; .await;
diagnostics_indicator diagnostics_indicator
.update(&mut cx, |diagnostics_indicator, cx| { .update(cx, |diagnostics_indicator, cx| {
diagnostics_indicator.current_diagnostic = new_diagnostic; diagnostics_indicator.current_diagnostic = new_diagnostic;
cx.notify(); cx.notify();
}) })

View file

@ -40,9 +40,9 @@ impl BlinkManager {
let epoch = self.next_blink_epoch(); let epoch = self.next_blink_epoch();
let interval = self.blink_interval; let interval = self.blink_interval;
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
Timer::after(interval).await; Timer::after(interval).await;
this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx)) this.update(cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
}) })
.detach(); .detach();
} }
@ -62,10 +62,10 @@ impl BlinkManager {
let epoch = self.next_blink_epoch(); let epoch = self.next_blink_epoch();
let interval = self.blink_interval; let interval = self.blink_interval;
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
Timer::after(interval).await; Timer::after(interval).await;
if let Some(this) = this.upgrade() { if let Some(this) = this.upgrade() {
this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx)) this.update(cx, |this, cx| this.blink_cursors(epoch, cx))
.ok(); .ok();
} }
}) })

View file

@ -51,7 +51,7 @@ pub fn switch_source_header(
cx, cx,
) )
}); });
cx.spawn_in(window, |_editor, mut cx| async move { cx.spawn_in(window, async move |_editor, cx| {
let switch_source_header = switch_source_header_task let switch_source_header = switch_source_header_task
.await .await
.with_context(|| format!("Switch source/header LSP request for path \"{source_file}\" failed"))?; .with_context(|| format!("Switch source/header LSP request for path \"{source_file}\" failed"))?;
@ -72,7 +72,7 @@ pub fn switch_source_header(
})?; })?;
workspace workspace
.update_in(&mut cx, |workspace, window, cx| { .update_in(cx, |workspace, window, cx| {
workspace.open_abs_path(path, OpenOptions { visible: Some(OpenVisible::None), ..Default::default() }, window, cx) workspace.open_abs_path(path, OpenOptions { visible: Some(OpenVisible::None), ..Default::default() }, window, cx)
}) })
.with_context(|| { .with_context(|| {

View file

@ -416,9 +416,9 @@ impl CompletionsMenu {
cx, cx,
); );
cx.spawn(move |editor, mut cx| async move { cx.spawn(async move |editor, cx| {
if let Some(true) = resolve_task.await.log_err() { if let Some(true) = resolve_task.await.log_err() {
editor.update(&mut cx, |_, cx| cx.notify()).ok(); editor.update(cx, |_, cx| cx.notify()).ok();
} }
}) })
.detach(); .detach();

View file

@ -198,9 +198,9 @@ impl WrapMap {
self.edits_since_sync = self.edits_since_sync.compose(&edits); self.edits_since_sync = self.edits_since_sync.compose(&edits);
} }
Err(wrap_task) => { Err(wrap_task) => {
self.background_task = Some(cx.spawn(|this, mut cx| async move { self.background_task = Some(cx.spawn(async move |this, cx| {
let (snapshot, edits) = wrap_task.await; let (snapshot, edits) = wrap_task.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.snapshot = snapshot; this.snapshot = snapshot;
this.edits_since_sync = this this.edits_since_sync = this
.edits_since_sync .edits_since_sync
@ -276,9 +276,9 @@ impl WrapMap {
self.edits_since_sync = self.edits_since_sync.compose(&output_edits); self.edits_since_sync = self.edits_since_sync.compose(&output_edits);
} }
Err(update_task) => { Err(update_task) => {
self.background_task = Some(cx.spawn(|this, mut cx| async move { self.background_task = Some(cx.spawn(async move |this, cx| {
let (snapshot, edits) = update_task.await; let (snapshot, edits) = update_task.await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.snapshot = snapshot; this.snapshot = snapshot;
this.edits_since_sync = this this.edits_since_sync = this
.edits_since_sync .edits_since_sync

View file

@ -185,7 +185,7 @@ use ui::{
h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key, h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
Tooltip, Tooltip,
}; };
use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt}; use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::{ use workspace::{
item::{ItemHandle, PreviewTabsSettings}, item::{ItemHandle, PreviewTabsSettings},
ItemId, RestoreOnStartupBehavior, ItemId, RestoreOnStartupBehavior,
@ -1715,9 +1715,9 @@ impl Editor {
let project = workspace.project().clone(); let project = workspace.project().clone();
let create = project.update(cx, |project, cx| project.create_buffer(cx)); let create = project.update(cx, |project, cx| project.create_buffer(cx));
cx.spawn_in(window, |workspace, mut cx| async move { cx.spawn_in(window, async move |workspace, cx| {
let buffer = create.await?; let buffer = create.await?;
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
let editor = let editor =
cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)); cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx); workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
@ -1753,9 +1753,9 @@ impl Editor {
let project = workspace.project().clone(); let project = workspace.project().clone();
let create = project.update(cx, |project, cx| project.create_buffer(cx)); let create = project.update(cx, |project, cx| project.create_buffer(cx));
cx.spawn_in(window, |workspace, mut cx| async move { cx.spawn_in(window, async move |workspace, cx| {
let buffer = create.await?; let buffer = create.await?;
workspace.update_in(&mut cx, move |workspace, window, cx| { workspace.update_in(cx, move |workspace, window, cx| {
workspace.split_item( workspace.split_item(
direction, direction,
Box::new( Box::new(
@ -2184,12 +2184,12 @@ impl Editor {
drop(context_menu); drop(context_menu);
let query = Self::completion_query(buffer, cursor_position); let query = Self::completion_query(buffer, cursor_position);
cx.spawn(move |this, mut cx| async move { cx.spawn(async move |this, cx| {
completion_menu completion_menu
.filter(query.as_deref(), cx.background_executor().clone()) .filter(query.as_deref(), cx.background_executor().clone())
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let mut context_menu = this.context_menu.borrow_mut(); let mut context_menu = this.context_menu.borrow_mut();
let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref() let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
else { else {
@ -4050,16 +4050,16 @@ impl Editor {
cx, cx,
) )
}); });
Some(cx.spawn_in(window, |editor, mut cx| async move { Some(cx.spawn_in(window, async move |editor, cx| {
if let Some(transaction) = on_type_formatting.await? { if let Some(transaction) = on_type_formatting.await? {
if push_to_client_history { if push_to_client_history {
buffer buffer
.update(&mut cx, |buffer, _| { .update(cx, |buffer, _| {
buffer.push_transaction(transaction, Instant::now()); buffer.push_transaction(transaction, Instant::now());
}) })
.ok(); .ok();
} }
editor.update(&mut cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.refresh_document_highlights(cx); editor.refresh_document_highlights(cx);
})?; })?;
} }
@ -4215,9 +4215,9 @@ impl Editor {
.map_or(true, |provider| provider.sort_completions()); .map_or(true, |provider| provider.sort_completions());
let id = post_inc(&mut self.next_completion_id); let id = post_inc(&mut self.next_completion_id);
let task = cx.spawn_in(window, |editor, mut cx| { let task = cx.spawn_in(window, async move |editor, cx| {
async move { async move {
editor.update(&mut cx, |this, _| { editor.update(cx, |this, _| {
this.completion_tasks.retain(|(task_id, _)| *task_id >= id); this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
})?; })?;
@ -4267,7 +4267,7 @@ impl Editor {
menu.visible().then_some(menu) menu.visible().then_some(menu)
}; };
editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(cx, |editor, window, cx| {
match editor.context_menu.borrow().as_ref() { match editor.context_menu.borrow().as_ref() {
None => {} None => {}
Some(CodeContextMenu::Completions(prev_menu)) => { Some(CodeContextMenu::Completions(prev_menu)) => {
@ -4308,6 +4308,7 @@ impl Editor {
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}); });
self.completion_tasks.push((id, task)); self.completion_tasks.push((id, task));
@ -4550,13 +4551,13 @@ impl Editor {
let deployed_from_indicator = action.deployed_from_indicator; let deployed_from_indicator = action.deployed_from_indicator;
let mut task = self.code_actions_task.take(); let mut task = self.code_actions_task.take();
let action = action.clone(); let action = action.clone();
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
while let Some(prev_task) = task { while let Some(prev_task) = task {
prev_task.await.log_err(); prev_task.await.log_err();
task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?; task = editor.update(cx, |this, _| this.code_actions_task.take())?;
} }
let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| { let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
if editor.focus_handle.is_focused(window) { if editor.focus_handle.is_focused(window) {
let multibuffer_point = action let multibuffer_point = action
.deployed_from_indicator .deployed_from_indicator
@ -4605,7 +4606,7 @@ impl Editor {
Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx) Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
}); });
Some(cx.spawn_in(window, |editor, mut cx| async move { Some(cx.spawn_in(window, async move |editor, cx| {
let task_context = match task_context { let task_context = match task_context {
Some(task_context) => task_context.await, Some(task_context) => task_context.await,
None => None, None => None,
@ -4626,7 +4627,7 @@ impl Editor {
&& code_actions && code_actions
.as_ref() .as_ref()
.map_or(true, |actions| actions.is_empty()); .map_or(true, |actions| actions.is_empty());
if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| { if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
*editor.context_menu.borrow_mut() = *editor.context_menu.borrow_mut() =
Some(CodeContextMenu::CodeActions(CodeActionsMenu { Some(CodeContextMenu::CodeActions(CodeActionsMenu {
buffer, buffer,
@ -4709,7 +4710,7 @@ impl Editor {
let apply_code_action = let apply_code_action =
provider.apply_code_action(buffer, action, excerpt_id, true, window, cx); provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
let workspace = workspace.downgrade(); let workspace = workspace.downgrade();
Some(cx.spawn_in(window, |editor, cx| async move { Some(cx.spawn_in(window, async move |editor, cx| {
let project_transaction = apply_code_action.await?; let project_transaction = apply_code_action.await?;
Self::open_project_transaction( Self::open_project_transaction(
&editor, &editor,
@ -4729,7 +4730,7 @@ impl Editor {
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
transaction: ProjectTransaction, transaction: ProjectTransaction,
title: String, title: String,
mut cx: AsyncWindowContext, cx: &mut AsyncWindowContext,
) -> Result<()> { ) -> Result<()> {
let mut entries = transaction.0.into_iter().collect::<Vec<_>>(); let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
cx.update(|_, cx| { cx.update(|_, cx| {
@ -4743,7 +4744,7 @@ impl Editor {
if let Some((buffer, transaction)) = entries.first() { if let Some((buffer, transaction)) = entries.first() {
if entries.len() == 1 { if entries.len() == 1 {
let excerpt = this.update(&mut cx, |editor, cx| { let excerpt = this.update(cx, |editor, cx| {
editor editor
.buffer() .buffer()
.read(cx) .read(cx)
@ -4751,7 +4752,7 @@ impl Editor {
})?; })?;
if let Some((_, excerpted_buffer, excerpt_range)) = excerpt { if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
if excerpted_buffer == *buffer { if excerpted_buffer == *buffer {
let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| { let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
let excerpt_range = excerpt_range.to_offset(buffer); let excerpt_range = excerpt_range.to_offset(buffer);
buffer buffer
.edited_ranges_for_transaction::<usize>(transaction) .edited_ranges_for_transaction::<usize>(transaction)
@ -4791,7 +4792,7 @@ impl Editor {
multibuffer multibuffer
})?; })?;
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
let project = workspace.project().clone(); let project = workspace.project().clone();
let editor = let editor =
cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx)); cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
@ -4854,12 +4855,12 @@ impl Editor {
return None; return None;
} }
self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move { self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
cx.background_executor() cx.background_executor()
.timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT) .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
.await; .await;
let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| { let (providers, tasks) = this.update_in(cx, |this, window, cx| {
let providers = this.code_action_providers.clone(); let providers = this.code_action_providers.clone();
let tasks = this let tasks = this
.code_action_providers .code_action_providers
@ -4884,7 +4885,7 @@ impl Editor {
} }
} }
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.available_code_actions = if actions.is_empty() { this.available_code_actions = if actions.is_empty() {
None None
} else { } else {
@ -4907,10 +4908,10 @@ impl Editor {
self.show_git_blame_inline = false; self.show_git_blame_inline = false;
self.show_git_blame_inline_delay_task = self.show_git_blame_inline_delay_task =
Some(cx.spawn_in(window, |this, mut cx| async move { Some(cx.spawn_in(window, async move |this, cx| {
cx.background_executor().timer(delay).await; cx.background_executor().timer(delay).await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.show_git_blame_inline = true; this.show_git_blame_inline = true;
cx.notify(); cx.notify();
}) })
@ -4935,7 +4936,7 @@ impl Editor {
return None; return None;
} }
let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce; let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move { self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
cx.background_executor() cx.background_executor()
.timer(Duration::from_millis(debounce)) .timer(Duration::from_millis(debounce))
.await; .await;
@ -4953,7 +4954,7 @@ impl Editor {
}; };
if let Some(highlights) = highlights { if let Some(highlights) = highlights {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
if this.pending_rename.is_some() { if this.pending_rename.is_some() {
return; return;
} }
@ -5046,12 +5047,12 @@ impl Editor {
return; return;
} }
let debounce = EditorSettings::get_global(cx).selection_highlight_debounce; let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move { self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
cx.background_executor() cx.background_executor()
.timer(Duration::from_millis(debounce)) .timer(Duration::from_millis(debounce))
.await; .await;
let Some(Some(matches_task)) = editor let Some(Some(matches_task)) = editor
.update_in(&mut cx, |editor, _, cx| { .update_in(cx, |editor, _, cx| {
if editor.selections.count() != 1 || editor.selections.line_mode { if editor.selections.count() != 1 || editor.selections.line_mode {
editor.clear_background_highlights::<SelectedTextHighlight>(cx); editor.clear_background_highlights::<SelectedTextHighlight>(cx);
return None; return None;
@ -5116,7 +5117,7 @@ impl Editor {
}; };
let matches = matches_task.await; let matches = matches_task.await;
editor editor
.update_in(&mut cx, |editor, _, cx| { .update_in(cx, |editor, _, cx| {
editor.clear_background_highlights::<SelectedTextHighlight>(cx); editor.clear_background_highlights::<SelectedTextHighlight>(cx);
if !matches.is_empty() { if !matches.is_empty() {
editor.highlight_background::<SelectedTextHighlight>( editor.highlight_background::<SelectedTextHighlight>(
@ -5342,9 +5343,9 @@ impl Editor {
fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.show_cursor_names = true; self.show_cursor_names = true;
cx.notify(); cx.notify();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
cx.background_executor().timer(CURSORS_VISIBLE_FOR).await; cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.show_cursor_names = false; this.show_cursor_names = false;
cx.notify() cx.notify()
}) })
@ -6230,7 +6231,7 @@ impl Editor {
let reveal_strategy = action.reveal; let reveal_strategy = action.reveal;
let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx); let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
let context = task_context.await?; let context = task_context.await?;
let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?; let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
@ -6238,7 +6239,7 @@ impl Editor {
resolved.reveal = reveal_strategy; resolved.reveal = reveal_strategy;
workspace workspace
.update(&mut cx, |workspace, cx| { .update(cx, |workspace, cx| {
workspace::tasks::schedule_resolved_task( workspace::tasks::schedule_resolved_task(
workspace, workspace,
task_source_kind, task_source_kind,
@ -11806,19 +11807,19 @@ impl Editor {
return Task::ready(()); return Task::ready(());
} }
let project = self.project.as_ref().map(Entity::downgrade); let project = self.project.as_ref().map(Entity::downgrade);
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
cx.background_executor().timer(UPDATE_DEBOUNCE).await; cx.background_executor().timer(UPDATE_DEBOUNCE).await;
let Some(project) = project.and_then(|p| p.upgrade()) else { let Some(project) = project.and_then(|p| p.upgrade()) else {
return; return;
}; };
let Ok(display_snapshot) = this.update(&mut cx, |this, cx| { let Ok(display_snapshot) = this.update(cx, |this, cx| {
this.display_map.update(cx, |map, cx| map.snapshot(cx)) this.display_map.update(cx, |map, cx| map.snapshot(cx))
}) else { }) else {
return; return;
}; };
let hide_runnables = project let hide_runnables = project
.update(&mut cx, |project, cx| { .update(cx, |project, cx| {
// Do not display any test indicators in non-dev server remote projects. // Do not display any test indicators in non-dev server remote projects.
project.is_via_collab() && project.ssh_connection_string(cx).is_none() project.is_via_collab() && project.ssh_connection_string(cx).is_none()
}) })
@ -11836,7 +11837,7 @@ impl Editor {
.await; .await;
let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone()); let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.clear_tasks(); this.clear_tasks();
for (key, value) in rows { for (key, value) in rows {
this.insert_tasks(key, value); this.insert_tasks(key, value);
@ -12425,11 +12426,11 @@ impl Editor {
) -> Task<Result<Navigated>> { ) -> Task<Result<Navigated>> {
let definition = let definition =
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx); self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
if definition.await? == Navigated::Yes { if definition.await? == Navigated::Yes {
return Ok(Navigated::Yes); return Ok(Navigated::Yes);
} }
match editor.update_in(&mut cx, |editor, window, cx| { match editor.update_in(cx, |editor, window, cx| {
editor.find_all_references(&FindAllReferences, window, cx) editor.find_all_references(&FindAllReferences, window, cx)
})? { })? {
Some(references) => references.await, Some(references) => references.await,
@ -12523,10 +12524,10 @@ impl Editor {
return Task::ready(Ok(Navigated::No)); return Task::ready(Ok(Navigated::No));
}; };
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
let definitions = definitions.await?; let definitions = definitions.await?;
let navigated = editor let navigated = editor
.update_in(&mut cx, |editor, window, cx| { .update_in(cx, |editor, window, cx| {
editor.navigate_to_hover_links( editor.navigate_to_hover_links(
Some(kind), Some(kind),
definitions definitions
@ -12566,7 +12567,7 @@ impl Editor {
None None
}; };
let url_finder = cx.spawn_in(window, |editor, mut cx| async move { let url_finder = cx.spawn_in(window, async move |editor, cx| {
let url = if let Some(end_pos) = end_position { let url = if let Some(end_pos) = end_position {
find_url_from_range(&buffer, start_position..end_pos, cx.clone()) find_url_from_range(&buffer, start_position..end_pos, cx.clone())
} else { } else {
@ -12574,7 +12575,7 @@ impl Editor {
}; };
if let Some(url) = url { if let Some(url) = url {
editor.update(&mut cx, |_, cx| { editor.update(cx, |_, cx| {
cx.open_url(&url); cx.open_url(&url);
}) })
} else { } else {
@ -12605,12 +12606,12 @@ impl Editor {
let project = self.project.clone(); let project = self.project.clone();
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
let result = find_file(&buffer, project, buffer_position, &mut cx).await; let result = find_file(&buffer, project, buffer_position, cx).await;
if let Some((_, path)) = result { if let Some((_, path)) = result {
workspace workspace
.update_in(&mut cx, |workspace, window, cx| { .update_in(cx, |workspace, window, cx| {
workspace.open_resolved_path(path, window, cx) workspace.open_resolved_path(path, window, cx)
})? })?
.await?; .await?;
@ -12655,9 +12656,9 @@ impl Editor {
} }
HoverLink::File(path) => { HoverLink::File(path) => {
if let Some(workspace) = self.workspace() { if let Some(workspace) = self.workspace() {
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
workspace workspace
.update_in(&mut cx, |workspace, window, cx| { .update_in(cx, |workspace, window, cx| {
workspace.open_resolved_path(path, window, cx) workspace.open_resolved_path(path, window, cx)
})? })?
.await .await
@ -12668,14 +12669,14 @@ impl Editor {
} }
} }
}; };
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
let target = match target_task.await.context("target resolution task")? { let target = match target_task.await.context("target resolution task")? {
TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes), TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
TargetTaskResult::Location(None) => return Ok(Navigated::No), TargetTaskResult::Location(None) => return Ok(Navigated::No),
TargetTaskResult::Location(Some(target)) => target, TargetTaskResult::Location(Some(target)) => target,
}; };
editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(cx, |editor, window, cx| {
let Some(workspace) = editor.workspace() else { let Some(workspace) = editor.workspace() else {
return Navigated::No; return Navigated::No;
}; };
@ -12721,9 +12722,9 @@ impl Editor {
}) })
}) })
} else if !definitions.is_empty() { } else if !definitions.is_empty() {
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
let (title, location_tasks, workspace) = editor let (title, location_tasks, workspace) = editor
.update_in(&mut cx, |editor, window, cx| { .update_in(cx, |editor, window, cx| {
let tab_kind = match kind { let tab_kind = match kind {
Some(GotoDefinitionKind::Implementation) => "Implementations", Some(GotoDefinitionKind::Implementation) => "Implementations",
_ => "Definitions", _ => "Definitions",
@ -12771,7 +12772,7 @@ impl Editor {
return Ok(Navigated::No); return Ok(Navigated::No);
}; };
let opened = workspace let opened = workspace
.update_in(&mut cx, |workspace, window, cx| { .update_in(cx, |workspace, window, cx| {
Self::open_locations_in_multibuffer( Self::open_locations_in_multibuffer(
workspace, workspace,
locations, locations,
@ -12802,8 +12803,8 @@ impl Editor {
return Task::ready(Ok(None)); return Task::ready(Ok(None));
}; };
cx.spawn_in(window, move |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
let location_task = editor.update(&mut cx, |_, cx| { let location_task = editor.update(cx, |_, cx| {
project.update(cx, |project, cx| { project.update(cx, |project, cx| {
let language_server_name = project let language_server_name = project
.language_server_statuses(cx) .language_server_statuses(cx)
@ -12822,7 +12823,7 @@ impl Editor {
let location = match location_task { let location = match location_task {
Some(task) => Some({ Some(task) => Some({
let target_buffer_handle = task.await.context("open local buffer")?; let target_buffer_handle = task.await.context("open local buffer")?;
let range = target_buffer_handle.update(&mut cx, |target_buffer, _| { let range = target_buffer_handle.update(cx, |target_buffer, _| {
let target_start = target_buffer let target_start = target_buffer
.clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left); .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
let target_end = target_buffer let target_end = target_buffer
@ -12880,21 +12881,13 @@ impl Editor {
let workspace = self.workspace()?; let workspace = self.workspace()?;
let project = workspace.read(cx).project().clone(); let project = workspace.read(cx).project().clone();
let references = project.update(cx, |project, cx| project.references(&buffer, head, cx)); let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
Some(cx.spawn_in(window, |editor, mut cx| async move { Some(cx.spawn_in(window, async move |editor, cx| {
let _cleanup = defer({ let _cleanup = cx.on_drop(&editor, move |editor, _| {
let mut cx = cx.clone(); if let Ok(i) = editor
move || { .find_all_references_task_sources
let _ = editor.update(&mut cx, |editor, _| { .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
if let Ok(i) = {
editor editor.find_all_references_task_sources.remove(i);
.find_all_references_task_sources
.binary_search_by(|anchor| {
anchor.cmp(&head_anchor, &multi_buffer_snapshot)
})
{
editor.find_all_references_task_sources.remove(i);
}
});
} }
}); });
@ -12903,7 +12896,7 @@ impl Editor {
return anyhow::Ok(Navigated::No); return anyhow::Ok(Navigated::No);
} }
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
let title = locations let title = locations
.first() .first()
.as_ref() .as_ref()
@ -13067,11 +13060,11 @@ impl Editor {
.unwrap_or_else(|| Task::ready(Ok(None))); .unwrap_or_else(|| Task::ready(Ok(None)));
drop(snapshot); drop(snapshot);
Some(cx.spawn_in(window, |this, mut cx| async move { Some(cx.spawn_in(window, async move |this, cx| {
let rename_range = if let Some(range) = prepare_rename.await? { let rename_range = if let Some(range) = prepare_rename.await? {
Some(range) Some(range)
} else { } else {
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let buffer = this.buffer.read(cx).snapshot(cx); let buffer = this.buffer.read(cx).snapshot(cx);
let mut buffer_highlights = this let mut buffer_highlights = this
.document_highlights_for_position(selection.head(), &buffer) .document_highlights_for_position(selection.head(), &buffer)
@ -13085,7 +13078,7 @@ impl Editor {
})? })?
}; };
if let Some(rename_range) = rename_range { if let Some(rename_range) = rename_range {
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
let snapshot = cursor_buffer.read(cx).snapshot(); let snapshot = cursor_buffer.read(cx).snapshot();
let rename_buffer_range = rename_range.to_offset(&snapshot); let rename_buffer_range = rename_range.to_offset(&snapshot);
let cursor_offset_in_rename_range = let cursor_offset_in_rename_range =
@ -13258,18 +13251,18 @@ impl Editor {
cx, cx,
)?; )?;
Some(cx.spawn_in(window, |editor, mut cx| async move { Some(cx.spawn_in(window, async move |editor, cx| {
let project_transaction = rename.await?; let project_transaction = rename.await?;
Self::open_project_transaction( Self::open_project_transaction(
&editor, &editor,
workspace, workspace,
project_transaction, project_transaction,
format!("Rename: {}{}", old_name, new_name), format!("Rename: {}{}", old_name, new_name),
cx.clone(), cx,
) )
.await?; .await?;
editor.update(&mut cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.refresh_document_highlights(cx); editor.refresh_document_highlights(cx);
})?; })?;
Ok(()) Ok(())
@ -13416,7 +13409,7 @@ impl Editor {
project.format(buffers, target, true, trigger, cx) project.format(buffers, target, true, trigger, cx)
}); });
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
let transaction = futures::select_biased! { let transaction = futures::select_biased! {
transaction = format.log_err().fuse() => transaction, transaction = format.log_err().fuse() => transaction,
() = timeout => { () = timeout => {
@ -13426,7 +13419,7 @@ impl Editor {
}; };
buffer buffer
.update(&mut cx, |buffer, cx| { .update(cx, |buffer, cx| {
if let Some(transaction) = transaction { if let Some(transaction) = transaction {
if !buffer.is_singleton() { if !buffer.is_singleton() {
buffer.push_transaction(&transaction.0, cx); buffer.push_transaction(&transaction.0, cx);
@ -13471,7 +13464,7 @@ impl Editor {
let apply_action = project.update(cx, |project, cx| { let apply_action = project.update(cx, |project, cx| {
project.apply_code_action_kind(buffers, kind, true, cx) project.apply_code_action_kind(buffers, kind, true, cx)
}); });
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
let transaction = futures::select_biased! { let transaction = futures::select_biased! {
() = timeout => { () = timeout => {
log::warn!("timed out waiting for executing code action"); log::warn!("timed out waiting for executing code action");
@ -13480,7 +13473,7 @@ impl Editor {
transaction = apply_action.log_err().fuse() => transaction, transaction = apply_action.log_err().fuse() => transaction,
}; };
buffer buffer
.update(&mut cx, |buffer, cx| { .update(cx, |buffer, cx| {
// check if we need this // check if we need this
if let Some(transaction) = transaction { if let Some(transaction) = transaction {
if !buffer.is_singleton() { if !buffer.is_singleton() {
@ -13695,12 +13688,12 @@ impl Editor {
} else { } else {
None None
}; };
self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move { self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
if let Some(debounce) = debounce { if let Some(debounce) = debounce {
cx.background_executor().timer(debounce).await; cx.background_executor().timer(debounce).await;
} }
let Some(snapshot) = editor let Some(snapshot) = editor
.update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx)) .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
.ok() .ok()
else { else {
return; return;
@ -13741,7 +13734,7 @@ impl Editor {
.await; .await;
editor editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
editor.inline_diagnostics = new_inline_diagnostics; editor.inline_diagnostics = new_inline_diagnostics;
cx.notify(); cx.notify();
}) })
@ -14042,9 +14035,9 @@ impl Editor {
self.fold_creases(fold_ranges, true, window, cx); self.fold_creases(fold_ranges, true, window, cx);
} else { } else {
self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move { self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
editor editor
.update_in(&mut cx, |editor, _, cx| { .update_in(cx, |editor, _, cx| {
for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() { for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
editor.fold_buffer(buffer_id, cx); editor.fold_buffer(buffer_id, cx);
} }
@ -14218,9 +14211,9 @@ impl Editor {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx); self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
} else { } else {
self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move { self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
editor editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() { for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
editor.unfold_buffer(buffer_id, cx); editor.unfold_buffer(buffer_id, cx);
} }
@ -14507,9 +14500,9 @@ impl Editor {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let task = self.save_buffers_for_ranges_if_needed(&ranges, cx); let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
cx.spawn(|this, mut cx| async move { cx.spawn(async move |this, cx| {
task.await?; task.await?;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
let snapshot = this.buffer.read(cx).snapshot(cx); let snapshot = this.buffer.read(cx).snapshot(cx);
let chunk_by = this let chunk_by = this
.diff_hunks_in_ranges(&ranges, &snapshot) .diff_hunks_in_ranges(&ranges, &snapshot)
@ -15463,34 +15456,32 @@ impl Editor {
let permalink_task = self.get_permalink_to_line(cx); let permalink_task = self.get_permalink_to_line(cx);
let workspace = self.workspace(); let workspace = self.workspace();
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| match permalink_task.await {
match permalink_task.await { Ok(permalink) => {
Ok(permalink) => { cx.update(|_, cx| {
cx.update(|_, cx| { cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string())); })
}) .ok();
.ok(); }
} Err(err) => {
Err(err) => { let message = format!("Failed to copy permalink: {err}");
let message = format!("Failed to copy permalink: {err}");
Err::<(), anyhow::Error>(err).log_err(); Err::<(), anyhow::Error>(err).log_err();
if let Some(workspace) = workspace { if let Some(workspace) = workspace {
workspace workspace
.update_in(&mut cx, |workspace, _, cx| { .update_in(cx, |workspace, _, cx| {
struct CopyPermalinkToLine; struct CopyPermalinkToLine;
workspace.show_toast( workspace.show_toast(
Toast::new( Toast::new(
NotificationId::unique::<CopyPermalinkToLine>(), NotificationId::unique::<CopyPermalinkToLine>(),
message, message,
), ),
cx, cx,
) )
}) })
.ok(); .ok();
}
} }
} }
}) })
@ -15520,34 +15511,32 @@ impl Editor {
let permalink_task = self.get_permalink_to_line(cx); let permalink_task = self.get_permalink_to_line(cx);
let workspace = self.workspace(); let workspace = self.workspace();
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| match permalink_task.await {
match permalink_task.await { Ok(permalink) => {
Ok(permalink) => { cx.update(|_, cx| {
cx.update(|_, cx| { cx.open_url(permalink.as_ref());
cx.open_url(permalink.as_ref()); })
}) .ok();
.ok(); }
} Err(err) => {
Err(err) => { let message = format!("Failed to open permalink: {err}");
let message = format!("Failed to open permalink: {err}");
Err::<(), anyhow::Error>(err).log_err(); Err::<(), anyhow::Error>(err).log_err();
if let Some(workspace) = workspace { if let Some(workspace) = workspace {
workspace workspace
.update(&mut cx, |workspace, cx| { .update(cx, |workspace, cx| {
struct OpenPermalinkToLine; struct OpenPermalinkToLine;
workspace.show_toast( workspace.show_toast(
Toast::new( Toast::new(
NotificationId::unique::<OpenPermalinkToLine>(), NotificationId::unique::<OpenPermalinkToLine>(),
message, message,
), ),
cx, cx,
) )
}) })
.ok(); .ok();
}
} }
} }
}) })
@ -15619,8 +15608,8 @@ impl Editor {
let title = multibuffer.title(cx).to_string(); let title = multibuffer.title(cx).to_string();
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
workspace.update_in(&mut cx, |workspace, window, cx| { workspace.update_in(cx, |workspace, window, cx| {
Self::open_locations_in_multibuffer( Self::open_locations_in_multibuffer(
workspace, workspace,
locations, locations,
@ -17008,12 +16997,12 @@ impl Editor {
} }
tasks tasks
}); });
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
for (buffer, task) in save_tasks { for (buffer, task) in save_tasks {
let result = task.await; let result = task.await;
if result.is_err() { if result.is_err() {
let Some(path) = buffer let Some(path) = buffer
.read_with(&cx, |buffer, cx| buffer.project_path(cx)) .read_with(cx, |buffer, cx| buffer.project_path(cx))
.ok() .ok()
else { else {
continue; continue;
@ -17212,10 +17201,10 @@ fn get_uncommitted_diff_for_buffer(
tasks.push(project.open_uncommitted_diff(buffer.clone(), cx)) tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
} }
}); });
cx.spawn(|mut cx| async move { cx.spawn(async move |cx| {
let diffs = future::join_all(tasks).await; let diffs = future::join_all(tasks).await;
buffer buffer
.update(&mut cx, |buffer, cx| { .update(cx, |buffer, cx| {
for diff in diffs.into_iter().flatten() { for diff in diffs.into_iter().flatten() {
buffer.add_diff(diff, cx); buffer.add_diff(diff, cx);
} }
@ -18069,13 +18058,13 @@ impl SemanticsProvider for Entity<Project> {
Some(self.update(cx, |project, cx| { Some(self.update(cx, |project, cx| {
let buffer = buffer.clone(); let buffer = buffer.clone();
let task = project.prepare_rename(buffer.clone(), position, cx); let task = project.prepare_rename(buffer.clone(), position, cx);
cx.spawn(|_, mut cx| async move { cx.spawn(async move |_, cx| {
Ok(match task.await? { Ok(match task.await? {
PrepareRenameResponse::Success(range) => Some(range), PrepareRenameResponse::Success(range) => Some(range),
PrepareRenameResponse::InvalidPosition => None, PrepareRenameResponse::InvalidPosition => None,
PrepareRenameResponse::OnlyUnpreparedRenameSupported => { PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
// Fallback on using TreeSitter info to determine identifier range // Fallback on using TreeSitter info to determine identifier range
buffer.update(&mut cx, |buffer, _| { buffer.update(cx, |buffer, _| {
let snapshot = buffer.snapshot(); let snapshot = buffer.snapshot();
let (range, kind) = snapshot.surrounding_word(position); let (range, kind) = snapshot.surrounding_word(position);
if kind != Some(CharKind::Word) { if kind != Some(CharKind::Word) {

View file

@ -9312,6 +9312,7 @@ async fn test_word_completion(cx: &mut TestAppContext) {
.server .server
.on_request::<lsp::request::Completion, _, _>(move |_, cx| { .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
let lsp_throttle_completions = lsp_throttle_completions.clone(); let lsp_throttle_completions = lsp_throttle_completions.clone();
let cx = cx.clone();
async move { async move {
if lsp_throttle_completions.load(atomic::Ordering::Acquire) { if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
cx.background_executor() cx.background_executor()

View file

@ -973,10 +973,10 @@ impl EditorElement {
}; };
editor.hovered_cursors.insert( editor.hovered_cursors.insert(
key.clone(), key.clone(),
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
cx.background_executor().timer(CURSORS_VISIBLE_FOR).await; cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
editor editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
editor.hovered_cursors.remove(&key); editor.hovered_cursors.remove(&key);
cx.notify(); cx.notify();
}) })
@ -5199,7 +5199,7 @@ impl EditorElement {
editor.scrollbar_marker_state.dirty = false; editor.scrollbar_marker_state.dirty = false;
editor.scrollbar_marker_state.pending_refresh = editor.scrollbar_marker_state.pending_refresh =
Some(cx.spawn_in(window, |editor, mut cx| async move { Some(cx.spawn_in(window, async move |editor, cx| {
let scrollbar_size = scrollbar_layout.hitbox.size; let scrollbar_size = scrollbar_layout.hitbox.size;
let scrollbar_markers = cx let scrollbar_markers = cx
.background_spawn(async move { .background_spawn(async move {
@ -5346,7 +5346,7 @@ impl EditorElement {
}) })
.await; .await;
editor.update(&mut cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.scrollbar_marker_state.markers = scrollbar_markers; editor.scrollbar_marker_state.markers = scrollbar_markers;
editor.scrollbar_marker_state.scrollbar_size = scrollbar_size; editor.scrollbar_marker_state.scrollbar_size = scrollbar_size;
editor.scrollbar_marker_state.pending_refresh = None; editor.scrollbar_marker_state.pending_refresh = None;

View file

@ -363,7 +363,7 @@ impl GitBlame {
let blame = self.project.read(cx).blame_buffer(&self.buffer, None, cx); let blame = self.project.read(cx).blame_buffer(&self.buffer, None, cx);
let provider_registry = GitHostingProviderRegistry::default_global(cx); let provider_registry = GitHostingProviderRegistry::default_global(cx);
self.task = cx.spawn(|this, mut cx| async move { self.task = cx.spawn(async move |this, cx| {
let result = cx let result = cx
.background_spawn({ .background_spawn({
let snapshot = snapshot.clone(); let snapshot = snapshot.clone();
@ -386,7 +386,7 @@ impl GitBlame {
}) })
.await; .await;
this.update(&mut cx, |this, cx| match result { this.update(cx, |this, cx| match result {
Ok(None) => { Ok(None) => {
// Nothing to do, e.g. no repository found // Nothing to do, e.g. no repository found
} }
@ -417,12 +417,12 @@ impl GitBlame {
} }
fn regenerate_on_edit(&mut self, cx: &mut Context<Self>) { fn regenerate_on_edit(&mut self, cx: &mut Context<Self>) {
self.regenerate_on_edit_task = cx.spawn(|this, mut cx| async move { self.regenerate_on_edit_task = cx.spawn(async move |this, cx| {
cx.background_executor() cx.background_executor()
.timer(REGENERATE_ON_EDIT_DEBOUNCE_INTERVAL) .timer(REGENERATE_ON_EDIT_DEBOUNCE_INTERVAL)
.await; .await;
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.generate(cx); this.generate(cx);
}) })
}) })

View file

@ -167,10 +167,10 @@ impl Editor {
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) { ) {
let reveal_task = self.cmd_click_reveal_task(point, modifiers, window, cx); let reveal_task = self.cmd_click_reveal_task(point, modifiers, window, cx);
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
let definition_revealed = reveal_task.await.log_err().unwrap_or(Navigated::No); let definition_revealed = reveal_task.await.log_err().unwrap_or(Navigated::No);
let find_references = editor let find_references = editor
.update_in(&mut cx, |editor, window, cx| { .update_in(cx, |editor, window, cx| {
if definition_revealed == Navigated::Yes { if definition_revealed == Navigated::Yes {
return None; return None;
} }
@ -529,12 +529,12 @@ pub fn show_link_definition(
let provider = editor.semantics_provider.clone(); let provider = editor.semantics_provider.clone();
let snapshot = snapshot.buffer_snapshot.clone(); let snapshot = snapshot.buffer_snapshot.clone();
hovered_link_state.task = Some(cx.spawn_in(window, |this, mut cx| { hovered_link_state.task = Some(cx.spawn_in(window, async move |this, cx| {
async move { async move {
let result = match &trigger_point { let result = match &trigger_point {
TriggerPoint::Text(_) => { TriggerPoint::Text(_) => {
if let Some((url_range, url)) = find_url(&buffer, buffer_position, cx.clone()) { if let Some((url_range, url)) = find_url(&buffer, buffer_position, cx.clone()) {
this.update(&mut cx, |_, _| { this.update(cx, |_, _| {
let range = maybe!({ let range = maybe!({
let start = let start =
snapshot.anchor_in_excerpt(excerpt_id, url_range.start)?; snapshot.anchor_in_excerpt(excerpt_id, url_range.start)?;
@ -545,7 +545,7 @@ pub fn show_link_definition(
}) })
.ok() .ok()
} else if let Some((filename_range, filename)) = } else if let Some((filename_range, filename)) =
find_file(&buffer, project.clone(), buffer_position, &mut cx).await find_file(&buffer, project.clone(), buffer_position, cx).await
{ {
let range = maybe!({ let range = maybe!({
let start = let start =
@ -589,7 +589,7 @@ pub fn show_link_definition(
)), )),
}; };
this.update(&mut cx, |editor, cx| { this.update(cx, |editor, cx| {
// Clear any existing highlights // Clear any existing highlights
editor.clear_highlights::<HoveredLinkState>(cx); editor.clear_highlights::<HoveredLinkState>(cx);
let Some(hovered_link_state) = editor.hovered_link_state.as_mut() else { let Some(hovered_link_state) = editor.hovered_link_state.as_mut() else {
@ -647,6 +647,7 @@ pub fn show_link_definition(
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())
} }
.log_err() .log_err()
.await
})); }));
editor.hovered_link_state = Some(hovered_link_state); editor.hovered_link_state = Some(hovered_link_state);

View file

@ -149,18 +149,18 @@ pub fn hover_at_inlay(
let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay; let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
let task = cx.spawn_in(window, |this, mut cx| { let task = cx.spawn_in(window, async move |this, cx| {
async move { async move {
cx.background_executor() cx.background_executor()
.timer(Duration::from_millis(hover_popover_delay)) .timer(Duration::from_millis(hover_popover_delay))
.await; .await;
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.hover_state.diagnostic_popover = None; this.hover_state.diagnostic_popover = None;
})?; })?;
let language_registry = project.update(&mut cx, |p, _| p.languages().clone())?; let language_registry = project.update(cx, |p, _| p.languages().clone())?;
let blocks = vec![inlay_hover.tooltip]; let blocks = vec![inlay_hover.tooltip];
let parsed_content = parse_blocks(&blocks, &language_registry, None, &mut cx).await; let parsed_content = parse_blocks(&blocks, &language_registry, None, cx).await;
let scroll_handle = ScrollHandle::new(); let scroll_handle = ScrollHandle::new();
let hover_popover = InfoPopover { let hover_popover = InfoPopover {
@ -172,7 +172,7 @@ pub fn hover_at_inlay(
anchor: None, anchor: None,
}; };
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
// TODO: no background highlights happen for inlays currently // TODO: no background highlights happen for inlays currently
this.hover_state.info_popovers = vec![hover_popover]; this.hover_state.info_popovers = vec![hover_popover];
cx.notify(); cx.notify();
@ -181,6 +181,7 @@ pub fn hover_at_inlay(
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}); });
editor.hover_state.info_task = Some(task); editor.hover_state.info_task = Some(task);
@ -257,7 +258,7 @@ fn show_hover(
let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay; let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
let task = cx.spawn_in(window, |this, mut cx| { let task = cx.spawn_in(window, async move |this, cx| {
async move { async move {
// If we need to delay, delay a set amount initially before making the lsp request // If we need to delay, delay a set amount initially before making the lsp request
let delay = if ignore_timeout { let delay = if ignore_timeout {
@ -375,7 +376,7 @@ fn show_hover(
None None
}; };
this.update(&mut cx, |this, _| { this.update(cx, |this, _| {
this.hover_state.diagnostic_popover = diagnostic_popover; this.hover_state.diagnostic_popover = diagnostic_popover;
})?; })?;
@ -409,7 +410,7 @@ fn show_hover(
} else { } else {
Vec::new() Vec::new()
}; };
let snapshot = this.update_in(&mut cx, |this, window, cx| this.snapshot(window, cx))?; let snapshot = this.update_in(cx, |this, window, cx| this.snapshot(window, cx))?;
let mut hover_highlights = Vec::with_capacity(hovers_response.len()); let mut hover_highlights = Vec::with_capacity(hovers_response.len());
let mut info_popovers = Vec::with_capacity( let mut info_popovers = Vec::with_capacity(
hovers_response.len() + if invisible_char.is_some() { 1 } else { 0 }, hovers_response.len() + if invisible_char.is_some() { 1 } else { 0 },
@ -420,7 +421,7 @@ fn show_hover(
text: format!("Unicode character U+{:02X}", invisible as u32), text: format!("Unicode character U+{:02X}", invisible as u32),
kind: HoverBlockKind::PlainText, kind: HoverBlockKind::PlainText,
}]; }];
let parsed_content = parse_blocks(&blocks, &language_registry, None, &mut cx).await; let parsed_content = parse_blocks(&blocks, &language_registry, None, cx).await;
let scroll_handle = ScrollHandle::new(); let scroll_handle = ScrollHandle::new();
info_popovers.push(InfoPopover { info_popovers.push(InfoPopover {
symbol_range: RangeInEditor::Text(range), symbol_range: RangeInEditor::Text(range),
@ -459,8 +460,7 @@ fn show_hover(
let blocks = hover_result.contents; let blocks = hover_result.contents;
let language = hover_result.language; let language = hover_result.language;
let parsed_content = let parsed_content = parse_blocks(&blocks, &language_registry, language, cx).await;
parse_blocks(&blocks, &language_registry, language, &mut cx).await;
let scroll_handle = ScrollHandle::new(); let scroll_handle = ScrollHandle::new();
hover_highlights.push(range.clone()); hover_highlights.push(range.clone());
info_popovers.push(InfoPopover { info_popovers.push(InfoPopover {
@ -473,7 +473,7 @@ fn show_hover(
}); });
} }
this.update_in(&mut cx, |editor, window, cx| { this.update_in(cx, |editor, window, cx| {
if hover_highlights.is_empty() { if hover_highlights.is_empty() {
editor.clear_background_highlights::<HoverState>(cx); editor.clear_background_highlights::<HoverState>(cx);
} else { } else {
@ -493,6 +493,7 @@ fn show_hover(
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()
.await
}); });
editor.hover_state.info_task = Some(task); editor.hover_state.info_task = Some(task);
@ -642,7 +643,7 @@ pub fn open_markdown_url(link: SharedString, window: &mut Window, cx: &mut App)
cx, cx,
); );
cx.spawn_in(window, |_, mut cx| async move { cx.spawn_in(window, async move |_, cx| {
let item = task.await?; let item = task.await?;
// Ruby LSP uses URLs with #L1,1-4,4 // Ruby LSP uses URLs with #L1,1-4,4
// we'll just take the first number and assume it's a line number // we'll just take the first number and assume it's a line number
@ -664,7 +665,7 @@ pub fn open_markdown_url(link: SharedString, window: &mut Window, cx: &mut App)
let Some(editor) = cx.update(|_, cx| item.act_as::<Editor>(cx))? else { let Some(editor) = cx.update(|_, cx| item.act_as::<Editor>(cx))? else {
return Ok(()); return Ok(());
}; };
editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(cx, |editor, window, cx| {
editor.change_selections( editor.change_selections(
Some(Autoscroll::fit()), Some(Autoscroll::fit()),
window, window,

View file

@ -111,16 +111,15 @@ impl Editor {
{ {
Ok(result) => state.active_indent_range = result, Ok(result) => state.active_indent_range = result,
Err(future) => { Err(future) => {
state.pending_refresh = state.pending_refresh = Some(cx.spawn_in(window, async move |editor, cx| {
Some(cx.spawn_in(window, |editor, mut cx| async move { let result = cx.background_spawn(future).await;
let result = cx.background_spawn(future).await; editor
editor .update(cx, |editor, _| {
.update(&mut cx, |editor, _| { editor.active_indent_guides_state.active_indent_range = result;
editor.active_indent_guides_state.active_indent_range = result; editor.active_indent_guides_state.pending_refresh = None;
editor.active_indent_guides_state.pending_refresh = None; })
}) .log_err();
.log_err(); }));
}));
return None; return None;
} }
} }

View file

@ -412,13 +412,13 @@ impl InlayHintCache {
} else { } else {
self.append_debounce self.append_debounce
}; };
self.refresh_task = cx.spawn(|editor, mut cx| async move { self.refresh_task = cx.spawn(async move |editor, cx| {
if let Some(debounce_duration) = debounce_duration { if let Some(debounce_duration) = debounce_duration {
cx.background_executor().timer(debounce_duration).await; cx.background_executor().timer(debounce_duration).await;
} }
editor editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
spawn_new_update_tasks( spawn_new_update_tasks(
editor, editor,
reason_description, reason_description,
@ -626,8 +626,8 @@ impl InlayHintCache {
let server_id = *server_id; let server_id = *server_id;
cached_hint.resolve_state = ResolveState::Resolving; cached_hint.resolve_state = ResolveState::Resolving;
drop(guard); drop(guard);
cx.spawn_in(window, |editor, mut cx| async move { cx.spawn_in(window, async move |editor, cx| {
let resolved_hint_task = editor.update(&mut cx, |editor, cx| { let resolved_hint_task = editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).buffer(buffer_id)?; let buffer = editor.buffer().read(cx).buffer(buffer_id)?;
editor.semantics_provider.as_ref()?.resolve_inlay_hint( editor.semantics_provider.as_ref()?.resolve_inlay_hint(
hint_to_resolve, hint_to_resolve,
@ -639,7 +639,7 @@ impl InlayHintCache {
if let Some(resolved_hint_task) = resolved_hint_task { if let Some(resolved_hint_task) = resolved_hint_task {
let mut resolved_hint = let mut resolved_hint =
resolved_hint_task.await.context("hint resolve task")?; resolved_hint_task.await.context("hint resolve task")?;
editor.update(&mut cx, |editor, _| { editor.update(cx, |editor, _| {
if let Some(excerpt_hints) = if let Some(excerpt_hints) =
editor.inlay_hint_cache.hints.get(&excerpt_id) editor.inlay_hint_cache.hints.get(&excerpt_id)
{ {
@ -846,14 +846,14 @@ fn new_update_task(
excerpt_buffer: Entity<Buffer>, excerpt_buffer: Entity<Buffer>,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) -> Task<()> { ) -> Task<()> {
cx.spawn(move |editor, mut cx| async move { cx.spawn(async move |editor, cx| {
let visible_range_update_results = future::join_all( let visible_range_update_results = future::join_all(
query_ranges query_ranges
.visible .visible
.into_iter() .into_iter()
.filter_map(|visible_range| { .filter_map(|visible_range| {
let fetch_task = editor let fetch_task = editor
.update(&mut cx, |_, cx| { .update(cx, |_, cx| {
fetch_and_update_hints( fetch_and_update_hints(
excerpt_buffer.clone(), excerpt_buffer.clone(),
query, query,
@ -891,7 +891,7 @@ fn new_update_task(
for (range, result) in visible_range_update_results { for (range, result) in visible_range_update_results {
if let Err(e) = result { if let Err(e) = result {
query_range_failed(&range, e, &mut cx); query_range_failed(&range, e, cx);
} }
} }
@ -903,7 +903,7 @@ fn new_update_task(
.chain(query_ranges.after_visible.into_iter()) .chain(query_ranges.after_visible.into_iter())
.filter_map(|invisible_range| { .filter_map(|invisible_range| {
let fetch_task = editor let fetch_task = editor
.update(&mut cx, |_, cx| { .update(cx, |_, cx| {
fetch_and_update_hints( fetch_and_update_hints(
excerpt_buffer.clone(), excerpt_buffer.clone(),
query, query,
@ -919,7 +919,7 @@ fn new_update_task(
.await; .await;
for (range, result) in invisible_range_update_results { for (range, result) in invisible_range_update_results {
if let Err(e) = result { if let Err(e) = result {
query_range_failed(&range, e, &mut cx); query_range_failed(&range, e, cx);
} }
} }
}) })
@ -932,10 +932,10 @@ fn fetch_and_update_hints(
invalidate: bool, invalidate: bool,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) -> Task<anyhow::Result<()>> { ) -> Task<anyhow::Result<()>> {
cx.spawn(|editor, mut cx| async move { cx.spawn(async move |editor, cx|{
let buffer_snapshot = excerpt_buffer.update(&mut cx, |buffer, _| buffer.snapshot())?; let buffer_snapshot = excerpt_buffer.update(cx, |buffer, _| buffer.snapshot())?;
let (lsp_request_limiter, multi_buffer_snapshot) = let (lsp_request_limiter, multi_buffer_snapshot) =
editor.update(&mut cx, |editor, cx| { editor.update(cx, |editor, cx| {
let multi_buffer_snapshot = let multi_buffer_snapshot =
editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx)); editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
let lsp_request_limiter = Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter); let lsp_request_limiter = Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter);
@ -953,7 +953,7 @@ fn fetch_and_update_hints(
let fetch_range_to_log = fetch_range.start.to_point(&buffer_snapshot) let fetch_range_to_log = fetch_range.start.to_point(&buffer_snapshot)
..fetch_range.end.to_point(&buffer_snapshot); ..fetch_range.end.to_point(&buffer_snapshot);
let inlay_hints_fetch_task = editor let inlay_hints_fetch_task = editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
if got_throttled { if got_throttled {
let query_not_around_visible_range = match editor let query_not_around_visible_range = match editor
.excerpts_for_inlay_hints_query(None, cx) .excerpts_for_inlay_hints_query(None, cx)
@ -997,7 +997,7 @@ fn fetch_and_update_hints(
.ok() .ok()
.flatten(); .flatten();
let cached_excerpt_hints = editor.update(&mut cx, |editor, _| { let cached_excerpt_hints = editor.update(cx, |editor, _| {
editor editor
.inlay_hint_cache .inlay_hint_cache
.hints .hints
@ -1005,7 +1005,7 @@ fn fetch_and_update_hints(
.cloned() .cloned()
})?; })?;
let visible_hints = editor.update(&mut cx, |editor, cx| editor.visible_inlay_hints(cx))?; let visible_hints = editor.update(cx, |editor, cx| editor.visible_inlay_hints(cx))?;
let new_hints = match inlay_hints_fetch_task { let new_hints = match inlay_hints_fetch_task {
Some(fetch_task) => { Some(fetch_task) => {
log::debug!( log::debug!(
@ -1050,7 +1050,7 @@ fn fetch_and_update_hints(
); );
log::trace!("New update: {new_update:?}"); log::trace!("New update: {new_update:?}");
editor editor
.update(&mut cx, |editor, cx| { .update(cx, |editor, cx| {
apply_hint_update( apply_hint_update(
editor, editor,
new_update, new_update,

View file

@ -87,7 +87,7 @@ impl FollowableItem for Editor {
.collect::<Result<Vec<_>>>() .collect::<Result<Vec<_>>>()
}); });
Some(window.spawn(cx, |mut cx| async move { Some(window.spawn(cx, async move |cx| {
let mut buffers = futures::future::try_join_all(buffers?) let mut buffers = futures::future::try_join_all(buffers?)
.await .await
.debug_assert_ok("leaders don't share views for unshared buffers")?; .debug_assert_ok("leaders don't share views for unshared buffers")?;
@ -147,7 +147,7 @@ impl FollowableItem for Editor {
scroll_y: state.scroll_y, scroll_y: state.scroll_y,
..Default::default() ..Default::default()
}, },
&mut cx, cx,
) )
.await?; .await?;
@ -319,8 +319,8 @@ impl FollowableItem for Editor {
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let update_view::Variant::Editor(message) = message; let update_view::Variant::Editor(message) = message;
let project = project.clone(); let project = project.clone();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
update_editor_from_message(this, project, message, &mut cx).await update_editor_from_message(this, project, message, cx).await
}) })
} }
@ -776,9 +776,9 @@ impl Item for Editor {
.into_iter() .into_iter()
.map(|handle| handle.read(cx).base_buffer().unwrap_or(handle.clone())) .map(|handle| handle.read(cx).base_buffer().unwrap_or(handle.clone()))
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
if format { if format {
this.update_in(&mut cx, |editor, window, cx| { this.update_in(cx, |editor, window, cx| {
editor.perform_format( editor.perform_format(
project.clone(), project.clone(),
FormatTrigger::Save, FormatTrigger::Save,
@ -793,7 +793,7 @@ impl Item for Editor {
if buffers.len() == 1 { if buffers.len() == 1 {
// Apply full save routine for singleton buffers, to allow to `touch` the file via the editor. // Apply full save routine for singleton buffers, to allow to `touch` the file via the editor.
project project
.update(&mut cx, |project, cx| project.save_buffers(buffers, cx))? .update(cx, |project, cx| project.save_buffers(buffers, cx))?
.await?; .await?;
} else { } else {
// For multi-buffers, only format and save the buffers with changes. // For multi-buffers, only format and save the buffers with changes.
@ -801,20 +801,16 @@ impl Item for Editor {
// so that language servers or other downstream listeners of save events get notified. // so that language servers or other downstream listeners of save events get notified.
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| { let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
buffer buffer
.update(&mut cx, |buffer, _| { .update(cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict())
buffer.is_dirty() || buffer.has_conflict()
})
.unwrap_or(false) .unwrap_or(false)
}); });
project project
.update(&mut cx, |project, cx| { .update(cx, |project, cx| project.save_buffers(dirty_buffers, cx))?
project.save_buffers(dirty_buffers, cx)
})?
.await?; .await?;
for buffer in clean_buffers { for buffer in clean_buffers {
buffer buffer
.update(&mut cx, |buffer, cx| { .update(cx, |buffer, cx| {
let version = buffer.saved_version().clone(); let version = buffer.saved_version().clone();
let mtime = buffer.saved_mtime(); let mtime = buffer.saved_mtime();
buffer.did_save(version, mtime, cx); buffer.did_save(version, mtime, cx);
@ -859,13 +855,13 @@ impl Item for Editor {
let buffers = self.buffer.read(cx).all_buffers(); let buffers = self.buffer.read(cx).all_buffers();
let reload_buffers = let reload_buffers =
project.update(cx, |project, cx| project.reload_buffers(buffers, true, cx)); project.update(cx, |project, cx| project.reload_buffers(buffers, true, cx));
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let transaction = reload_buffers.log_err().await; let transaction = reload_buffers.log_err().await;
this.update(&mut cx, |editor, cx| { this.update(cx, |editor, cx| {
editor.request_autoscroll(Autoscroll::fit(), cx) editor.request_autoscroll(Autoscroll::fit(), cx)
})?; })?;
buffer buffer
.update(&mut cx, |buffer, cx| { .update(cx, |buffer, cx| {
if let Some(transaction) = transaction { if let Some(transaction) = transaction {
if !buffer.is_singleton() { if !buffer.is_singleton() {
buffer.push_transaction(&transaction.0, cx); buffer.push_transaction(&transaction.0, cx);
@ -996,7 +992,9 @@ impl SerializableItem for Editor {
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
window.spawn(cx, |_| DB.delete_unloaded_items(workspace_id, alive_items)) window.spawn(cx, async move |_| {
DB.delete_unloaded_items(workspace_id, alive_items).await
})
} }
fn deserialize( fn deserialize(
@ -1040,11 +1038,11 @@ impl SerializableItem for Editor {
contents: Some(contents), contents: Some(contents),
language, language,
.. ..
} => window.spawn(cx, |mut cx| { } => window.spawn(cx, {
let project = project.clone(); let project = project.clone();
async move { async move |cx| {
let language_registry = let language_registry =
project.update(&mut cx, |project, _| project.languages().clone())?; project.update(cx, |project, _| project.languages().clone())?;
let language = if let Some(language_name) = language { let language = if let Some(language_name) = language {
// We don't fail here, because we'd rather not set the language if the name changed // We don't fail here, because we'd rather not set the language if the name changed
@ -1059,11 +1057,11 @@ impl SerializableItem for Editor {
// First create the empty buffer // First create the empty buffer
let buffer = project let buffer = project
.update(&mut cx, |project, cx| project.create_buffer(cx))? .update(cx, |project, cx| project.create_buffer(cx))?
.await?; .await?;
// Then set the text so that the dirty bit is set correctly // Then set the text so that the dirty bit is set correctly
buffer.update(&mut cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.set_language_registry(language_registry); buffer.set_language_registry(language_registry);
if let Some(language) = language { if let Some(language) = language {
buffer.set_language(Some(language), cx); buffer.set_language(Some(language), cx);
@ -1102,7 +1100,7 @@ impl SerializableItem for Editor {
match project_item { match project_item {
Some(project_item) => { Some(project_item) => {
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let (_, project_item) = project_item.await?; let (_, project_item) = project_item.await?;
let buffer = project_item.downcast::<Buffer>().map_err(|_| { let buffer = project_item.downcast::<Buffer>().map_err(|_| {
anyhow!("Project item at stored path was not a buffer") anyhow!("Project item at stored path was not a buffer")
@ -1114,7 +1112,7 @@ impl SerializableItem for Editor {
// simple, because we don't have to persist all of the metadata that we get // simple, because we don't have to persist all of the metadata that we get
// by loading the file (git diff base, ...). // by loading the file (git diff base, ...).
if let Some(buffer_text) = contents { if let Some(buffer_text) = contents {
buffer.update(&mut cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
// If we did restore an mtime, we want to store it on the buffer // If we did restore an mtime, we want to store it on the buffer
// so that the next edit will mark the buffer as dirty/conflicted. // so that the next edit will mark the buffer as dirty/conflicted.
if mtime.is_some() { if mtime.is_some() {
@ -1166,9 +1164,9 @@ impl SerializableItem for Editor {
cx, cx,
) )
}); });
window.spawn(cx, |mut cx| async move { window.spawn(cx, async move |cx| {
let editor = open_by_abs_path?.await?.downcast::<Editor>().with_context(|| format!("Failed to downcast to Editor after opening abs path {abs_path:?}"))?; let editor = open_by_abs_path?.await?.downcast::<Editor>().with_context(|| format!("Failed to downcast to Editor after opening abs path {abs_path:?}"))?;
editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(cx, |editor, window, cx| {
editor.read_selections_from_db(item_id, workspace_id, window, cx); editor.read_selections_from_db(item_id, workspace_id, window, cx);
editor.read_scroll_position_from_db(item_id, workspace_id, window, cx); editor.read_scroll_position_from_db(item_id, workspace_id, window, cx);
})?; })?;
@ -1228,7 +1226,7 @@ impl SerializableItem for Editor {
let snapshot = buffer.read(cx).snapshot(); let snapshot = buffer.read(cx).snapshot();
Some(cx.spawn_in(window, |_this, cx| async move { Some(cx.spawn_in(window, async move |_this, cx| {
cx.background_spawn(async move { cx.background_spawn(async move {
let (contents, language) = if serialize_dirty_buffers && is_dirty { let (contents, language) = if serialize_dirty_buffers && is_dirty {
let contents = snapshot.text(); let contents = snapshot.text();

View file

@ -434,7 +434,7 @@ pub(crate) fn handle_from(
let (buffer_version_initial, mut buffer_parse_status_rx) = let (buffer_version_initial, mut buffer_parse_status_rx) =
buffer.read_with(cx, |buffer, _| (buffer.version(), buffer.parse_status())); buffer.read_with(cx, |buffer, _| (buffer.version(), buffer.parse_status()));
cx.spawn_in(window, |this, mut cx| async move { cx.spawn_in(window, async move |this, cx| {
let Some(buffer_parse_status) = buffer_parse_status_rx.recv().await.ok() else { let Some(buffer_parse_status) = buffer_parse_status_rx.recv().await.ok() else {
return Some(()); return Some(());
}; };
@ -445,7 +445,7 @@ pub(crate) fn handle_from(
}; };
} }
let buffer_snapshot = buffer.read_with(&cx, |buf, _| buf.snapshot()).ok()?; let buffer_snapshot = buffer.read_with(cx, |buf, _| buf.snapshot()).ok()?;
let Some(edit_behavior_state) = let Some(edit_behavior_state) =
should_auto_close(&buffer_snapshot, &edited_ranges, &jsx_tag_auto_close_config) should_auto_close(&buffer_snapshot, &edited_ranges, &jsx_tag_auto_close_config)
@ -456,7 +456,7 @@ pub(crate) fn handle_from(
let ensure_no_edits_since_start = || -> Option<()> { let ensure_no_edits_since_start = || -> Option<()> {
// <div>wef,wefwef // <div>wef,wefwef
let has_edits_since_start = this let has_edits_since_start = this
.read_with(&cx, |this, cx| { .read_with(cx, |this, cx| {
this.buffer.read_with(cx, |buffer, cx| { this.buffer.read_with(cx, |buffer, cx| {
buffer.buffer(buffer_id).map_or(true, |buffer| { buffer.buffer(buffer_id).map_or(true, |buffer| {
buffer.read_with(cx, |buffer, _| { buffer.read_with(cx, |buffer, _| {
@ -506,7 +506,7 @@ pub(crate) fn handle_from(
ensure_no_edits_since_start()?; ensure_no_edits_since_start()?;
let multi_buffer_snapshot = this let multi_buffer_snapshot = this
.read_with(&cx, |this, cx| { .read_with(cx, |this, cx| {
this.buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)) this.buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx))
}) })
.ok()?; .ok()?;
@ -516,7 +516,7 @@ pub(crate) fn handle_from(
{ {
let selections = this let selections = this
.read_with(&cx, |this, _| this.selections.disjoint_anchors().clone()) .read_with(cx, |this, _| this.selections.disjoint_anchors().clone())
.ok()?; .ok()?;
for selection in selections.iter() { for selection in selections.iter() {
let Some(selection_buffer_offset_head) = let Some(selection_buffer_offset_head) =
@ -576,14 +576,14 @@ pub(crate) fn handle_from(
} }
buffer buffer
.update(&mut cx, |buffer, cx| { .update(cx, |buffer, cx| {
buffer.edit(edits, None, cx); buffer.edit(edits, None, cx);
}) })
.ok()?; .ok()?;
if any_selections_need_update { if any_selections_need_update {
let multi_buffer_snapshot = this let multi_buffer_snapshot = this
.read_with(&cx, |this, cx| { .read_with(cx, |this, cx| {
this.buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)) this.buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx))
}) })
.ok()?; .ok()?;
@ -601,7 +601,7 @@ pub(crate) fn handle_from(
selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot)) selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
this.update_in(&mut cx, |this, window, cx| { this.update_in(cx, |this, window, cx| {
this.change_selections_inner(None, false, window, cx, |s| { this.change_selections_inner(None, false, window, cx, |s| {
s.select(base_selections); s.select(base_selections);
}); });

Some files were not shown because too many files have changed in this diff Show more