prettier_store.rs

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