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        loop {
234            let installation_task = project.update(&mut cx, |project, _| {
235                match &project.default_prettier.prettier {
236                    PrettierInstallation::NotInstalled {
237                        installation_task, ..
238                    } => ControlFlow::Continue(installation_task.clone()),
239                    PrettierInstallation::Installed(default_prettier) => {
240                        ControlFlow::Break(default_prettier.clone())
241                    }
242                }
243            })?;
244            match installation_task {
245                ControlFlow::Continue(None) => {
246                    anyhow::bail!("Default prettier is not installed and cannot be started")
247                }
248                ControlFlow::Continue(Some(installation_task)) => {
249                    log::info!("Waiting for default prettier to install");
250                    if let Err(e) = installation_task.await {
251                        project.update(&mut cx, |project, _| {
252                            if let PrettierInstallation::NotInstalled {
253                                installation_task,
254                                attempts,
255                                ..
256                            } = &mut project.default_prettier.prettier
257                            {
258                                *installation_task = None;
259                                *attempts += 1;
260                            }
261                        })?;
262                        anyhow::bail!(
263                            "Cannot start default prettier due to its installation failure: {e:#}"
264                        );
265                    }
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: 0,
272                                prettier: Some(new_default_prettier.clone()),
273                            });
274                        new_default_prettier
275                    })?;
276                    return Ok(new_default_prettier);
277                }
278                ControlFlow::Break(instance) => match instance.prettier {
279                    Some(instance) => return Ok(instance),
280                    None => {
281                        let new_default_prettier = project.update(&mut cx, |project, cx| {
282                            let new_default_prettier =
283                                start_prettier(node, DEFAULT_PRETTIER_DIR.clone(), worktree_id, cx);
284                            project.default_prettier.prettier =
285                                PrettierInstallation::Installed(PrettierInstance {
286                                    attempt: instance.attempt + 1,
287                                    prettier: Some(new_default_prettier.clone()),
288                                });
289                            new_default_prettier
290                        })?;
291                        return Ok(new_default_prettier);
292                    }
293                },
294            }
295        }
296    })
297}
298
299fn start_prettier(
300    node: Arc<dyn NodeRuntime>,
301    prettier_dir: PathBuf,
302    worktree_id: Option<WorktreeId>,
303    cx: &mut ModelContext<'_, Project>,
304) -> PrettierTask {
305    cx.spawn(|project, mut cx| async move {
306        log::info!("Starting prettier at path {prettier_dir:?}");
307        let language_registry = project.update(&mut cx, |project, _| project.languages.clone())?;
308        let new_server_id = language_registry.next_language_server_id();
309
310        let new_prettier = Prettier::start(
311            new_server_id,
312            prettier_dir,
313            node,
314            language_registry,
315            cx.clone(),
316        )
317        .await
318        .context("default prettier spawn")
319        .map(Arc::new)
320        .map_err(Arc::new)?;
321        register_new_prettier(&project, &new_prettier, worktree_id, new_server_id, &mut cx);
322        Ok(new_prettier)
323    })
324    .shared()
325}
326
327fn register_new_prettier(
328    project: &WeakModel<Project>,
329    prettier: &Prettier,
330    worktree_id: Option<WorktreeId>,
331    new_server_id: LanguageServerId,
332    cx: &mut AsyncAppContext,
333) {
334    let prettier_dir = prettier.prettier_dir();
335    let is_default = prettier.is_default();
336    if is_default {
337        log::info!("Started default prettier in {prettier_dir:?}");
338    } else {
339        log::info!("Started prettier in {prettier_dir:?}");
340    }
341    if let Some(prettier_server) = prettier.server() {
342        project
343            .update(cx, |project, cx| {
344                let name = if is_default {
345                    LanguageServerName(Arc::from("prettier (default)"))
346                } else {
347                    let worktree_path = worktree_id
348                        .and_then(|id| project.worktree_for_id(id, cx))
349                        .map(|worktree| worktree.update(cx, |worktree, _| worktree.abs_path()));
350                    let name = match worktree_path {
351                        Some(worktree_path) => {
352                            if prettier_dir == worktree_path.as_ref() {
353                                let name = prettier_dir
354                                    .file_name()
355                                    .and_then(|name| name.to_str())
356                                    .unwrap_or_default();
357                                format!("prettier ({name})")
358                            } else {
359                                let dir_to_display = prettier_dir
360                                    .strip_prefix(worktree_path.as_ref())
361                                    .ok()
362                                    .unwrap_or(prettier_dir);
363                                format!("prettier ({})", dir_to_display.display())
364                            }
365                        }
366                        None => format!("prettier ({})", prettier_dir.display()),
367                    };
368                    LanguageServerName(Arc::from(name))
369                };
370                project
371                    .supplementary_language_servers
372                    .insert(new_server_id, (name, Arc::clone(prettier_server)));
373                cx.emit(Event::LanguageServerAdded(new_server_id));
374            })
375            .ok();
376    }
377}
378
379async fn install_prettier_packages(
380    fs: &dyn Fs,
381    plugins_to_install: HashSet<&'static str>,
382    node: Arc<dyn NodeRuntime>,
383) -> anyhow::Result<()> {
384    let packages_to_versions =
385        future::try_join_all(plugins_to_install.iter().chain(Some(&"prettier")).map(
386            |package_name| async {
387                let returned_package_name = package_name.to_string();
388                let latest_version = node
389                    .npm_package_latest_version(package_name)
390                    .await
391                    .with_context(|| {
392                        format!("fetching latest npm version for package {returned_package_name}")
393                    })?;
394                anyhow::Ok((returned_package_name, latest_version))
395            },
396        ))
397        .await
398        .context("fetching latest npm versions")?;
399
400    let default_prettier_dir = DEFAULT_PRETTIER_DIR.as_path();
401    match fs.metadata(default_prettier_dir).await.with_context(|| {
402        format!("fetching FS metadata for default prettier dir {default_prettier_dir:?}")
403    })? {
404        Some(prettier_dir_metadata) => anyhow::ensure!(
405            prettier_dir_metadata.is_dir,
406            "default prettier dir {default_prettier_dir:?} is not a directory"
407        ),
408        None => fs
409            .create_dir(default_prettier_dir)
410            .await
411            .with_context(|| format!("creating default prettier dir {default_prettier_dir:?}"))?,
412    }
413
414    log::info!("Installing default prettier and plugins: {packages_to_versions:?}");
415    let borrowed_packages = packages_to_versions
416        .iter()
417        .map(|(package, version)| (package.as_str(), version.as_str()))
418        .collect::<Vec<_>>();
419    node.npm_install_packages(default_prettier_dir, &borrowed_packages)
420        .await
421        .context("fetching formatter packages")?;
422    anyhow::Ok(())
423}
424
425async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {
426    let prettier_wrapper_path = DEFAULT_PRETTIER_DIR.join(prettier::PRETTIER_SERVER_FILE);
427    fs.save(
428        &prettier_wrapper_path,
429        &text::Rope::from(prettier::PRETTIER_SERVER_JS),
430        text::LineEnding::Unix,
431    )
432    .await
433    .with_context(|| {
434        format!(
435            "writing {} file at {prettier_wrapper_path:?}",
436            prettier::PRETTIER_SERVER_FILE
437        )
438    })?;
439    Ok(())
440}
441
442async fn should_write_prettier_server_file(fs: &dyn Fs) -> bool {
443    let prettier_wrapper_path = DEFAULT_PRETTIER_DIR.join(prettier::PRETTIER_SERVER_FILE);
444    if !fs.is_file(&prettier_wrapper_path).await {
445        return true;
446    }
447    let Ok(prettier_server_file_contents) = fs.load(&prettier_wrapper_path).await else {
448        return true;
449    };
450    prettier_server_file_contents != prettier::PRETTIER_SERVER_JS
451}
452
453impl Project {
454    pub fn update_prettier_settings(
455        &self,
456        worktree: &Model<Worktree>,
457        changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
458        cx: &mut ModelContext<'_, Project>,
459    ) {
460        let prettier_config_files = Prettier::CONFIG_FILE_NAMES
461            .iter()
462            .map(Path::new)
463            .collect::<HashSet<_>>();
464
465        let prettier_config_file_changed = changes
466            .iter()
467            .filter(|(_, _, change)| !matches!(change, PathChange::Loaded))
468            .filter(|(path, _, _)| {
469                !path
470                    .components()
471                    .any(|component| component.as_os_str().to_string_lossy() == "node_modules")
472            })
473            .find(|(path, _, _)| prettier_config_files.contains(path.as_ref()));
474        let current_worktree_id = worktree.read(cx).id();
475        if let Some((config_path, _, _)) = prettier_config_file_changed {
476            log::info!(
477                "Prettier config file {config_path:?} changed, reloading prettier instances for worktree {current_worktree_id}"
478            );
479            let prettiers_to_reload =
480                self.prettiers_per_worktree
481                    .get(&current_worktree_id)
482                    .iter()
483                    .flat_map(|prettier_paths| prettier_paths.iter())
484                    .flatten()
485                    .filter_map(|prettier_path| {
486                        Some((
487                            current_worktree_id,
488                            Some(prettier_path.clone()),
489                            self.prettier_instances.get(prettier_path)?.clone(),
490                        ))
491                    })
492                    .chain(self.default_prettier.instance().map(|default_prettier| {
493                        (current_worktree_id, None, default_prettier.clone())
494                    }))
495                    .collect::<Vec<_>>();
496
497            cx.background_executor()
498                .spawn(async move {
499                    let _: Vec<()> = future::join_all(prettiers_to_reload.into_iter().map(|(worktree_id, prettier_path, prettier_instance)| {
500                        async move {
501                            if let Some(instance) = prettier_instance.prettier {
502                                match instance.await {
503                                    Ok(prettier) => {
504                                        prettier.clear_cache().log_err().await;
505                                    },
506                                    Err(e) => {
507                                        match prettier_path {
508                                            Some(prettier_path) => log::error!(
509                                                "Failed to clear prettier {prettier_path:?} cache for worktree {worktree_id:?} on prettier settings update: {e:#}"
510                                            ),
511                                            None => log::error!(
512                                                "Failed to clear default prettier cache for worktree {worktree_id:?} on prettier settings update: {e:#}"
513                                            ),
514                                        }
515                                    },
516                                }
517                            }
518                        }
519                    }))
520                    .await;
521                })
522                .detach();
523        }
524    }
525
526    fn prettier_instance_for_buffer(
527        &mut self,
528        buffer: &Model<Buffer>,
529        cx: &mut ModelContext<Self>,
530    ) -> Task<Option<(Option<PathBuf>, PrettierTask)>> {
531        if !self.is_local() {
532            return Task::ready(None);
533        }
534        let buffer = buffer.read(cx);
535        let buffer_file = buffer.file();
536        let Some(buffer_language) = buffer.language() else {
537            return Task::ready(None);
538        };
539        if buffer_language.prettier_parser_name().is_none() {
540            return Task::ready(None);
541        }
542        let Some(node) = self.node.as_ref().map(Arc::clone) else {
543            return Task::ready(None);
544        };
545        match File::from_dyn(buffer_file).map(|file| (file.worktree_id(cx), file.abs_path(cx))) {
546            Some((worktree_id, buffer_path)) => {
547                let fs = Arc::clone(&self.fs);
548                let installed_prettiers = self.prettier_instances.keys().cloned().collect();
549                cx.spawn(|project, mut cx| async move {
550                    match cx
551                        .background_executor()
552                        .spawn(async move {
553                            Prettier::locate_prettier_installation(
554                                fs.as_ref(),
555                                &installed_prettiers,
556                                &buffer_path,
557                            )
558                            .await
559                        })
560                        .await
561                    {
562                        Ok(ControlFlow::Break(())) => None,
563                        Ok(ControlFlow::Continue(None)) => {
564                            let default_instance = project
565                                .update(&mut cx, |project, cx| {
566                                    project
567                                        .prettiers_per_worktree
568                                        .entry(worktree_id)
569                                        .or_default()
570                                        .insert(None);
571                                    project.default_prettier.prettier_task(
572                                        &node,
573                                        Some(worktree_id),
574                                        cx,
575                                    )
576                                })
577                                .ok()?;
578                            Some((None, default_instance?.log_err().await?))
579                        }
580                        Ok(ControlFlow::Continue(Some(prettier_dir))) => {
581                            project
582                                .update(&mut cx, |project, _| {
583                                    project
584                                        .prettiers_per_worktree
585                                        .entry(worktree_id)
586                                        .or_default()
587                                        .insert(Some(prettier_dir.clone()))
588                                })
589                                .ok()?;
590                            if let Some(prettier_task) = project
591                                .update(&mut cx, |project, cx| {
592                                    project.prettier_instances.get_mut(&prettier_dir).map(
593                                        |existing_instance| {
594                                            existing_instance.prettier_task(
595                                                &node,
596                                                Some(&prettier_dir),
597                                                Some(worktree_id),
598                                                cx,
599                                            )
600                                        },
601                                    )
602                                })
603                                .ok()?
604                            {
605                                log::debug!("Found already started prettier in {prettier_dir:?}");
606                                return Some((Some(prettier_dir), prettier_task?.await.log_err()?));
607                            }
608
609                            log::info!("Found prettier in {prettier_dir:?}, starting.");
610                            let new_prettier_task = project
611                                .update(&mut cx, |project, cx| {
612                                    let new_prettier_task = start_prettier(
613                                        node,
614                                        prettier_dir.clone(),
615                                        Some(worktree_id),
616                                        cx,
617                                    );
618                                    project.prettier_instances.insert(
619                                        prettier_dir.clone(),
620                                        PrettierInstance {
621                                            attempt: 0,
622                                            prettier: Some(new_prettier_task.clone()),
623                                        },
624                                    );
625                                    new_prettier_task
626                                })
627                                .ok()?;
628                            Some((Some(prettier_dir), new_prettier_task))
629                        }
630                        Err(e) => {
631                            log::error!("Failed to determine prettier path for buffer: {e:#}");
632                            None
633                        }
634                    }
635                })
636            }
637            None => {
638                let new_task = self.default_prettier.prettier_task(&node, None, cx);
639                cx.spawn(|_, _| async move { Some((None, new_task?.log_err().await?)) })
640            }
641        }
642    }
643
644    #[cfg(any(test, feature = "test-support"))]
645    pub fn install_default_prettier(
646        &mut self,
647        _worktree: Option<WorktreeId>,
648        plugins: HashSet<&'static str>,
649        _cx: &mut ModelContext<Self>,
650    ) {
651        // suppress unused code warnings
652        let _ = should_write_prettier_server_file;
653        let _ = install_prettier_packages;
654        let _ = save_prettier_server_file;
655
656        self.default_prettier.installed_plugins.extend(plugins);
657        self.default_prettier.prettier = PrettierInstallation::Installed(PrettierInstance {
658            attempt: 0,
659            prettier: None,
660        });
661    }
662
663    #[cfg(not(any(test, feature = "test-support")))]
664    pub fn install_default_prettier(
665        &mut self,
666        worktree: Option<WorktreeId>,
667        mut new_plugins: HashSet<&'static str>,
668        cx: &mut ModelContext<Self>,
669    ) {
670        let Some(node) = self.node.as_ref().cloned() else {
671            return;
672        };
673        let fs = Arc::clone(&self.fs);
674        let locate_prettier_installation = match worktree.and_then(|worktree_id| {
675            self.worktree_for_id(worktree_id, cx)
676                .map(|worktree| worktree.read(cx).abs_path())
677        }) {
678            Some(locate_from) => {
679                let installed_prettiers = self.prettier_instances.keys().cloned().collect();
680                cx.background_executor().spawn(async move {
681                    Prettier::locate_prettier_installation(
682                        fs.as_ref(),
683                        &installed_prettiers,
684                        locate_from.as_ref(),
685                    )
686                    .await
687                })
688            }
689            None => Task::ready(Ok(ControlFlow::Continue(None))),
690        };
691        new_plugins.retain(|plugin| !self.default_prettier.installed_plugins.contains(plugin));
692        let mut installation_attempt = 0;
693        let previous_installation_task = match &mut self.default_prettier.prettier {
694            PrettierInstallation::NotInstalled {
695                installation_task,
696                attempts,
697                not_installed_plugins,
698            } => {
699                installation_attempt = *attempts;
700                if installation_attempt > prettier::FAIL_THRESHOLD {
701                    *installation_task = None;
702                    log::warn!(
703                        "Default prettier installation had failed {installation_attempt} times, not attempting again",
704                    );
705                    return;
706                }
707                new_plugins.extend(not_installed_plugins.iter());
708                installation_task.clone()
709            }
710            PrettierInstallation::Installed { .. } => {
711                if new_plugins.is_empty() {
712                    return;
713                }
714                None
715            }
716        };
717
718        log::info!("Initializing default prettier with plugins {new_plugins:?}");
719        let plugins_to_install = new_plugins.clone();
720        let fs = Arc::clone(&self.fs);
721        let new_installation_task = cx
722            .spawn(|project, mut cx| async move {
723                match locate_prettier_installation
724                    .await
725                    .context("locate prettier installation")
726                    .map_err(Arc::new)?
727                {
728                    ControlFlow::Break(()) => return Ok(()),
729                    ControlFlow::Continue(prettier_path) => {
730                        if prettier_path.is_some() {
731                            new_plugins.clear();
732                        }
733                        let mut needs_install = should_write_prettier_server_file(fs.as_ref()).await;
734                        if let Some(previous_installation_task) = previous_installation_task {
735                            if let Err(e) = previous_installation_task.await {
736                                log::error!("Failed to install default prettier: {e:#}");
737                                project.update(&mut cx, |project, _| {
738                                    if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut project.default_prettier.prettier {
739                                        *attempts += 1;
740                                        new_plugins.extend(not_installed_plugins.iter());
741                                        installation_attempt = *attempts;
742                                        needs_install = true;
743                                    };
744                                })?;
745                            }
746                        };
747                        if installation_attempt > prettier::FAIL_THRESHOLD {
748                            project.update(&mut cx, |project, _| {
749                                if let PrettierInstallation::NotInstalled { installation_task, .. } = &mut project.default_prettier.prettier {
750                                    *installation_task = None;
751                                };
752                            })?;
753                            log::warn!(
754                                "Default prettier installation had failed {installation_attempt} times, not attempting again",
755                            );
756                            return Ok(());
757                        }
758                        project.update(&mut cx, |project, _| {
759                            new_plugins.retain(|plugin| {
760                                !project.default_prettier.installed_plugins.contains(plugin)
761                            });
762                            if let PrettierInstallation::NotInstalled { not_installed_plugins, .. } = &mut project.default_prettier.prettier {
763                                not_installed_plugins.retain(|plugin| {
764                                    !project.default_prettier.installed_plugins.contains(plugin)
765                                });
766                                not_installed_plugins.extend(new_plugins.iter());
767                            }
768                            needs_install |= !new_plugins.is_empty();
769                        })?;
770                        if needs_install {
771                            let installed_plugins = new_plugins.clone();
772                            cx.background_executor()
773                                .spawn(async move {
774                                    install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
775                                    // Save the server file last, so the reinstall need could be determined by the absence of the file.
776                                    save_prettier_server_file(fs.as_ref()).await?;
777                                    anyhow::Ok(())
778                                })
779                                .await
780                                .context("prettier & plugins install")
781                                .map_err(Arc::new)?;
782                            log::info!("Initialized prettier with plugins: {installed_plugins:?}");
783                            project.update(&mut cx, |project, _| {
784                                project.default_prettier.prettier =
785                                    PrettierInstallation::Installed(PrettierInstance {
786                                        attempt: 0,
787                                        prettier: None,
788                                    });
789                                project.default_prettier
790                                    .installed_plugins
791                                    .extend(installed_plugins);
792                            })?;
793                        }
794                    }
795                }
796                Ok(())
797            })
798            .shared();
799        self.default_prettier.prettier = PrettierInstallation::NotInstalled {
800            attempts: installation_attempt,
801            installation_task: Some(new_installation_task),
802            not_installed_plugins: plugins_to_install,
803        };
804    }
805}