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