sum_tree: Replace rayon with futures (#41586)

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored by: Kate <kate@zed.dev>
This commit is contained in:
Lukas Wirth
2025-10-31 10:39:01 +00:00
committed by GitHub
parent 7c29c6d7a6
commit f2ce06c7b0
67 changed files with 1271 additions and 640 deletions
+15 -3
View File
@@ -180,7 +180,13 @@ impl RemoteBufferStore {
buffer_file = Some(Arc::new(File::from_proto(file, worktree, cx)?)
as Arc<dyn language::File>);
}
Buffer::from_proto(replica_id, capability, state, buffer_file)
Buffer::from_proto(
replica_id,
capability,
state,
buffer_file,
cx.background_executor(),
)
});
match buffer_result {
@@ -628,9 +634,10 @@ impl LocalBufferStore {
Ok(loaded) => {
let reservation = cx.reserve_entity::<Buffer>()?;
let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64());
let executor = cx.background_executor().clone();
let text_buffer = cx
.background_spawn(async move {
text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text)
text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text, &executor)
})
.await;
cx.insert_entity(reservation, |_| {
@@ -639,7 +646,12 @@ impl LocalBufferStore {
}
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, "");
let text_buffer = text::Buffer::new(
ReplicaId::LOCAL,
buffer_id,
"",
cx.background_executor(),
);
Buffer::build(
text_buffer,
Some(Arc::new(File {
+32 -12
View File
@@ -276,8 +276,8 @@ mod tests {
use util::{path, rel_path::rel_path};
use worktree::WorktreeSettings;
#[test]
fn test_parse_conflicts_in_buffer() {
#[gpui::test]
fn test_parse_conflicts_in_buffer(cx: &mut TestAppContext) {
// Create a buffer with conflict markers
let test_content = r#"
This is some text before the conflict.
@@ -299,7 +299,12 @@ mod tests {
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content,
cx.background_executor(),
);
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);
@@ -355,8 +360,8 @@ mod tests {
assert_eq!(conflicts_in_range.len(), 0);
}
#[test]
fn test_nested_conflict_markers() {
#[gpui::test]
fn test_nested_conflict_markers(cx: &mut TestAppContext) {
// Create a buffer with nested conflict markers
let test_content = r#"
This is some text before the conflict.
@@ -374,7 +379,12 @@ mod tests {
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content,
cx.background_executor(),
);
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);
@@ -396,8 +406,8 @@ mod tests {
assert_eq!(their_text, "This is their version in a nested conflict\n");
}
#[test]
fn test_conflict_markers_at_eof() {
#[gpui::test]
fn test_conflict_markers_at_eof(cx: &mut TestAppContext) {
let test_content = r#"
<<<<<<< ours
=======
@@ -405,15 +415,20 @@ mod tests {
>>>>>>> "#
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content,
cx.background_executor(),
);
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);
assert_eq!(conflict_snapshot.conflicts.len(), 1);
}
#[test]
fn test_conflicts_in_range() {
#[gpui::test]
fn test_conflicts_in_range(cx: &mut TestAppContext) {
// Create a buffer with conflict markers
let test_content = r#"
one
@@ -447,7 +462,12 @@ mod tests {
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content.clone());
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content.clone(),
cx.background_executor(),
);
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);
+91 -48
View File
@@ -13,7 +13,9 @@ use futures::{
future::{self, Shared},
stream::FuturesUnordered,
};
use gpui::{AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};
use gpui::{
AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,
};
use language::{
Buffer, LanguageRegistry, LocalFile,
language_settings::{Formatter, LanguageSettings},
@@ -558,99 +560,137 @@ impl PrettierStore {
let plugins_to_install = new_plugins.clone();
let fs = Arc::clone(&self.fs);
let new_installation_task = cx
.spawn(async move |prettier_store, cx| {
cx.background_executor().timer(Duration::from_millis(30)).await;
.spawn(async move |prettier_store, cx| {
cx.background_executor()
.timer(Duration::from_millis(30))
.await;
let location_data = prettier_store.update(cx, |prettier_store, cx| {
worktree.and_then(|worktree_id| {
prettier_store.worktree_store
.read(cx)
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path())
}).map(|locate_from| {
let installed_prettiers = prettier_store.prettier_instances.keys().cloned().collect();
(locate_from, installed_prettiers)
})
worktree
.and_then(|worktree_id| {
prettier_store
.worktree_store
.read(cx)
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path())
})
.map(|locate_from| {
let installed_prettiers =
prettier_store.prettier_instances.keys().cloned().collect();
(locate_from, installed_prettiers)
})
})?;
let locate_prettier_installation = match location_data {
Some((locate_from, installed_prettiers)) => Prettier::locate_prettier_installation(
fs.as_ref(),
&installed_prettiers,
locate_from.as_ref(),
)
.await
.context("locate prettier installation").map_err(Arc::new)?,
Some((locate_from, installed_prettiers)) => {
Prettier::locate_prettier_installation(
fs.as_ref(),
&installed_prettiers,
locate_from.as_ref(),
)
.await
.context("locate prettier installation")
.map_err(Arc::new)?
}
None => ControlFlow::Continue(None),
};
match locate_prettier_installation
{
match locate_prettier_installation {
ControlFlow::Break(()) => return Ok(()),
ControlFlow::Continue(prettier_path) => {
if prettier_path.is_some() {
new_plugins.clear();
}
let mut needs_install = should_write_prettier_server_file(fs.as_ref()).await;
let mut needs_install =
should_write_prettier_server_file(fs.as_ref()).await;
if let Some(previous_installation_task) = previous_installation_task
&& let Err(e) = previous_installation_task.await {
log::error!("Failed to install default prettier: {e:#}");
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {
*attempts += 1;
new_plugins.extend(not_installed_plugins.iter().cloned());
installation_attempt = *attempts;
needs_install = true;
};
})?;
};
&& let Err(e) = previous_installation_task.await
{
log::error!("Failed to install default prettier: {e:#}");
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled {
attempts,
not_installed_plugins,
..
} = &mut prettier_store.default_prettier.prettier
{
*attempts += 1;
new_plugins.extend(not_installed_plugins.iter().cloned());
installation_attempt = *attempts;
needs_install = true;
};
})?;
};
if installation_attempt > prettier::FAIL_THRESHOLD {
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled { installation_task, .. } = &mut prettier_store.default_prettier.prettier {
if let PrettierInstallation::NotInstalled {
installation_task,
..
} = &mut prettier_store.default_prettier.prettier
{
*installation_task = None;
};
})?;
log::warn!(
"Default prettier installation had failed {installation_attempt} times, not attempting again",
"Default prettier installation had failed {installation_attempt} \
times, not attempting again",
);
return Ok(());
}
prettier_store.update(cx, |prettier_store, _| {
new_plugins.retain(|plugin| {
!prettier_store.default_prettier.installed_plugins.contains(plugin)
!prettier_store
.default_prettier
.installed_plugins
.contains(plugin)
});
if let PrettierInstallation::NotInstalled { not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {
if let PrettierInstallation::NotInstalled {
not_installed_plugins,
..
} = &mut prettier_store.default_prettier.prettier
{
not_installed_plugins.retain(|plugin| {
!prettier_store.default_prettier.installed_plugins.contains(plugin)
!prettier_store
.default_prettier
.installed_plugins
.contains(plugin)
});
not_installed_plugins.extend(new_plugins.iter().cloned());
}
needs_install |= !new_plugins.is_empty();
})?;
if needs_install {
log::info!("Initializing default prettier with plugins {new_plugins:?}");
log::info!(
"Initializing default prettier with plugins {new_plugins:?}"
);
let installed_plugins = new_plugins.clone();
let executor = cx.background_executor().clone();
cx.background_spawn(async move {
install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
// Save the server file last, so the reinstall need could be determined by the absence of the file.
save_prettier_server_file(fs.as_ref()).await?;
save_prettier_server_file(fs.as_ref(), &executor).await?;
anyhow::Ok(())
})
.await
.context("prettier & plugins install")
.map_err(Arc::new)?;
log::info!("Initialized default prettier with plugins: {installed_plugins:?}");
.await
.context("prettier & plugins install")
.map_err(Arc::new)?;
log::info!(
"Initialized default prettier with plugins: {installed_plugins:?}"
);
prettier_store.update(cx, |prettier_store, _| {
prettier_store.default_prettier.prettier =
PrettierInstallation::Installed(PrettierInstance {
attempt: 0,
prettier: None,
});
prettier_store.default_prettier
prettier_store
.default_prettier
.installed_plugins
.extend(installed_plugins);
})?;
} else {
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled { .. } = &mut prettier_store.default_prettier.prettier {
if let PrettierInstallation::NotInstalled { .. } =
&mut prettier_store.default_prettier.prettier
{
prettier_store.default_prettier.prettier =
PrettierInstallation::Installed(PrettierInstance {
attempt: 0,
@@ -936,11 +976,14 @@ async fn install_prettier_packages(
anyhow::Ok(())
}
async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {
async fn save_prettier_server_file(
fs: &dyn Fs,
executor: &BackgroundExecutor,
) -> anyhow::Result<()> {
let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
fs.save(
&prettier_wrapper_path,
&text::Rope::from(prettier::PRETTIER_SERVER_JS),
&text::Rope::from_str(prettier::PRETTIER_SERVER_JS, executor),
text::LineEnding::Unix,
)
.await
+10 -3
View File
@@ -712,8 +712,10 @@ pub enum ResolveState {
impl InlayHint {
pub fn text(&self) -> Rope {
match &self.label {
InlayHintLabel::String(s) => Rope::from(s),
InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(),
InlayHintLabel::String(s) => Rope::from_str_small(s),
InlayHintLabel::LabelParts(parts) => {
Rope::from_iter_small(parts.iter().map(|part| &*part.value))
}
}
}
}
@@ -5402,7 +5404,12 @@ impl Project {
worktree
.update(cx, |worktree, cx| {
let line_ending = text::LineEnding::detect(&new_text);
worktree.write_file(rel_path.clone(), new_text.into(), line_ending, cx)
worktree.write_file(
rel_path.clone(),
Rope::from_str(&new_text, cx.background_executor()),
line_ending,
cx,
)
})?
.await
.context("Failed to write settings file")?;
+9 -9
View File
@@ -1461,21 +1461,21 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
.unwrap();
fs.save(
path!("/the-root/Cargo.lock").as_ref(),
&"".into(),
&Rope::default(),
Default::default(),
)
.await
.unwrap();
fs.save(
path!("/the-stdlib/LICENSE").as_ref(),
&"".into(),
&Rope::default(),
Default::default(),
)
.await
.unwrap();
fs.save(
path!("/the/stdlib/src/string.rs").as_ref(),
&"".into(),
&Rope::default(),
Default::default(),
)
.await
@@ -4072,7 +4072,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext)
// to be detected by the worktree, so that the buffer starts reloading.
fs.save(
path!("/dir/file1").as_ref(),
&"the first contents".into(),
&Rope::from_str("the first contents", cx.background_executor()),
Default::default(),
)
.await
@@ -4083,7 +4083,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext)
// previous file change may still be in progress.
fs.save(
path!("/dir/file1").as_ref(),
&"the second contents".into(),
&Rope::from_str("the second contents", cx.background_executor()),
Default::default(),
)
.await
@@ -4127,7 +4127,7 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) {
// to be detected by the worktree, so that the buffer starts reloading.
fs.save(
path!("/dir/file1").as_ref(),
&"the first contents".into(),
&Rope::from_str("the first contents", cx.background_executor()),
Default::default(),
)
.await
@@ -4805,7 +4805,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
marked_text_offsets("oneˇ\nthree ˇFOURˇ five\nsixtyˇ seven\n");
fs.save(
path!("/dir/the-file").as_ref(),
&new_contents.as_str().into(),
&Rope::from_str(new_contents.as_str(), cx.background_executor()),
LineEnding::Unix,
)
.await
@@ -4837,7 +4837,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
// Change the file on disk again, adding blank lines to the beginning.
fs.save(
path!("/dir/the-file").as_ref(),
&"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(),
&Rope::from_str("\n\n\nAAAA\naaa\nBB\nbbbbb\n", cx.background_executor()),
LineEnding::Unix,
)
.await
@@ -4889,7 +4889,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
// state updates correctly.
fs.save(
path!("/dir/file1").as_ref(),
&"aaa\nb\nc\n".into(),
&Rope::from_str("aaa\nb\nc\n", cx.background_executor()),
LineEnding::Windows,
)
.await