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