prettier_support.rs

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