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