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