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