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