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