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