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