prettier_support.rs

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