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