diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index e79e5555a61d0ddb8a93a1708c676554f191c3f6..7371eb678538dbc12abe43bde4073ffd9d2bdb21 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -568,18 +568,15 @@ impl WrapSnapshot { let mut old_start = old_cursor.start().output.lines; old_start += tab_edit.old.start.0 - old_cursor.start().input.lines; - // todo(lw): Should these be seek_forward? - old_cursor.seek(&tab_edit.old.end, Bias::Right); + old_cursor.seek_forward(&tab_edit.old.end, Bias::Right); let mut old_end = old_cursor.start().output.lines; old_end += tab_edit.old.end.0 - old_cursor.start().input.lines; - // todo(lw): Should these be seek_forward? new_cursor.seek(&tab_edit.new.start, Bias::Right); let mut new_start = new_cursor.start().output.lines; new_start += tab_edit.new.start.0 - new_cursor.start().input.lines; - // todo(lw): Should these be seek_forward? - new_cursor.seek(&tab_edit.new.end, Bias::Right); + new_cursor.seek_forward(&tab_edit.new.end, Bias::Right); let mut new_end = new_cursor.start().output.lines; new_end += tab_edit.new.end.0 - new_cursor.start().input.lines; diff --git a/crates/editor/src/git/blame.rs b/crates/editor/src/git/blame.rs index 4f210cc9db8913eb7c46c6150d1ecd5d4f9020bb..b36a57a7e47bf148fff4201ec87ac7c868658a04 100644 --- a/crates/editor/src/git/blame.rs +++ b/crates/editor/src/git/blame.rs @@ -602,6 +602,7 @@ impl GitBlame { } fn regenerate_on_edit(&mut self, cx: &mut Context) { + // todo(lw): hot foreground spawn self.regenerate_on_edit_task = cx.spawn(async move |this, cx| { cx.background_executor() .timer(REGENERATE_ON_EDIT_DEBOUNCE_INTERVAL) diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index 2638a49eba5d1c69a41a759efedfe4814ed6dc2c..2a67ff67479021353d7231939726a13b948bf4b7 100644 --- a/crates/go_to_line/src/cursor_position.rs +++ b/crates/go_to_line/src/cursor_position.rs @@ -1,4 +1,4 @@ -use editor::{Editor, MultiBufferSnapshot}; +use editor::{Editor, EditorEvent, MultiBufferSnapshot}; use gpui::{App, Entity, FocusHandle, Focusable, Styled, Subscription, Task, WeakEntity}; use settings::Settings; use std::{fmt::Write, num::NonZeroU32, time::Duration}; @@ -81,7 +81,7 @@ impl CursorPosition { fn update_position( &mut self, - editor: Entity, + editor: &Entity, debounce: Option, window: &mut Window, cx: &mut Context, @@ -269,19 +269,21 @@ impl StatusItemView for CursorPosition { cx: &mut Context, ) { if let Some(editor) = active_pane_item.and_then(|item| item.act_as::(cx)) { - self._observe_active_editor = - Some( - cx.observe_in(&editor, window, |cursor_position, editor, window, cx| { - Self::update_position( - cursor_position, - editor, - Some(UPDATE_DEBOUNCE), - window, - cx, - ) - }), - ); - self.update_position(editor, None, window, cx); + self._observe_active_editor = Some(cx.subscribe_in( + &editor, + window, + |cursor_position, editor, event, window, cx| match event { + EditorEvent::SelectionsChanged { .. } => Self::update_position( + cursor_position, + editor, + Some(UPDATE_DEBOUNCE), + window, + cx, + ), + _ => {} + }, + )); + self.update_position(&editor, None, window, cx); } else { self.position = None; self._observe_active_editor = None; diff --git a/crates/gpui/src/app/async_context.rs b/crates/gpui/src/app/async_context.rs index cfe7a5a75c258d09194c7d77a117208161713c6f..381541d4b11377b988dd30e03155855c7ba25aed 100644 --- a/crates/gpui/src/app/async_context.rs +++ b/crates/gpui/src/app/async_context.rs @@ -176,7 +176,7 @@ impl AsyncApp { lock.open_window(options, build_root_view) } - /// Schedule a future to be polled in the background. + /// Schedule a future to be polled in the foreground. #[track_caller] pub fn spawn(&self, f: AsyncFn) -> Task where diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index b820e120dd738df8a39d3a40379414984942f158..b6d3a407f5dbbab07e0273e668e9b5710824edda 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -479,7 +479,6 @@ impl ForegroundExecutor { } /// Enqueues the given Task to run on the main thread at some point in the future. - #[track_caller] pub fn spawn(&self, future: impl Future + 'static) -> Task where R: 'static, diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index c2da93aa7399267f6300625da58aba9bf6dccc4f..c72350f38561e7aea62b7d3402eaa24bbdb08044 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1573,21 +1573,24 @@ impl Buffer { self.reparse = None; } Err(parse_task) => { + // todo(lw): hot foreground spawn self.reparse = Some(cx.spawn(async move |this, cx| { - let new_syntax_map = parse_task.await; + let new_syntax_map = cx.background_spawn(parse_task).await; this.update(cx, move |this, cx| { - let grammar_changed = + let grammar_changed = || { this.language.as_ref().is_none_or(|current_language| { !Arc::ptr_eq(&language, current_language) - }); - let language_registry_changed = new_syntax_map - .contains_unknown_injections() - && language_registry.is_some_and(|registry| { - registry.version() != new_syntax_map.language_registry_version() - }); - let parse_again = language_registry_changed - || grammar_changed - || this.version.changed_since(&parsed_version); + }) + }; + let language_registry_changed = || { + new_syntax_map.contains_unknown_injections() + && language_registry.is_some_and(|registry| { + registry.version() != new_syntax_map.language_registry_version() + }) + }; + let parse_again = this.version.changed_since(&parsed_version) + || language_registry_changed() + || grammar_changed(); this.did_finish_parsing(new_syntax_map, cx); this.reparse = None; if parse_again { diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index b6175b7aaab4f631728bcfaf8094120068032994..568d1ac8671fc3e10fb7656dfdffa7211accd1cd 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -1,7 +1,7 @@ use std::{mem, ops::Range, sync::Arc}; use collections::HashSet; -use gpui::{App, AppContext, Context, Entity, Task}; +use gpui::{App, AppContext, Context, Entity}; use itertools::Itertools; use language::{Buffer, BufferSnapshot}; use rope::Point; @@ -117,12 +117,14 @@ impl MultiBuffer { buffer: Entity, ranges: Vec>, context_line_count: u32, - cx: &mut Context, - ) -> Task>> { + cx: &Context, + ) -> impl Future>> + use<> { let buffer_snapshot = buffer.read(cx).snapshot(); - cx.spawn(async move |multi_buffer, cx| { + let multi_buffer = cx.weak_entity(); + let mut app = cx.to_async(); + async move { let snapshot = buffer_snapshot.clone(); - let (excerpt_ranges, new, counts) = cx + let (excerpt_ranges, new, counts) = app .background_spawn(async move { let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot)); let excerpt_ranges = @@ -133,7 +135,7 @@ impl MultiBuffer { .await; multi_buffer - .update(cx, move |multi_buffer, cx| { + .update(&mut app, move |multi_buffer, cx| { let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path( path_key, buffer, @@ -147,7 +149,7 @@ impl MultiBuffer { }) .ok() .unwrap_or_default() - }) + } } pub(super) fn expand_excerpts_with_paths( diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index b9249d36e2ca8da6b17f342a8db9f3dcca113515..39e302a2d9b1ae92cce9691c957cb9fcfbf26d7d 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -619,29 +619,24 @@ impl LocalBufferStore { worktree: Entity, cx: &mut Context, ) -> Task>> { - let load_buffer = worktree.update(cx, |worktree, cx| { - let load_file = worktree.load_file(path.as_ref(), cx); - let reservation = cx.reserve_entity(); - let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64()); - let path = path.clone(); - cx.spawn(async move |_, cx| { - let loaded = load_file.await.with_context(|| { - format!("Could not open path: {}", path.display(PathStyle::local())) - })?; - let text_buffer = cx - .background_spawn(async move { - text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text) - }) - .await; - cx.insert_entity(reservation, |_| { - Buffer::build(text_buffer, Some(loaded.file), Capability::ReadWrite) - }) - }) - }); - + let load_file = worktree.update(cx, |worktree, cx| worktree.load_file(path.as_ref(), cx)); cx.spawn(async move |this, cx| { - let buffer = match load_buffer.await { - Ok(buffer) => Ok(buffer), + let path = path.clone(); + let buffer = match load_file.await.with_context(|| { + format!("Could not open path: {}", path.display(PathStyle::local())) + }) { + Ok(loaded) => { + let reservation = cx.reserve_entity::()?; + let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64()); + let text_buffer = cx + .background_spawn(async move { + text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text) + }) + .await; + cx.insert_entity(reservation, |_| { + Buffer::build(text_buffer, Some(loaded.file), Capability::ReadWrite) + })? + } Err(error) if is_not_found_error(&error) => cx.new(|cx| { let buffer_id = BufferId::from(cx.entity_id().as_non_zero_u64()); let text_buffer = text::Buffer::new(ReplicaId::LOCAL, buffer_id, ""); @@ -657,9 +652,9 @@ impl LocalBufferStore { })), Capability::ReadWrite, ) - }), - Err(e) => Err(e), - }?; + })?, + Err(e) => return Err(e), + }; this.update(cx, |this, cx| { this.add_buffer(buffer.clone(), cx)?; let buffer_id = buffer.read(cx).remote_id(); @@ -840,6 +835,7 @@ impl BufferStore { entry .insert( + // todo(lw): hot foreground spawn cx.spawn(async move |this, cx| { let load_result = load_buffer.await; this.update(cx, |this, cx| { diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 736c96f34e171c4fde83c2db032484456144ae5a..03642df3b4f395e190d03feb04203f7595aaf3cf 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -709,6 +709,7 @@ impl GitStore { repo.load_committed_text(buffer_id, repo_path, cx) }); + // todo(lw): hot foreground spawn cx.spawn(async move |this, cx| { Self::open_diff_internal(this, DiffKind::Uncommitted, changes.await, buffer, cx) .await diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 1d4dbc6c86be9ba80e62c29ef32ce1161a6d1a25..891ad2420c6f8a79659a1f05afd0821b995b5b1a 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -9171,7 +9171,9 @@ async fn test_odd_events_for_ignored_dirs( repository_updates.lock().drain(..).collect::>(), vec![ RepositoryEvent::MergeHeadsChanged, - RepositoryEvent::BranchChanged + RepositoryEvent::BranchChanged, + RepositoryEvent::StatusesChanged { full_scan: false }, + RepositoryEvent::StatusesChanged { full_scan: false }, ], "Initial worktree scan should produce a repo update event" ); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index f407a0a4dbfd00b6515a392f18572c373499d2cc..a8be82d5d5a3fcb20b8ea964af19e3f60fea0573 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -322,18 +322,25 @@ impl ProjectSearch { let mut limit_reached = false; while let Some(results) = matches.next().await { - let mut buffers_with_ranges = Vec::with_capacity(results.len()); - for result in results { - match result { - project::search::SearchResult::Buffer { buffer, ranges } => { - buffers_with_ranges.push((buffer, ranges)); - } - project::search::SearchResult::LimitReached => { - limit_reached = true; + let (buffers_with_ranges, has_reached_limit) = cx + .background_executor() + .spawn(async move { + let mut limit_reached = false; + let mut buffers_with_ranges = Vec::with_capacity(results.len()); + for result in results { + match result { + project::search::SearchResult::Buffer { buffer, ranges } => { + buffers_with_ranges.push((buffer, ranges)); + } + project::search::SearchResult::LimitReached => { + limit_reached = true; + } + } } - } - } - + (buffers_with_ranges, limit_reached) + }) + .await; + limit_reached |= has_reached_limit; let mut new_ranges = project_search .update(cx, |project_search, cx| { project_search.excerpts.update(cx, |excerpts, cx| { @@ -352,7 +359,6 @@ impl ProjectSearch { }) }) .ok()?; - while let Some(new_ranges) = new_ranges.next().await { project_search .update(cx, |project_search, cx| { diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 5f8253e2dfb48fa6882dabf49c64073023a2a298..a4d3f61141c8b05a7ff2ccf2ef0df5896833f199 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -1318,7 +1318,8 @@ impl LocalWorktree { let entry = self.refresh_entry(path.clone(), None, cx); let is_private = self.is_path_private(path.as_ref()); - cx.spawn(async move |this, _cx| { + let this = cx.weak_entity(); + cx.background_spawn(async move { // WARN: Temporary workaround for #27283. // We are not efficient with our memory usage per file, and use in excess of 64GB for a 10GB file // Therefore, as a temporary workaround to prevent system freezes, we just bail before opening a file @@ -1702,6 +1703,7 @@ impl LocalWorktree { }; let t0 = Instant::now(); let mut refresh = self.refresh_entries_for_paths(paths); + // todo(lw): Hot foreground spawn cx.spawn(async move |this, cx| { refresh.recv().await; log::trace!("refreshed entry {path:?} in {:?}", t0.elapsed()); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index d712f782ca78745a94ce22c9a57900a8b8e42863..2d7d47e968e93eef3d455cec9c324a4d4e0cff42 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2860,16 +2860,20 @@ mod tests { }); // Split the pane with the first entry, then open the second entry again. - let (task1, task2) = window + window .update(cx, |w, window, cx| { - ( - w.split_and_clone(w.active_pane().clone(), SplitDirection::Right, window, cx), - w.open_path(file2.clone(), None, true, window, cx), - ) + w.split_and_clone(w.active_pane().clone(), SplitDirection::Right, window, cx) + }) + .unwrap() + .await + .unwrap(); + window + .update(cx, |w, window, cx| { + w.open_path(file2.clone(), None, true, window, cx) }) + .unwrap() + .await .unwrap(); - task1.await.unwrap(); - task2.await.unwrap(); window .read_with(cx, |w, cx| {