prettier_support.rs

  1use std::{
  2    ops::ControlFlow,
  3    path::{Path, PathBuf},
  4    sync::Arc,
  5};
  6
  7use anyhow::Context;
  8use collections::HashSet;
  9use fs::Fs;
 10use futures::{
 11    future::{self, Shared},
 12    FutureExt,
 13};
 14use gpui::{AsyncAppContext, Model, ModelContext, Task, WeakModel};
 15use language::{
 16    language_settings::{Formatter, LanguageSettings},
 17    Buffer, Language, LanguageServerName, LocalFile,
 18};
 19use lsp::{LanguageServer, LanguageServerId};
 20use node_runtime::NodeRuntime;
 21use prettier::Prettier;
 22use util::{paths::DEFAULT_PRETTIER_DIR, ResultExt, TryFutureExt};
 23
 24use crate::{
 25    Event, File, FormatOperation, PathChange, Project, ProjectEntryId, Worktree, WorktreeId,
 26};
 27
 28pub fn prettier_plugins_for_language(
 29    language: &Language,
 30    language_settings: &LanguageSettings,
 31) -> Option<HashSet<&'static str>> {
 32    match &language_settings.formatter {
 33        Formatter::Prettier { .. } | Formatter::Auto => {}
 34        Formatter::LanguageServer | Formatter::External { .. } => return None,
 35    };
 36    let mut prettier_plugins = None;
 37    if language.prettier_parser_name().is_some() {
 38        prettier_plugins
 39            .get_or_insert_with(|| HashSet::default())
 40            .extend(
 41                language
 42                    .lsp_adapters()
 43                    .iter()
 44                    .flat_map(|adapter| adapter.prettier_plugins()),
 45            )
 46    }
 47
 48    prettier_plugins
 49}
 50
 51pub(super) async fn format_with_prettier(
 52    project: &WeakModel<Project>,
 53    buffer: &Model<Buffer>,
 54    cx: &mut AsyncAppContext,
 55) -> Option<FormatOperation> {
 56    if let Some((prettier_path, prettier_task)) = project
 57        .update(cx, |project, cx| {
 58            project.prettier_instance_for_buffer(buffer, cx)
 59        })
 60        .ok()?
 61        .await
 62    {
 63        match prettier_task.await {
 64            Ok(prettier) => {
 65                let buffer_path = buffer
 66                    .update(cx, |buffer, cx| {
 67                        File::from_dyn(buffer.file()).map(|file| file.abs_path(cx))
 68                    })
 69                    .ok()?;
 70                match prettier.format(buffer, buffer_path, cx).await {
 71                    Ok(new_diff) => return Some(FormatOperation::Prettier(new_diff)),
 72                    Err(e) => {
 73                        log::error!(
 74                            "Prettier instance from {prettier_path:?} failed to format a buffer: {e:#}"
 75                        );
 76                    }
 77                }
 78            }
 79            Err(e) => project
 80                .update(cx, |project, _| {
 81                    let instance_to_update = match prettier_path {
 82                        Some(prettier_path) => {
 83                            log::error!(
 84                            "Prettier instance from path {prettier_path:?} failed to spawn: {e:#}"
 85                        );
 86                            project.prettier_instances.get_mut(&prettier_path)
 87                        }
 88                        None => {
 89                            log::error!("Default prettier instance failed to spawn: {e:#}");
 90                            match &mut project.default_prettier.prettier {
 91                                PrettierInstallation::NotInstalled { .. } => None,
 92                                PrettierInstallation::Installed(instance) => Some(instance),
 93                            }
 94                        }
 95                    };
 96
 97                    if let Some(instance) = instance_to_update {
 98                        instance.attempt += 1;
 99                        instance.prettier = None;
100                    }
101                })
102                .ok()?,
103        }
104    }
105
106    None
107}
108
109pub struct DefaultPrettier {
110    prettier: PrettierInstallation,
111    installed_plugins: HashSet<&'static str>,
112}
113
114pub enum PrettierInstallation {
115    NotInstalled {
116        attempts: usize,
117        installation_task: Option<Shared<Task<Result<(), Arc<anyhow::Error>>>>>,
118        not_installed_plugins: HashSet<&'static str>,
119    },
120    Installed(PrettierInstance),
121}
122
123pub type PrettierTask = Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>;
124
125#[derive(Clone)]
126pub struct PrettierInstance {
127    attempt: usize,
128    prettier: Option<PrettierTask>,
129}
130
131impl Default for DefaultPrettier {
132    fn default() -> Self {
133        Self {
134            prettier: PrettierInstallation::NotInstalled {
135                attempts: 0,
136                installation_task: None,
137                not_installed_plugins: HashSet::default(),
138            },
139            installed_plugins: HashSet::default(),
140        }
141    }
142}
143
144impl DefaultPrettier {
145    pub fn instance(&self) -> Option<&PrettierInstance> {
146        if let PrettierInstallation::Installed(instance) = &self.prettier {
147            Some(instance)
148        } else {
149            None
150        }
151    }
152
153    pub fn prettier_task(
154        &mut self,
155        node: &Arc<dyn NodeRuntime>,
156        worktree_id: Option<WorktreeId>,
157        cx: &mut ModelContext<'_, Project>,
158    ) -> Option<Task<anyhow::Result<PrettierTask>>> {
159        match &mut self.prettier {
160            PrettierInstallation::NotInstalled { .. } => {
161                Some(start_default_prettier(Arc::clone(node), worktree_id, cx))
162            }
163            PrettierInstallation::Installed(existing_instance) => {
164                existing_instance.prettier_task(node, None, worktree_id, cx)
165            }
166        }
167    }
168}
169
170impl PrettierInstance {
171    pub fn prettier_task(
172        &mut self,
173        node: &Arc<dyn NodeRuntime>,
174        prettier_dir: Option<&Path>,
175        worktree_id: Option<WorktreeId>,
176        cx: &mut ModelContext<'_, Project>,
177    ) -> Option<Task<anyhow::Result<PrettierTask>>> {
178        if self.attempt > prettier::FAIL_THRESHOLD {
179            match prettier_dir {
180                Some(prettier_dir) => log::warn!(
181                    "Prettier from path {prettier_dir:?} exceeded launch threshold, not starting"
182                ),
183                None => log::warn!("Default prettier exceeded launch threshold, not starting"),
184            }
185            return None;
186        }
187        Some(match &self.prettier {
188            Some(prettier_task) => Task::ready(Ok(prettier_task.clone())),
189            None => match prettier_dir {
190                Some(prettier_dir) => {
191                    let new_task = start_prettier(
192                        Arc::clone(node),
193                        prettier_dir.to_path_buf(),
194                        worktree_id,
195                        cx,
196                    );
197                    self.attempt += 1;
198                    self.prettier = Some(new_task.clone());
199                    Task::ready(Ok(new_task))
200                }
201                None => {
202                    self.attempt += 1;
203                    let node = Arc::clone(node);
204                    cx.spawn(|project, mut cx| async move {
205                        project
206                            .update(&mut cx, |_, cx| {
207                                start_default_prettier(node, worktree_id, cx)
208                            })?
209                            .await
210                    })
211                }
212            },
213        })
214    }
215
216    pub async fn server(&self) -> Option<Arc<LanguageServer>> {
217        self.prettier.clone()?.await.ok()?.server().cloned()
218    }
219}
220
221fn start_default_prettier(
222    node: Arc<dyn NodeRuntime>,
223    worktree_id: Option<WorktreeId>,
224    cx: &mut ModelContext<'_, Project>,
225) -> Task<anyhow::Result<PrettierTask>> {
226    cx.spawn(|project, mut cx| async move {
227        loop {
228            let installation_task = project.update(&mut cx, |project, _| {
229                match &project.default_prettier.prettier {
230                    PrettierInstallation::NotInstalled {
231                        installation_task, ..
232                    } => ControlFlow::Continue(installation_task.clone()),
233                    PrettierInstallation::Installed(default_prettier) => {
234                        ControlFlow::Break(default_prettier.clone())
235                    }
236                }
237            })?;
238            match installation_task {
239                ControlFlow::Continue(None) => {
240                    anyhow::bail!("Default prettier is not installed and cannot be started")
241                }
242                ControlFlow::Continue(Some(installation_task)) => {
243                    log::info!("Waiting for default prettier to install");
244                    if let Err(e) = installation_task.await {
245                        project.update(&mut cx, |project, _| {
246                            if let PrettierInstallation::NotInstalled {
247                                installation_task,
248                                attempts,
249                                ..
250                            } = &mut project.default_prettier.prettier
251                            {
252                                *installation_task = None;
253                                *attempts += 1;
254                            }
255                        })?;
256                        anyhow::bail!(
257                            "Cannot start default prettier due to its installation failure: {e:#}"
258                        );
259                    }
260                    let new_default_prettier = project.update(&mut cx, |project, cx| {
261                        let new_default_prettier =
262                            start_prettier(node, DEFAULT_PRETTIER_DIR.clone(), worktree_id, cx);
263                        project.default_prettier.prettier =
264                            PrettierInstallation::Installed(PrettierInstance {
265                                attempt: 0,
266                                prettier: Some(new_default_prettier.clone()),
267                            });
268                        new_default_prettier
269                    })?;
270                    return Ok(new_default_prettier);
271                }
272                ControlFlow::Break(instance) => match instance.prettier {
273                    Some(instance) => return Ok(instance),
274                    None => {
275                        let new_default_prettier = project.update(&mut cx, |project, cx| {
276                            let new_default_prettier =
277                                start_prettier(node, DEFAULT_PRETTIER_DIR.clone(), worktree_id, cx);
278                            project.default_prettier.prettier =
279                                PrettierInstallation::Installed(PrettierInstance {
280                                    attempt: instance.attempt + 1,
281                                    prettier: Some(new_default_prettier.clone()),
282                                });
283                            new_default_prettier
284                        })?;
285                        return Ok(new_default_prettier);
286                    }
287                },
288            }
289        }
290    })
291}
292
293fn start_prettier(
294    node: Arc<dyn NodeRuntime>,
295    prettier_dir: PathBuf,
296    worktree_id: Option<WorktreeId>,
297    cx: &mut ModelContext<'_, Project>,
298) -> PrettierTask {
299    cx.spawn(|project, mut cx| async move {
300        log::info!("Starting prettier at path {prettier_dir:?}");
301        let new_server_id = project.update(&mut cx, |project, _| {
302            project.languages.next_language_server_id()
303        })?;
304
305        let new_prettier = Prettier::start(new_server_id, prettier_dir, node, cx.clone())
306            .await
307            .context("default prettier spawn")
308            .map(Arc::new)
309            .map_err(Arc::new)?;
310        register_new_prettier(&project, &new_prettier, worktree_id, new_server_id, &mut cx);
311        Ok(new_prettier)
312    })
313    .shared()
314}
315
316fn register_new_prettier(
317    project: &WeakModel<Project>,
318    prettier: &Prettier,
319    worktree_id: Option<WorktreeId>,
320    new_server_id: LanguageServerId,
321    cx: &mut AsyncAppContext,
322) {
323    let prettier_dir = prettier.prettier_dir();
324    let is_default = prettier.is_default();
325    if is_default {
326        log::info!("Started default prettier in {prettier_dir:?}");
327    } else {
328        log::info!("Started prettier in {prettier_dir:?}");
329    }
330    if let Some(prettier_server) = prettier.server() {
331        project
332            .update(cx, |project, cx| {
333                let name = if is_default {
334                    LanguageServerName(Arc::from("prettier (default)"))
335                } else {
336                    let worktree_path = worktree_id
337                        .and_then(|id| project.worktree_for_id(id, cx))
338                        .map(|worktree| worktree.update(cx, |worktree, _| worktree.abs_path()));
339                    let name = match worktree_path {
340                        Some(worktree_path) => {
341                            if prettier_dir == worktree_path.as_ref() {
342                                let name = prettier_dir
343                                    .file_name()
344                                    .and_then(|name| name.to_str())
345                                    .unwrap_or_default();
346                                format!("prettier ({name})")
347                            } else {
348                                let dir_to_display = prettier_dir
349                                    .strip_prefix(worktree_path.as_ref())
350                                    .ok()
351                                    .unwrap_or(prettier_dir);
352                                format!("prettier ({})", dir_to_display.display())
353                            }
354                        }
355                        None => format!("prettier ({})", prettier_dir.display()),
356                    };
357                    LanguageServerName(Arc::from(name))
358                };
359                project
360                    .supplementary_language_servers
361                    .insert(new_server_id, (name, Arc::clone(prettier_server)));
362                cx.emit(Event::LanguageServerAdded(new_server_id));
363            })
364            .ok();
365    }
366}
367
368async fn install_prettier_packages(
369    plugins_to_install: HashSet<&'static str>,
370    node: Arc<dyn NodeRuntime>,
371) -> anyhow::Result<()> {
372    let packages_to_versions =
373        future::try_join_all(plugins_to_install.iter().chain(Some(&"prettier")).map(
374            |package_name| async {
375                let returned_package_name = package_name.to_string();
376                let latest_version = node
377                    .npm_package_latest_version(package_name)
378                    .await
379                    .with_context(|| {
380                        format!("fetching latest npm version for package {returned_package_name}")
381                    })?;
382                anyhow::Ok((returned_package_name, latest_version))
383            },
384        ))
385        .await
386        .context("fetching latest npm versions")?;
387
388    log::info!("Fetching default prettier and plugins: {packages_to_versions:?}");
389    let borrowed_packages = packages_to_versions
390        .iter()
391        .map(|(package, version)| (package.as_str(), version.as_str()))
392        .collect::<Vec<_>>();
393    node.npm_install_packages(DEFAULT_PRETTIER_DIR.as_path(), &borrowed_packages)
394        .await
395        .context("fetching formatter packages")?;
396    anyhow::Ok(())
397}
398
399async fn save_prettier_server_file(fs: &dyn Fs) -> Result<(), anyhow::Error> {
400    let prettier_wrapper_path = DEFAULT_PRETTIER_DIR.join(prettier::PRETTIER_SERVER_FILE);
401    fs.save(
402        &prettier_wrapper_path,
403        &text::Rope::from(prettier::PRETTIER_SERVER_JS),
404        text::LineEnding::Unix,
405    )
406    .await
407    .with_context(|| {
408        format!(
409            "writing {} file at {prettier_wrapper_path:?}",
410            prettier::PRETTIER_SERVER_FILE
411        )
412    })?;
413    Ok(())
414}
415
416impl Project {
417    pub fn update_prettier_settings(
418        &self,
419        worktree: &Model<Worktree>,
420        changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
421        cx: &mut ModelContext<'_, Project>,
422    ) {
423        let prettier_config_files = Prettier::CONFIG_FILE_NAMES
424            .iter()
425            .map(Path::new)
426            .collect::<HashSet<_>>();
427
428        let prettier_config_file_changed = changes
429            .iter()
430            .filter(|(_, _, change)| !matches!(change, PathChange::Loaded))
431            .filter(|(path, _, _)| {
432                !path
433                    .components()
434                    .any(|component| component.as_os_str().to_string_lossy() == "node_modules")
435            })
436            .find(|(path, _, _)| prettier_config_files.contains(path.as_ref()));
437        let current_worktree_id = worktree.read(cx).id();
438        if let Some((config_path, _, _)) = prettier_config_file_changed {
439            log::info!(
440                "Prettier config file {config_path:?} changed, reloading prettier instances for worktree {current_worktree_id}"
441            );
442            let prettiers_to_reload =
443                self.prettiers_per_worktree
444                    .get(&current_worktree_id)
445                    .iter()
446                    .flat_map(|prettier_paths| prettier_paths.iter())
447                    .flatten()
448                    .filter_map(|prettier_path| {
449                        Some((
450                            current_worktree_id,
451                            Some(prettier_path.clone()),
452                            self.prettier_instances.get(prettier_path)?.clone(),
453                        ))
454                    })
455                    .chain(self.default_prettier.instance().map(|default_prettier| {
456                        (current_worktree_id, None, default_prettier.clone())
457                    }))
458                    .collect::<Vec<_>>();
459
460            cx.background_executor()
461                .spawn(async move {
462                    let _: Vec<()> = future::join_all(prettiers_to_reload.into_iter().map(|(worktree_id, prettier_path, prettier_instance)| {
463                        async move {
464                            if let Some(instance) = prettier_instance.prettier {
465                                match instance.await {
466                                    Ok(prettier) => {
467                                        prettier.clear_cache().log_err().await;
468                                    },
469                                    Err(e) => {
470                                        match prettier_path {
471                                            Some(prettier_path) => log::error!(
472                                                "Failed to clear prettier {prettier_path:?} cache for worktree {worktree_id:?} on prettier settings update: {e:#}"
473                                            ),
474                                            None => log::error!(
475                                                "Failed to clear default prettier cache for worktree {worktree_id:?} on prettier settings update: {e:#}"
476                                            ),
477                                        }
478                                    },
479                                }
480                            }
481                        }
482                    }))
483                    .await;
484                })
485                .detach();
486        }
487    }
488
489    fn prettier_instance_for_buffer(
490        &mut self,
491        buffer: &Model<Buffer>,
492        cx: &mut ModelContext<Self>,
493    ) -> Task<Option<(Option<PathBuf>, PrettierTask)>> {
494        let buffer = buffer.read(cx);
495        let buffer_file = buffer.file();
496        let Some(buffer_language) = buffer.language() else {
497            return Task::ready(None);
498        };
499        if buffer_language.prettier_parser_name().is_none() {
500            return Task::ready(None);
501        }
502
503        if self.is_local() {
504            let Some(node) = self.node.as_ref().map(Arc::clone) else {
505                return Task::ready(None);
506            };
507            match File::from_dyn(buffer_file).map(|file| (file.worktree_id(cx), file.abs_path(cx)))
508            {
509                Some((worktree_id, buffer_path)) => {
510                    let fs = Arc::clone(&self.fs);
511                    let installed_prettiers = self.prettier_instances.keys().cloned().collect();
512                    return cx.spawn(|project, mut cx| async move {
513                        match cx
514                            .background_executor()
515                            .spawn(async move {
516                                Prettier::locate_prettier_installation(
517                                    fs.as_ref(),
518                                    &installed_prettiers,
519                                    &buffer_path,
520                                )
521                                .await
522                            })
523                            .await
524                        {
525                            Ok(ControlFlow::Break(())) => {
526                                return None;
527                            }
528                            Ok(ControlFlow::Continue(None)) => {
529                                let default_instance = project
530                                    .update(&mut cx, |project, cx| {
531                                        project
532                                            .prettiers_per_worktree
533                                            .entry(worktree_id)
534                                            .or_default()
535                                            .insert(None);
536                                        project.default_prettier.prettier_task(
537                                            &node,
538                                            Some(worktree_id),
539                                            cx,
540                                        )
541                                    })
542                                    .ok()?;
543                                Some((None, default_instance?.log_err().await?))
544                            }
545                            Ok(ControlFlow::Continue(Some(prettier_dir))) => {
546                                project
547                                    .update(&mut cx, |project, _| {
548                                        project
549                                            .prettiers_per_worktree
550                                            .entry(worktree_id)
551                                            .or_default()
552                                            .insert(Some(prettier_dir.clone()))
553                                    })
554                                    .ok()?;
555                                if let Some(prettier_task) = project
556                                    .update(&mut cx, |project, cx| {
557                                        project.prettier_instances.get_mut(&prettier_dir).map(
558                                            |existing_instance| {
559                                                existing_instance.prettier_task(
560                                                    &node,
561                                                    Some(&prettier_dir),
562                                                    Some(worktree_id),
563                                                    cx,
564                                                )
565                                            },
566                                        )
567                                    })
568                                    .ok()?
569                                {
570                                    log::debug!(
571                                        "Found already started prettier in {prettier_dir:?}"
572                                    );
573                                    return Some((
574                                        Some(prettier_dir),
575                                        prettier_task?.await.log_err()?,
576                                    ));
577                                }
578
579                                log::info!("Found prettier in {prettier_dir:?}, starting.");
580                                let new_prettier_task = project
581                                    .update(&mut cx, |project, cx| {
582                                        let new_prettier_task = start_prettier(
583                                            node,
584                                            prettier_dir.clone(),
585                                            Some(worktree_id),
586                                            cx,
587                                        );
588                                        project.prettier_instances.insert(
589                                            prettier_dir.clone(),
590                                            PrettierInstance {
591                                                attempt: 0,
592                                                prettier: Some(new_prettier_task.clone()),
593                                            },
594                                        );
595                                        new_prettier_task
596                                    })
597                                    .ok()?;
598                                Some((Some(prettier_dir), new_prettier_task))
599                            }
600                            Err(e) => {
601                                log::error!("Failed to determine prettier path for buffer: {e:#}");
602                                return None;
603                            }
604                        }
605                    });
606                }
607                None => {
608                    let new_task = self.default_prettier.prettier_task(&node, None, cx);
609                    return cx
610                        .spawn(|_, _| async move { Some((None, new_task?.log_err().await?)) });
611                }
612            }
613        } else {
614            return Task::ready(None);
615        }
616    }
617
618    #[cfg(any(test, feature = "test-support"))]
619    pub fn install_default_prettier(
620        &mut self,
621        _worktree: Option<WorktreeId>,
622        plugins: HashSet<&'static str>,
623        _cx: &mut ModelContext<Self>,
624    ) {
625        // suppress unused code warnings
626        let _ = install_prettier_packages;
627        let _ = save_prettier_server_file;
628
629        self.default_prettier.installed_plugins.extend(plugins);
630        self.default_prettier.prettier = PrettierInstallation::Installed(PrettierInstance {
631            attempt: 0,
632            prettier: None,
633        });
634    }
635
636    #[cfg(not(any(test, feature = "test-support")))]
637    pub fn install_default_prettier(
638        &mut self,
639        worktree: Option<WorktreeId>,
640        mut new_plugins: HashSet<&'static str>,
641        cx: &mut ModelContext<Self>,
642    ) {
643        let Some(node) = self.node.as_ref().cloned() else {
644            return;
645        };
646        log::info!("Initializing default prettier with plugins {new_plugins:?}");
647        let fs = Arc::clone(&self.fs);
648        let locate_prettier_installation = match worktree.and_then(|worktree_id| {
649            self.worktree_for_id(worktree_id, cx)
650                .map(|worktree| worktree.read(cx).abs_path())
651        }) {
652            Some(locate_from) => {
653                let installed_prettiers = self.prettier_instances.keys().cloned().collect();
654                cx.background_executor().spawn(async move {
655                    Prettier::locate_prettier_installation(
656                        fs.as_ref(),
657                        &installed_prettiers,
658                        locate_from.as_ref(),
659                    )
660                    .await
661                })
662            }
663            None => Task::ready(Ok(ControlFlow::Continue(None))),
664        };
665        new_plugins.retain(|plugin| !self.default_prettier.installed_plugins.contains(plugin));
666        let mut installation_attempt = 0;
667        let previous_installation_task = match &mut self.default_prettier.prettier {
668            PrettierInstallation::NotInstalled {
669                installation_task,
670                attempts,
671                not_installed_plugins,
672            } => {
673                installation_attempt = *attempts;
674                if installation_attempt > prettier::FAIL_THRESHOLD {
675                    *installation_task = None;
676                    log::warn!(
677                        "Default prettier installation had failed {installation_attempt} times, not attempting again",
678                    );
679                    return;
680                }
681                new_plugins.extend(not_installed_plugins.iter());
682                installation_task.clone()
683            }
684            PrettierInstallation::Installed { .. } => {
685                if new_plugins.is_empty() {
686                    return;
687                }
688                None
689            }
690        };
691
692        let plugins_to_install = new_plugins.clone();
693        let fs = Arc::clone(&self.fs);
694        let new_installation_task = cx
695            .spawn(|project, mut cx| async move {
696                match locate_prettier_installation
697                    .await
698                    .context("locate prettier installation")
699                    .map_err(Arc::new)?
700                {
701                    ControlFlow::Break(()) => return Ok(()),
702                    ControlFlow::Continue(prettier_path) => {
703                        if prettier_path.is_some() {
704                            new_plugins.clear();
705                        }
706                        let mut needs_install = false;
707                        if let Some(previous_installation_task) = previous_installation_task {
708                            if let Err(e) = previous_installation_task.await {
709                                log::error!("Failed to install default prettier: {e:#}");
710                                project.update(&mut cx, |project, _| {
711                                    if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut project.default_prettier.prettier {
712                                        *attempts += 1;
713                                        new_plugins.extend(not_installed_plugins.iter());
714                                        installation_attempt = *attempts;
715                                        needs_install = true;
716                                    };
717                                })?;
718                            }
719                        };
720                        if installation_attempt > prettier::FAIL_THRESHOLD {
721                            project.update(&mut cx, |project, _| {
722                                if let PrettierInstallation::NotInstalled { installation_task, .. } = &mut project.default_prettier.prettier {
723                                    *installation_task = None;
724                                };
725                            })?;
726                            log::warn!(
727                                "Default prettier installation had failed {installation_attempt} times, not attempting again",
728                            );
729                            return Ok(());
730                        }
731                        project.update(&mut cx, |project, _| {
732                            new_plugins.retain(|plugin| {
733                                !project.default_prettier.installed_plugins.contains(plugin)
734                            });
735                            if let PrettierInstallation::NotInstalled { not_installed_plugins, .. } = &mut project.default_prettier.prettier {
736                                not_installed_plugins.retain(|plugin| {
737                                    !project.default_prettier.installed_plugins.contains(plugin)
738                                });
739                                not_installed_plugins.extend(new_plugins.iter());
740                            }
741                            needs_install |= !new_plugins.is_empty();
742                        })?;
743                        if needs_install {
744                            let installed_plugins = new_plugins.clone();
745                            cx.background_executor()
746                                .spawn(async move {
747                                    save_prettier_server_file(fs.as_ref()).await?;
748                                    install_prettier_packages(new_plugins, node).await
749                                })
750                                .await
751                                .context("prettier & plugins install")
752                                .map_err(Arc::new)?;
753                            log::info!("Initialized prettier with plugins: {installed_plugins:?}");
754                            project.update(&mut cx, |project, _| {
755                                project.default_prettier.prettier =
756                                    PrettierInstallation::Installed(PrettierInstance {
757                                        attempt: 0,
758                                        prettier: None,
759                                    });
760                                project.default_prettier
761                                    .installed_plugins
762                                    .extend(installed_plugins);
763                            })?;
764                        }
765                    }
766                }
767                Ok(())
768            })
769            .shared();
770        self.default_prettier.prettier = PrettierInstallation::NotInstalled {
771            attempts: installation_attempt,
772            installation_task: Some(new_installation_task),
773            not_installed_plugins: plugins_to_install,
774        };
775    }
776}