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