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