1use std::{
   2    ops::ControlFlow,
   3    path::{Path, PathBuf},
   4    sync::Arc,
   5    time::Duration,
   6};
   7
   8use anyhow::{Context as _, Result, anyhow};
   9use collections::{HashMap, HashSet};
  10use fs::Fs;
  11use futures::{
  12    FutureExt,
  13    future::{self, Shared},
  14    stream::FuturesUnordered,
  15};
  16use gpui::{
  17    AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,
  18};
  19use language::{
  20    Buffer, LanguageRegistry, LocalFile,
  21    language_settings::{Formatter, LanguageSettings},
  22};
  23use lsp::{LanguageServer, LanguageServerId, LanguageServerName};
  24use node_runtime::NodeRuntime;
  25use paths::default_prettier_dir;
  26use prettier::Prettier;
  27use smol::stream::StreamExt;
  28use util::{ResultExt, TryFutureExt, rel_path::RelPath};
  29
  30use crate::{
  31    File, PathChange, ProjectEntryId, Worktree, lsp_store::WorktreeId,
  32    worktree_store::WorktreeStore,
  33};
  34
  35pub struct PrettierStore {
  36    node: NodeRuntime,
  37    fs: Arc<dyn Fs>,
  38    languages: Arc<LanguageRegistry>,
  39    worktree_store: Entity<WorktreeStore>,
  40    default_prettier: DefaultPrettier,
  41    prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
  42    prettier_ignores_per_worktree: HashMap<WorktreeId, HashSet<PathBuf>>,
  43    prettier_instances: HashMap<PathBuf, PrettierInstance>,
  44}
  45
  46pub(crate) enum PrettierStoreEvent {
  47    LanguageServerRemoved(LanguageServerId),
  48    LanguageServerAdded {
  49        new_server_id: LanguageServerId,
  50        name: LanguageServerName,
  51        prettier_server: Arc<LanguageServer>,
  52    },
  53}
  54
  55impl EventEmitter<PrettierStoreEvent> for PrettierStore {}
  56
  57impl PrettierStore {
  58    pub fn new(
  59        node: NodeRuntime,
  60        fs: Arc<dyn Fs>,
  61        languages: Arc<LanguageRegistry>,
  62        worktree_store: Entity<WorktreeStore>,
  63        _: &mut Context<Self>,
  64    ) -> Self {
  65        Self {
  66            node,
  67            fs,
  68            languages,
  69            worktree_store,
  70            default_prettier: DefaultPrettier::default(),
  71            prettiers_per_worktree: HashMap::default(),
  72            prettier_ignores_per_worktree: HashMap::default(),
  73            prettier_instances: HashMap::default(),
  74        }
  75    }
  76
  77    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
  78        self.prettier_ignores_per_worktree.remove(&id_to_remove);
  79        let mut prettier_instances_to_clean = FuturesUnordered::new();
  80        if let Some(prettier_paths) = self.prettiers_per_worktree.remove(&id_to_remove) {
  81            for path in prettier_paths.iter().flatten() {
  82                if let Some(prettier_instance) = self.prettier_instances.remove(path) {
  83                    prettier_instances_to_clean.push(async move {
  84                        prettier_instance
  85                            .server()
  86                            .await
  87                            .map(|server| server.server_id())
  88                    });
  89                }
  90            }
  91        }
  92        cx.spawn(async move |prettier_store, cx| {
  93            while let Some(prettier_server_id) = prettier_instances_to_clean.next().await {
  94                if let Some(prettier_server_id) = prettier_server_id {
  95                    prettier_store
  96                        .update(cx, |_, cx| {
  97                            cx.emit(PrettierStoreEvent::LanguageServerRemoved(
  98                                prettier_server_id,
  99                            ));
 100                        })
 101                        .ok();
 102                }
 103            }
 104        })
 105        .detach();
 106    }
 107
 108    fn prettier_instance_for_buffer(
 109        &mut self,
 110        buffer: &Entity<Buffer>,
 111        cx: &mut Context<Self>,
 112    ) -> Task<Option<(Option<PathBuf>, PrettierTask)>> {
 113        let buffer = buffer.read(cx);
 114        let buffer_file = buffer.file();
 115        if buffer.language().is_none() {
 116            return Task::ready(None);
 117        }
 118
 119        let node = self.node.clone();
 120
 121        match File::from_dyn(buffer_file).map(|file| (file.worktree_id(cx), file.abs_path(cx))) {
 122            Some((worktree_id, buffer_path)) => {
 123                let fs = Arc::clone(&self.fs);
 124                let installed_prettiers = self.prettier_instances.keys().cloned().collect();
 125                cx.spawn(async move |lsp_store, cx| {
 126                    match cx
 127                        .background_spawn(async move {
 128                            Prettier::locate_prettier_installation(
 129                                fs.as_ref(),
 130                                &installed_prettiers,
 131                                &buffer_path,
 132                            )
 133                            .await
 134                        })
 135                        .await
 136                    {
 137                        Ok(ControlFlow::Break(())) => None,
 138                        Ok(ControlFlow::Continue(None)) => {
 139                            let default_instance = lsp_store
 140                                .update(cx, |lsp_store, cx| {
 141                                    lsp_store
 142                                        .prettiers_per_worktree
 143                                        .entry(worktree_id)
 144                                        .or_default()
 145                                        .insert(None);
 146                                    lsp_store.default_prettier.prettier_task(
 147                                        &node,
 148                                        Some(worktree_id),
 149                                        cx,
 150                                    )
 151                                })
 152                                .ok()?;
 153                            Some((None, default_instance?.log_err().await?))
 154                        }
 155                        Ok(ControlFlow::Continue(Some(prettier_dir))) => {
 156                            lsp_store
 157                                .update(cx, |lsp_store, _| {
 158                                    lsp_store
 159                                        .prettiers_per_worktree
 160                                        .entry(worktree_id)
 161                                        .or_default()
 162                                        .insert(Some(prettier_dir.clone()))
 163                                })
 164                                .ok()?;
 165                            if let Some(prettier_task) = lsp_store
 166                                .update(cx, |lsp_store, cx| {
 167                                    lsp_store.prettier_instances.get_mut(&prettier_dir).map(
 168                                        |existing_instance| {
 169                                            existing_instance.prettier_task(
 170                                                &node,
 171                                                Some(&prettier_dir),
 172                                                Some(worktree_id),
 173                                                cx,
 174                                            )
 175                                        },
 176                                    )
 177                                })
 178                                .ok()?
 179                            {
 180                                log::debug!("Found already started prettier in {prettier_dir:?}");
 181                                return Some((Some(prettier_dir), prettier_task?.await.log_err()?));
 182                            }
 183
 184                            log::info!("Found prettier in {prettier_dir:?}, starting.");
 185                            let new_prettier_task = lsp_store
 186                                .update(cx, |lsp_store, cx| {
 187                                    let new_prettier_task = Self::start_prettier(
 188                                        node,
 189                                        prettier_dir.clone(),
 190                                        Some(worktree_id),
 191                                        cx,
 192                                    );
 193                                    lsp_store.prettier_instances.insert(
 194                                        prettier_dir.clone(),
 195                                        PrettierInstance {
 196                                            attempt: 0,
 197                                            prettier: Some(new_prettier_task.clone()),
 198                                        },
 199                                    );
 200                                    new_prettier_task
 201                                })
 202                                .ok()?;
 203                            Some((Some(prettier_dir), new_prettier_task))
 204                        }
 205                        Err(e) => {
 206                            log::error!("Failed to determine prettier path for buffer: {e:#}");
 207                            None
 208                        }
 209                    }
 210                })
 211            }
 212            None => {
 213                let new_task = self.default_prettier.prettier_task(&node, None, cx);
 214                cx.spawn(async move |_, _| Some((None, new_task?.log_err().await?)))
 215            }
 216        }
 217    }
 218
 219    fn prettier_ignore_for_buffer(
 220        &mut self,
 221        buffer: &Entity<Buffer>,
 222        cx: &mut Context<Self>,
 223    ) -> Task<Option<PathBuf>> {
 224        let buffer = buffer.read(cx);
 225        let buffer_file = buffer.file();
 226        if buffer.language().is_none() {
 227            return Task::ready(None);
 228        }
 229        match File::from_dyn(buffer_file).map(|file| (file.worktree_id(cx), file.abs_path(cx))) {
 230            Some((worktree_id, buffer_path)) => {
 231                let fs = Arc::clone(&self.fs);
 232                let prettier_ignores = self
 233                    .prettier_ignores_per_worktree
 234                    .get(&worktree_id)
 235                    .cloned()
 236                    .unwrap_or_default();
 237                cx.spawn(async move |lsp_store, cx| {
 238                    match cx
 239                        .background_spawn(async move {
 240                            Prettier::locate_prettier_ignore(
 241                                fs.as_ref(),
 242                                &prettier_ignores,
 243                                &buffer_path,
 244                            )
 245                            .await
 246                        })
 247                        .await
 248                    {
 249                        Ok(ControlFlow::Break(())) => None,
 250                        Ok(ControlFlow::Continue(None)) => None,
 251                        Ok(ControlFlow::Continue(Some(ignore_dir))) => {
 252                            log::debug!("Found prettier ignore in {ignore_dir:?}");
 253                            lsp_store
 254                                .update(cx, |store, _| {
 255                                    store
 256                                        .prettier_ignores_per_worktree
 257                                        .entry(worktree_id)
 258                                        .or_default()
 259                                        .insert(ignore_dir.clone());
 260                                })
 261                                .ok();
 262                            Some(ignore_dir)
 263                        }
 264                        Err(e) => {
 265                            log::error!(
 266                                "Failed to determine prettier ignore path for buffer: {e:#}"
 267                            );
 268                            None
 269                        }
 270                    }
 271                })
 272            }
 273            None => Task::ready(None),
 274        }
 275    }
 276
 277    fn start_prettier(
 278        node: NodeRuntime,
 279        prettier_dir: PathBuf,
 280        worktree_id: Option<WorktreeId>,
 281        cx: &mut Context<Self>,
 282    ) -> PrettierTask {
 283        cx.spawn(async move |prettier_store, cx| {
 284            log::info!("Starting prettier at path {prettier_dir:?}");
 285            let new_server_id = prettier_store.read_with(cx, |prettier_store, _| {
 286                prettier_store.languages.next_language_server_id()
 287            })?;
 288
 289            let new_prettier = Prettier::start(new_server_id, prettier_dir, node, cx.clone())
 290                .await
 291                .context("default prettier spawn")
 292                .map(Arc::new)
 293                .map_err(Arc::new)?;
 294            Self::register_new_prettier(
 295                &prettier_store,
 296                &new_prettier,
 297                worktree_id,
 298                new_server_id,
 299                cx,
 300            );
 301            Ok(new_prettier)
 302        })
 303        .shared()
 304    }
 305
 306    fn start_default_prettier(
 307        node: NodeRuntime,
 308        worktree_id: Option<WorktreeId>,
 309        cx: &mut Context<PrettierStore>,
 310    ) -> Task<anyhow::Result<PrettierTask>> {
 311        cx.spawn(async move |prettier_store, cx| {
 312            let installation_task = prettier_store.read_with(cx, |prettier_store, _| {
 313                match &prettier_store.default_prettier.prettier {
 314                    PrettierInstallation::NotInstalled {
 315                        installation_task, ..
 316                    } => ControlFlow::Continue(installation_task.clone()),
 317                    PrettierInstallation::Installed(default_prettier) => {
 318                        ControlFlow::Break(default_prettier.clone())
 319                    }
 320                }
 321            })?;
 322            match installation_task {
 323                ControlFlow::Continue(None) => {
 324                    anyhow::bail!("Default prettier is not installed and cannot be started")
 325                }
 326                ControlFlow::Continue(Some(installation_task)) => {
 327                    log::info!("Waiting for default prettier to install");
 328                    if let Err(e) = installation_task.await {
 329                        prettier_store.update(cx, |project, _| {
 330                            if let PrettierInstallation::NotInstalled {
 331                                installation_task,
 332                                attempts,
 333                                ..
 334                            } = &mut project.default_prettier.prettier
 335                            {
 336                                *installation_task = None;
 337                                *attempts += 1;
 338                            }
 339                        })?;
 340                        anyhow::bail!(
 341                            "Cannot start default prettier due to its installation failure: {e:#}"
 342                        );
 343                    }
 344                    let new_default_prettier =
 345                        prettier_store.update(cx, |prettier_store, cx| {
 346                            let new_default_prettier = Self::start_prettier(
 347                                node,
 348                                default_prettier_dir().clone(),
 349                                worktree_id,
 350                                cx,
 351                            );
 352                            prettier_store.default_prettier.prettier =
 353                                PrettierInstallation::Installed(PrettierInstance {
 354                                    attempt: 0,
 355                                    prettier: Some(new_default_prettier.clone()),
 356                                });
 357                            new_default_prettier
 358                        })?;
 359                    Ok(new_default_prettier)
 360                }
 361                ControlFlow::Break(instance) => match instance.prettier {
 362                    Some(instance) => Ok(instance),
 363                    None => {
 364                        let new_default_prettier =
 365                            prettier_store.update(cx, |prettier_store, cx| {
 366                                let new_default_prettier = Self::start_prettier(
 367                                    node,
 368                                    default_prettier_dir().clone(),
 369                                    worktree_id,
 370                                    cx,
 371                                );
 372                                prettier_store.default_prettier.prettier =
 373                                    PrettierInstallation::Installed(PrettierInstance {
 374                                        attempt: instance.attempt + 1,
 375                                        prettier: Some(new_default_prettier.clone()),
 376                                    });
 377                                new_default_prettier
 378                            })?;
 379                        Ok(new_default_prettier)
 380                    }
 381                },
 382            }
 383        })
 384    }
 385
 386    fn register_new_prettier(
 387        prettier_store: &WeakEntity<Self>,
 388        prettier: &Prettier,
 389        worktree_id: Option<WorktreeId>,
 390        new_server_id: LanguageServerId,
 391        cx: &mut AsyncApp,
 392    ) {
 393        let prettier_dir = prettier.prettier_dir();
 394        let is_default = prettier.is_default();
 395        if is_default {
 396            log::info!("Started default prettier in {prettier_dir:?}");
 397        } else {
 398            log::info!("Started prettier in {prettier_dir:?}");
 399        }
 400        if let Some(prettier_server) = prettier.server() {
 401            prettier_store
 402                .update(cx, |prettier_store, cx| {
 403                    let name = if is_default {
 404                        LanguageServerName("prettier (default)".to_string().into())
 405                    } else {
 406                        let worktree_path = worktree_id
 407                            .and_then(|id| {
 408                                prettier_store
 409                                    .worktree_store
 410                                    .read(cx)
 411                                    .worktree_for_id(id, cx)
 412                            })
 413                            .map(|worktree| worktree.read(cx).abs_path());
 414                        let name = match worktree_path {
 415                            Some(worktree_path) => {
 416                                if prettier_dir == worktree_path.as_ref() {
 417                                    let name = prettier_dir
 418                                        .file_name()
 419                                        .and_then(|name| name.to_str())
 420                                        .unwrap_or_default();
 421                                    format!("prettier ({name})")
 422                                } else {
 423                                    let dir_to_display = prettier_dir
 424                                        .strip_prefix(worktree_path.as_ref())
 425                                        .ok()
 426                                        .unwrap_or(prettier_dir);
 427                                    format!("prettier ({})", dir_to_display.display())
 428                                }
 429                            }
 430                            None => format!("prettier ({})", prettier_dir.display()),
 431                        };
 432                        LanguageServerName(name.into())
 433                    };
 434                    cx.emit(PrettierStoreEvent::LanguageServerAdded {
 435                        new_server_id,
 436                        name,
 437                        prettier_server: prettier_server.clone(),
 438                    });
 439                })
 440                .ok();
 441        }
 442    }
 443
 444    pub fn update_prettier_settings(
 445        &self,
 446        worktree: &Entity<Worktree>,
 447        changes: &[(Arc<RelPath>, ProjectEntryId, PathChange)],
 448        cx: &mut Context<Self>,
 449    ) {
 450        let prettier_config_files = Prettier::CONFIG_FILE_NAMES
 451            .iter()
 452            .map(|name| RelPath::unix(name).unwrap())
 453            .collect::<HashSet<_>>();
 454
 455        let prettier_config_file_changed = changes
 456            .iter()
 457            .filter(|(_, _, change)| !matches!(change, PathChange::Loaded))
 458            .filter(|(path, _, _)| {
 459                !path
 460                    .components()
 461                    .any(|component| component == "node_modules")
 462            })
 463            .find(|(path, _, _)| prettier_config_files.contains(path.as_ref()));
 464        let current_worktree_id = worktree.read(cx).id();
 465        if let Some((config_path, _, _)) = prettier_config_file_changed {
 466            log::info!(
 467                "Prettier config file {config_path:?} changed, reloading prettier instances for worktree {current_worktree_id}"
 468            );
 469            let prettiers_to_reload =
 470                self.prettiers_per_worktree
 471                    .get(¤t_worktree_id)
 472                    .iter()
 473                    .flat_map(|prettier_paths| prettier_paths.iter())
 474                    .flatten()
 475                    .filter_map(|prettier_path| {
 476                        Some((
 477                            current_worktree_id,
 478                            Some(prettier_path.clone()),
 479                            self.prettier_instances.get(prettier_path)?.clone(),
 480                        ))
 481                    })
 482                    .chain(self.default_prettier.instance().map(|default_prettier| {
 483                        (current_worktree_id, None, default_prettier.clone())
 484                    }))
 485                    .collect::<Vec<_>>();
 486
 487            cx.background_spawn(async move {
 488                let _: Vec<()> = future::join_all(prettiers_to_reload.into_iter().map(|(worktree_id, prettier_path, prettier_instance)| {
 489                    async move {
 490                        if let Some(instance) = prettier_instance.prettier {
 491                            match instance.await {
 492                                Ok(prettier) => {
 493                                    prettier.clear_cache().log_err().await;
 494                                },
 495                                Err(e) => {
 496                                    match prettier_path {
 497                                        Some(prettier_path) => log::error!(
 498                                            "Failed to clear prettier {prettier_path:?} cache for worktree {worktree_id:?} on prettier settings update: {e:#}"
 499                                        ),
 500                                        None => log::error!(
 501                                            "Failed to clear default prettier cache for worktree {worktree_id:?} on prettier settings update: {e:#}"
 502                                        ),
 503                                    }
 504                                },
 505                            }
 506                        }
 507                    }
 508                }))
 509                .await;
 510            })
 511                .detach();
 512        }
 513    }
 514
 515    pub fn install_default_prettier(
 516        &mut self,
 517        worktree: Option<WorktreeId>,
 518        plugins: impl Iterator<Item = Arc<str>>,
 519        cx: &mut Context<Self>,
 520    ) {
 521        if cfg!(any(test, feature = "test-support")) {
 522            self.default_prettier.installed_plugins.extend(plugins);
 523            self.default_prettier.prettier = PrettierInstallation::Installed(PrettierInstance {
 524                attempt: 0,
 525                prettier: None,
 526            });
 527            return;
 528        }
 529
 530        let mut new_plugins = plugins.collect::<HashSet<_>>();
 531        let node = self.node.clone();
 532
 533        new_plugins.retain(|plugin| !self.default_prettier.installed_plugins.contains(plugin));
 534        let mut installation_attempt = 0;
 535        let previous_installation_task = match &mut self.default_prettier.prettier {
 536            PrettierInstallation::NotInstalled {
 537                installation_task,
 538                attempts,
 539                not_installed_plugins,
 540            } => {
 541                installation_attempt = *attempts;
 542                if installation_attempt > prettier::FAIL_THRESHOLD {
 543                    *installation_task = None;
 544                    log::warn!(
 545                        "Default prettier installation had failed {installation_attempt} times, not attempting again",
 546                    );
 547                    return;
 548                }
 549                new_plugins.extend(not_installed_plugins.iter().cloned());
 550                installation_task.clone()
 551            }
 552            PrettierInstallation::Installed { .. } => {
 553                if new_plugins.is_empty() {
 554                    return;
 555                }
 556                None
 557            }
 558        };
 559
 560        let plugins_to_install = new_plugins.clone();
 561        let fs = Arc::clone(&self.fs);
 562        let new_installation_task = cx
 563            .spawn(async move |prettier_store, cx| {
 564                cx.background_executor()
 565                    .timer(Duration::from_millis(30))
 566                    .await;
 567                let location_data = prettier_store.update(cx, |prettier_store, cx| {
 568                    worktree
 569                        .and_then(|worktree_id| {
 570                            prettier_store
 571                                .worktree_store
 572                                .read(cx)
 573                                .worktree_for_id(worktree_id, cx)
 574                                .map(|worktree| worktree.read(cx).abs_path())
 575                        })
 576                        .map(|locate_from| {
 577                            let installed_prettiers =
 578                                prettier_store.prettier_instances.keys().cloned().collect();
 579                            (locate_from, installed_prettiers)
 580                        })
 581                })?;
 582                let locate_prettier_installation = match location_data {
 583                    Some((locate_from, installed_prettiers)) => {
 584                        Prettier::locate_prettier_installation(
 585                            fs.as_ref(),
 586                            &installed_prettiers,
 587                            locate_from.as_ref(),
 588                        )
 589                        .await
 590                        .context("locate prettier installation")
 591                        .map_err(Arc::new)?
 592                    }
 593                    None => ControlFlow::Continue(None),
 594                };
 595
 596                match locate_prettier_installation {
 597                    ControlFlow::Break(()) => return Ok(()),
 598                    ControlFlow::Continue(prettier_path) => {
 599                        if prettier_path.is_some() {
 600                            new_plugins.clear();
 601                        }
 602                        let mut needs_install =
 603                            should_write_prettier_server_file(fs.as_ref()).await;
 604                        if let Some(previous_installation_task) = previous_installation_task
 605                            && let Err(e) = previous_installation_task.await
 606                        {
 607                            log::error!("Failed to install default prettier: {e:#}");
 608                            prettier_store.update(cx, |prettier_store, _| {
 609                                if let PrettierInstallation::NotInstalled {
 610                                    attempts,
 611                                    not_installed_plugins,
 612                                    ..
 613                                } = &mut prettier_store.default_prettier.prettier
 614                                {
 615                                    *attempts += 1;
 616                                    new_plugins.extend(not_installed_plugins.iter().cloned());
 617                                    installation_attempt = *attempts;
 618                                    needs_install = true;
 619                                };
 620                            })?;
 621                        };
 622                        if installation_attempt > prettier::FAIL_THRESHOLD {
 623                            prettier_store.update(cx, |prettier_store, _| {
 624                                if let PrettierInstallation::NotInstalled {
 625                                    installation_task,
 626                                    ..
 627                                } = &mut prettier_store.default_prettier.prettier
 628                                {
 629                                    *installation_task = None;
 630                                };
 631                            })?;
 632                            log::warn!(
 633                                "Default prettier installation had failed {installation_attempt} \
 634                                times, not attempting again",
 635                            );
 636                            return Ok(());
 637                        }
 638                        prettier_store.update(cx, |prettier_store, _| {
 639                            new_plugins.retain(|plugin| {
 640                                !prettier_store
 641                                    .default_prettier
 642                                    .installed_plugins
 643                                    .contains(plugin)
 644                            });
 645                            if let PrettierInstallation::NotInstalled {
 646                                not_installed_plugins,
 647                                ..
 648                            } = &mut prettier_store.default_prettier.prettier
 649                            {
 650                                not_installed_plugins.retain(|plugin| {
 651                                    !prettier_store
 652                                        .default_prettier
 653                                        .installed_plugins
 654                                        .contains(plugin)
 655                                });
 656                                not_installed_plugins.extend(new_plugins.iter().cloned());
 657                            }
 658                            needs_install |= !new_plugins.is_empty();
 659                        })?;
 660                        if needs_install {
 661                            log::info!(
 662                                "Initializing default prettier with plugins {new_plugins:?}"
 663                            );
 664                            let installed_plugins = new_plugins.clone();
 665                            let executor = cx.background_executor().clone();
 666                            cx.background_spawn(async move {
 667                                install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
 668                                // Save the server file last, so the reinstall need could be determined by the absence of the file.
 669                                save_prettier_server_file(fs.as_ref(), &executor).await?;
 670                                anyhow::Ok(())
 671                            })
 672                            .await
 673                            .context("prettier & plugins install")
 674                            .map_err(Arc::new)?;
 675                            log::info!(
 676                                "Initialized default prettier with plugins: {installed_plugins:?}"
 677                            );
 678                            prettier_store.update(cx, |prettier_store, _| {
 679                                prettier_store.default_prettier.prettier =
 680                                    PrettierInstallation::Installed(PrettierInstance {
 681                                        attempt: 0,
 682                                        prettier: None,
 683                                    });
 684                                prettier_store
 685                                    .default_prettier
 686                                    .installed_plugins
 687                                    .extend(installed_plugins);
 688                            })?;
 689                        } else {
 690                            prettier_store.update(cx, |prettier_store, _| {
 691                                if let PrettierInstallation::NotInstalled { .. } =
 692                                    &mut prettier_store.default_prettier.prettier
 693                                {
 694                                    prettier_store.default_prettier.prettier =
 695                                        PrettierInstallation::Installed(PrettierInstance {
 696                                            attempt: 0,
 697                                            prettier: None,
 698                                        });
 699                                }
 700                            })?;
 701                        }
 702                    }
 703                }
 704                Ok(())
 705            })
 706            .shared();
 707        self.default_prettier.prettier = PrettierInstallation::NotInstalled {
 708            attempts: installation_attempt,
 709            installation_task: Some(new_installation_task),
 710            not_installed_plugins: plugins_to_install,
 711        };
 712    }
 713
 714    pub fn on_settings_changed(
 715        &mut self,
 716        language_formatters_to_check: Vec<(Option<WorktreeId>, LanguageSettings)>,
 717        cx: &mut Context<Self>,
 718    ) {
 719        let mut prettier_plugins_by_worktree = HashMap::default();
 720        for (worktree, language_settings) in language_formatters_to_check {
 721            if language_settings.prettier.allowed
 722                && let Some(plugins) = prettier_plugins_for_language(&language_settings)
 723            {
 724                prettier_plugins_by_worktree
 725                    .entry(worktree)
 726                    .or_insert_with(HashSet::default)
 727                    .extend(plugins.iter().cloned());
 728            }
 729        }
 730        for (worktree, prettier_plugins) in prettier_plugins_by_worktree {
 731            self.install_default_prettier(
 732                worktree,
 733                prettier_plugins.into_iter().map(Arc::from),
 734                cx,
 735            );
 736        }
 737    }
 738}
 739
 740pub fn prettier_plugins_for_language(
 741    language_settings: &LanguageSettings,
 742) -> Option<&HashSet<String>> {
 743    let formatters = language_settings.formatter.as_ref();
 744    if formatters.contains(&Formatter::Prettier) || formatters.contains(&Formatter::Auto) {
 745        return Some(&language_settings.prettier.plugins);
 746    }
 747    None
 748}
 749
 750pub(super) async fn format_with_prettier(
 751    prettier_store: &WeakEntity<PrettierStore>,
 752    buffer: &Entity<Buffer>,
 753    cx: &mut AsyncApp,
 754) -> Option<Result<language::Diff>> {
 755    let prettier_instance = prettier_store
 756        .update(cx, |prettier_store, cx| {
 757            prettier_store.prettier_instance_for_buffer(buffer, cx)
 758        })
 759        .ok()?
 760        .await;
 761
 762    let ignore_dir = prettier_store
 763        .update(cx, |prettier_store, cx| {
 764            prettier_store.prettier_ignore_for_buffer(buffer, cx)
 765        })
 766        .ok()?
 767        .await;
 768
 769    let (prettier_path, prettier_task) = prettier_instance?;
 770
 771    let prettier_description = match prettier_path.as_ref() {
 772        Some(path) => format!("prettier at {path:?}"),
 773        None => "default prettier instance".to_string(),
 774    };
 775
 776    match prettier_task.await {
 777        Ok(prettier) => {
 778            let buffer_path = buffer
 779                .update(cx, |buffer, cx| {
 780                    File::from_dyn(buffer.file()).map(|file| file.abs_path(cx))
 781                })
 782                .ok()
 783                .flatten();
 784
 785            let format_result = prettier
 786                .format(buffer, buffer_path, ignore_dir, cx)
 787                .await
 788                .with_context(|| format!("{} failed to format buffer", prettier_description));
 789
 790            Some(format_result)
 791        }
 792        Err(error) => {
 793            prettier_store
 794                .update(cx, |project, _| {
 795                    let instance_to_update = match prettier_path {
 796                        Some(prettier_path) => project.prettier_instances.get_mut(&prettier_path),
 797                        None => match &mut project.default_prettier.prettier {
 798                            PrettierInstallation::NotInstalled { .. } => None,
 799                            PrettierInstallation::Installed(instance) => Some(instance),
 800                        },
 801                    };
 802
 803                    if let Some(instance) = instance_to_update {
 804                        instance.attempt += 1;
 805                        instance.prettier = None;
 806                    }
 807                })
 808                .log_err();
 809
 810            Some(Err(anyhow!(
 811                "{prettier_description} failed to spawn: {error:#}"
 812            )))
 813        }
 814    }
 815}
 816
 817#[derive(Debug)]
 818pub struct DefaultPrettier {
 819    prettier: PrettierInstallation,
 820    installed_plugins: HashSet<Arc<str>>,
 821}
 822
 823#[derive(Debug)]
 824pub enum PrettierInstallation {
 825    NotInstalled {
 826        attempts: usize,
 827        installation_task: Option<Shared<Task<Result<(), Arc<anyhow::Error>>>>>,
 828        not_installed_plugins: HashSet<Arc<str>>,
 829    },
 830    Installed(PrettierInstance),
 831}
 832
 833pub type PrettierTask = Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>;
 834
 835#[derive(Debug, Clone)]
 836pub struct PrettierInstance {
 837    attempt: usize,
 838    prettier: Option<PrettierTask>,
 839}
 840
 841impl Default for DefaultPrettier {
 842    fn default() -> Self {
 843        Self {
 844            prettier: PrettierInstallation::NotInstalled {
 845                attempts: 0,
 846                installation_task: None,
 847                not_installed_plugins: HashSet::default(),
 848            },
 849            installed_plugins: HashSet::default(),
 850        }
 851    }
 852}
 853
 854impl DefaultPrettier {
 855    pub fn instance(&self) -> Option<&PrettierInstance> {
 856        if let PrettierInstallation::Installed(instance) = &self.prettier {
 857            Some(instance)
 858        } else {
 859            None
 860        }
 861    }
 862
 863    pub fn prettier_task(
 864        &mut self,
 865        node: &NodeRuntime,
 866        worktree_id: Option<WorktreeId>,
 867        cx: &mut Context<PrettierStore>,
 868    ) -> Option<Task<anyhow::Result<PrettierTask>>> {
 869        match &mut self.prettier {
 870            PrettierInstallation::NotInstalled { .. } => Some(
 871                PrettierStore::start_default_prettier(node.clone(), worktree_id, cx),
 872            ),
 873            PrettierInstallation::Installed(existing_instance) => {
 874                existing_instance.prettier_task(node, None, worktree_id, cx)
 875            }
 876        }
 877    }
 878}
 879
 880impl PrettierInstance {
 881    pub fn prettier_task(
 882        &mut self,
 883        node: &NodeRuntime,
 884        prettier_dir: Option<&Path>,
 885        worktree_id: Option<WorktreeId>,
 886        cx: &mut Context<PrettierStore>,
 887    ) -> Option<Task<anyhow::Result<PrettierTask>>> {
 888        if self.attempt > prettier::FAIL_THRESHOLD {
 889            match prettier_dir {
 890                Some(prettier_dir) => log::warn!(
 891                    "Prettier from path {prettier_dir:?} exceeded launch threshold, not starting"
 892                ),
 893                None => log::warn!("Default prettier exceeded launch threshold, not starting"),
 894            }
 895            return None;
 896        }
 897        Some(match &self.prettier {
 898            Some(prettier_task) => Task::ready(Ok(prettier_task.clone())),
 899            None => match prettier_dir {
 900                Some(prettier_dir) => {
 901                    let new_task = PrettierStore::start_prettier(
 902                        node.clone(),
 903                        prettier_dir.to_path_buf(),
 904                        worktree_id,
 905                        cx,
 906                    );
 907                    self.attempt += 1;
 908                    self.prettier = Some(new_task.clone());
 909                    Task::ready(Ok(new_task))
 910                }
 911                None => {
 912                    self.attempt += 1;
 913                    let node = node.clone();
 914                    cx.spawn(async move |prettier_store, cx| {
 915                        prettier_store
 916                            .update(cx, |_, cx| {
 917                                PrettierStore::start_default_prettier(node, worktree_id, cx)
 918                            })?
 919                            .await
 920                    })
 921                }
 922            },
 923        })
 924    }
 925
 926    pub async fn server(&self) -> Option<Arc<LanguageServer>> {
 927        self.prettier.clone()?.await.ok()?.server().cloned()
 928    }
 929}
 930
 931async fn install_prettier_packages(
 932    fs: &dyn Fs,
 933    plugins_to_install: HashSet<Arc<str>>,
 934    node: NodeRuntime,
 935) -> anyhow::Result<()> {
 936    let packages_to_versions = future::try_join_all(
 937        plugins_to_install
 938            .iter()
 939            .chain(Some(&"prettier".into()))
 940            .map(|package_name| async {
 941                let returned_package_name = package_name.to_string();
 942                let latest_version = node
 943                    .npm_package_latest_version(package_name)
 944                    .await
 945                    .with_context(|| {
 946                        format!("fetching latest npm version for package {returned_package_name}")
 947                    })?;
 948                anyhow::Ok((returned_package_name, latest_version))
 949            }),
 950    )
 951    .await
 952    .context("fetching latest npm versions")?;
 953
 954    let default_prettier_dir = default_prettier_dir().as_path();
 955    match fs.metadata(default_prettier_dir).await.with_context(|| {
 956        format!("fetching FS metadata for default prettier dir {default_prettier_dir:?}")
 957    })? {
 958        Some(prettier_dir_metadata) => anyhow::ensure!(
 959            prettier_dir_metadata.is_dir,
 960            "default prettier dir {default_prettier_dir:?} is not a directory"
 961        ),
 962        None => fs
 963            .create_dir(default_prettier_dir)
 964            .await
 965            .with_context(|| format!("creating default prettier dir {default_prettier_dir:?}"))?,
 966    }
 967
 968    log::info!("Installing default prettier and plugins: {packages_to_versions:?}");
 969    let borrowed_packages = packages_to_versions
 970        .iter()
 971        .map(|(package, version)| (package.as_str(), version.as_str()))
 972        .collect::<Vec<_>>();
 973    node.npm_install_packages(default_prettier_dir, &borrowed_packages)
 974        .await
 975        .context("fetching formatter packages")?;
 976    anyhow::Ok(())
 977}
 978
 979async fn save_prettier_server_file(
 980    fs: &dyn Fs,
 981    executor: &BackgroundExecutor,
 982) -> anyhow::Result<()> {
 983    let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
 984    fs.save(
 985        &prettier_wrapper_path,
 986        &text::Rope::from_str(prettier::PRETTIER_SERVER_JS, executor),
 987        text::LineEnding::Unix,
 988    )
 989    .await
 990    .with_context(|| {
 991        format!(
 992            "writing {} file at {prettier_wrapper_path:?}",
 993            prettier::PRETTIER_SERVER_FILE
 994        )
 995    })?;
 996    Ok(())
 997}
 998
 999async fn should_write_prettier_server_file(fs: &dyn Fs) -> bool {
1000    let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
1001    if !fs.is_file(&prettier_wrapper_path).await {
1002        return true;
1003    }
1004    let Ok(prettier_server_file_contents) = fs.load(&prettier_wrapper_path).await else {
1005        return true;
1006    };
1007    prettier_server_file_contents != prettier::PRETTIER_SERVER_JS
1008}