Properly register initialized default prettier (#33669)

Kirill Bulatov created

Stop doing useless prettier-related work when doing a project search.

Before, project search might cause

<img width="1728" alt="not_pretty"
src="https://github.com/user-attachments/assets/5f8b935f-962d-488e-984f-50dfbaee97ba"
/>

but now we debounce the prettier-related task first, and actually set
the "installed" state for the default prettier, when there's no install
needed.

Release Notes:

- N/A

Change summary

crates/project/src/prettier_store.rs | 86 +++++++++++++++++------------
1 file changed, 49 insertions(+), 37 deletions(-)

Detailed changes

crates/project/src/prettier_store.rs 🔗

@@ -2,6 +2,7 @@ use std::{
     ops::ControlFlow,
     path::{Path, PathBuf},
     sync::Arc,
+    time::Duration,
 };
 
 use anyhow::{Context as _, Result, anyhow};
@@ -527,26 +528,6 @@ impl PrettierStore {
         let mut new_plugins = plugins.collect::<HashSet<_>>();
         let node = self.node.clone();
 
-        let fs = Arc::clone(&self.fs);
-        let locate_prettier_installation = match worktree.and_then(|worktree_id| {
-            self.worktree_store
-                .read(cx)
-                .worktree_for_id(worktree_id, cx)
-                .map(|worktree| worktree.read(cx).abs_path())
-        }) {
-            Some(locate_from) => {
-                let installed_prettiers = self.prettier_instances.keys().cloned().collect();
-                cx.background_spawn(async move {
-                    Prettier::locate_prettier_installation(
-                        fs.as_ref(),
-                        &installed_prettiers,
-                        locate_from.as_ref(),
-                    )
-                    .await
-                })
-            }
-            None => Task::ready(Ok(ControlFlow::Continue(None))),
-        };
         new_plugins.retain(|plugin| !self.default_prettier.installed_plugins.contains(plugin));
         let mut installation_attempt = 0;
         let previous_installation_task = match &mut self.default_prettier.prettier {
@@ -574,15 +555,34 @@ impl PrettierStore {
             }
         };
 
-        log::info!("Initializing default prettier with plugins {new_plugins:?}");
         let plugins_to_install = new_plugins.clone();
         let fs = Arc::clone(&self.fs);
         let new_installation_task = cx
-            .spawn(async move  |project, cx| {
-                match locate_prettier_installation
+            .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)
+                    })
+                })?;
+                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)?
+                    .context("locate prettier installation").map_err(Arc::new)?,
+                    None => ControlFlow::Continue(None),
+                };
+
+                match locate_prettier_installation
                 {
                     ControlFlow::Break(()) => return Ok(()),
                     ControlFlow::Continue(prettier_path) => {
@@ -593,8 +593,8 @@ impl PrettierStore {
                         if let Some(previous_installation_task) = previous_installation_task {
                             if let Err(e) = previous_installation_task.await {
                                 log::error!("Failed to install default prettier: {e:#}");
-                                project.update(cx, |project, _| {
-                                    if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut project.default_prettier.prettier {
+                                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;
@@ -604,8 +604,8 @@ impl PrettierStore {
                             }
                         };
                         if installation_attempt > prettier::FAIL_THRESHOLD {
-                            project.update(cx, |project, _| {
-                                if let PrettierInstallation::NotInstalled { installation_task, .. } = &mut project.default_prettier.prettier {
+                            prettier_store.update(cx, |prettier_store, _| {
+                                if let PrettierInstallation::NotInstalled { installation_task, .. } = &mut prettier_store.default_prettier.prettier {
                                     *installation_task = None;
                                 };
                             })?;
@@ -614,19 +614,20 @@ impl PrettierStore {
                             );
                             return Ok(());
                         }
-                        project.update(cx, |project, _| {
+                        prettier_store.update(cx, |prettier_store, _| {
                             new_plugins.retain(|plugin| {
-                                !project.default_prettier.installed_plugins.contains(plugin)
+                                !prettier_store.default_prettier.installed_plugins.contains(plugin)
                             });
-                            if let PrettierInstallation::NotInstalled { not_installed_plugins, .. } = &mut project.default_prettier.prettier {
+                            if let PrettierInstallation::NotInstalled { not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {
                                 not_installed_plugins.retain(|plugin| {
-                                    !project.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:?}");
                             let installed_plugins = new_plugins.clone();
                             cx.background_spawn(async move {
                                 install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
@@ -637,17 +638,27 @@ impl PrettierStore {
                                 .await
                                 .context("prettier & plugins install")
                                 .map_err(Arc::new)?;
-                            log::info!("Initialized prettier with plugins: {installed_plugins:?}");
-                            project.update(cx, |project, _| {
-                                project.default_prettier.prettier =
+                            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,
                                     });
-                                project.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 {
+                                    prettier_store.default_prettier.prettier =
+                                        PrettierInstallation::Installed(PrettierInstance {
+                                            attempt: 0,
+                                            prettier: None,
+                                        });
+                                }
+                            })?;
                         }
                     }
                 }
@@ -767,6 +778,7 @@ pub(super) async fn format_with_prettier(
     }
 }
 
+#[derive(Debug)]
 pub struct DefaultPrettier {
     prettier: PrettierInstallation,
     installed_plugins: HashSet<Arc<str>>,