prettier_support.rs

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