diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7623f84c6d..919b563cfb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -172,6 +172,7 @@ pub struct Project { struct DefaultPrettier { instance: Option, Arc>>>>, + installation_process: Option>>>>, #[cfg(not(any(test, feature = "test-support")))] installed_plugins: HashSet<&'static str>, } @@ -924,8 +925,7 @@ impl Project { } for (worktree, language, settings) in language_formatters_to_check { - self.install_default_formatters(worktree, &language, &settings, cx) - .detach_and_log_err(cx); + self.install_default_formatters(worktree, &language, &settings, cx); } // Start all the newly-enabled language servers. @@ -2681,8 +2681,7 @@ impl Project { let buffer_file = File::from_dyn(buffer_file.as_ref()); let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx)); - self.install_default_formatters(worktree, &new_language, &settings, cx) - .detach_and_log_err(cx); + self.install_default_formatters(worktree, &new_language, &settings, cx); if let Some(file) = buffer_file { let worktree = file.worktree.clone(); if let Some(tree) = worktree.read(cx).as_local() { @@ -8454,27 +8453,12 @@ impl Project { match started_default_prettier { Some(old_task) => return Some((None, old_task)), None => { - let new_task = project.update(&mut cx, |project, cx| { - let new_task = start_prettier( - node, - DEFAULT_PRETTIER_DIR.clone(), - Some(worktree_id), - cx, - ); - project - .default_prettier - .get_or_insert_with(|| DefaultPrettier { - instance: None, - #[cfg(not(any( - test, - feature = "test-support" - )))] - installed_plugins: HashSet::default(), - }) - .instance = Some(new_task.clone()); - new_task - }); - return Some((None, new_task)); + let new_default_prettier = project + .update(&mut cx, |_, cx| { + start_default_prettier(node, Some(worktree_id), cx) + }) + .await; + return Some((None, new_default_prettier)); } } } @@ -8532,16 +8516,8 @@ impl Project { match started_default_prettier { Some(old_task) => return Task::ready(Some((None, old_task))), None => { - let new_task = - start_prettier(node, DEFAULT_PRETTIER_DIR.clone(), None, cx); - self.default_prettier - .get_or_insert_with(|| DefaultPrettier { - instance: None, - #[cfg(not(any(test, feature = "test-support")))] - installed_plugins: HashSet::default(), - }) - .instance = Some(new_task.clone()); - return Task::ready(Some((None, new_task))); + let new_task = start_default_prettier(node, None, cx); + return cx.spawn(|_, _| async move { Some((None, new_task.await)) }); } } } @@ -8563,8 +8539,7 @@ impl Project { _new_language: &Language, _language_settings: &LanguageSettings, _cx: &mut ModelContext, - ) -> Task> { - return Task::ready(Ok(())); + ) { } #[cfg(not(any(test, feature = "test-support")))] @@ -8574,13 +8549,13 @@ impl Project { new_language: &Language, language_settings: &LanguageSettings, cx: &mut ModelContext, - ) -> Task> { + ) { match &language_settings.formatter { Formatter::Prettier { .. } | Formatter::Auto => {} - Formatter::LanguageServer | Formatter::External { .. } => return Task::ready(Ok(())), + Formatter::LanguageServer | Formatter::External { .. } => return, }; let Some(node) = self.node.as_ref().cloned() else { - return Task::ready(Ok(())); + return; }; let mut prettier_plugins = None; @@ -8595,7 +8570,7 @@ impl Project { ) } let Some(prettier_plugins) = prettier_plugins else { - return Task::ready(Ok(())); + return; }; let fs = Arc::clone(&self.fs); @@ -8622,60 +8597,124 @@ impl Project { plugins_to_install .retain(|plugin| !default_prettier.installed_plugins.contains(plugin)); if plugins_to_install.is_empty() { - return Task::ready(Ok(())); + return; } - default_prettier.instance.clone() + default_prettier.installation_process.clone() } else { None }; let fs = Arc::clone(&self.fs); - cx.spawn(|this, mut cx| async move { - match locate_prettier_installation - .await - .context("locate prettier installation")? - { - Some(_non_default_prettier) => return Ok(()), - None => { - let mut needs_restart = match previous_installation_process { - Some(previous_installation_process) => { - previous_installation_process.await.is_err() - } - None => true, - }; - this.update(&mut cx, |this, _| { - if let Some(default_prettier) = &mut this.default_prettier { - plugins_to_install.retain(|plugin| { - !default_prettier.installed_plugins.contains(plugin) - }); - needs_restart |= !plugins_to_install.is_empty(); - } - }); - if needs_restart { - let installed_plugins = plugins_to_install.clone(); - cx.background() - .spawn(async move { - install_default_prettier(plugins_to_install, node, fs).await - }) - .await - .context("prettier & plugins install")?; + let default_prettier = self + .default_prettier + .get_or_insert_with(|| DefaultPrettier { + instance: None, + installation_process: None, + installed_plugins: HashSet::default(), + }); + default_prettier.installation_process = Some( + cx.spawn(|this, mut cx| async move { + match locate_prettier_installation + .await + .context("locate prettier installation") + .map_err(Arc::new)? + { + Some(_non_default_prettier) => return Ok(()), + None => { + let mut needs_install = match previous_installation_process { + Some(previous_installation_process) => { + previous_installation_process.await.is_err() + } + None => true, + }; this.update(&mut cx, |this, _| { - let default_prettier = - this.default_prettier - .get_or_insert_with(|| DefaultPrettier { - instance: None, - installed_plugins: HashSet::default(), - }); - default_prettier.instance = None; - default_prettier.installed_plugins.extend(installed_plugins); + if let Some(default_prettier) = &mut this.default_prettier { + plugins_to_install.retain(|plugin| { + !default_prettier.installed_plugins.contains(plugin) + }); + needs_install |= !plugins_to_install.is_empty(); + } }); + if needs_install { + let installed_plugins = plugins_to_install.clone(); + cx.background() + .spawn(async move { + install_default_prettier(plugins_to_install, node, fs).await + }) + .await + .context("prettier & plugins install") + .map_err(Arc::new)?; + this.update(&mut cx, |this, _| { + let default_prettier = + this.default_prettier + .get_or_insert_with(|| DefaultPrettier { + instance: None, + installation_process: Some( + Task::ready(Ok(())).shared(), + ), + installed_plugins: HashSet::default(), + }); + default_prettier.instance = None; + default_prettier.installed_plugins.extend(installed_plugins); + }); + } } } - } - Ok(()) - }) + Ok(()) + }) + .shared(), + ); } } +fn start_default_prettier( + node: Arc, + worktree_id: Option, + cx: &mut ModelContext<'_, Project>, +) -> Task, Arc>>>> { + cx.spawn(|project, mut cx| async move { + loop { + let default_prettier_installing = project.update(&mut cx, |project, _| { + project + .default_prettier + .as_ref() + .and_then(|default_prettier| default_prettier.installation_process.clone()) + }); + match default_prettier_installing { + Some(installation_task) => { + if installation_task.await.is_ok() { + break; + } + } + None => break, + } + } + + project.update(&mut cx, |project, cx| { + match project + .default_prettier + .as_mut() + .and_then(|default_prettier| default_prettier.instance.as_mut()) + { + Some(default_prettier) => default_prettier.clone(), + None => { + let new_default_prettier = + start_prettier(node, DEFAULT_PRETTIER_DIR.clone(), worktree_id, cx); + project + .default_prettier + .get_or_insert_with(|| DefaultPrettier { + instance: None, + installation_process: None, + #[cfg(not(any(test, feature = "test-support")))] + installed_plugins: HashSet::default(), + }) + .instance = Some(new_default_prettier.clone()); + new_default_prettier + } + } + }) + }) +} + fn start_prettier( node: Arc, prettier_dir: PathBuf, diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 32dc542c20..90d32643d5 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -13,7 +13,7 @@ use pretty_assertions::assert_eq; use serde_json::json; use std::{cell::RefCell, os::unix, rc::Rc, task::Poll}; use unindent::Unindent as _; -use util::{assert_set_eq, test::temp_tree, paths::PathMatcher}; +use util::{assert_set_eq, paths::PathMatcher, test::temp_tree}; #[cfg(test)] #[ctor::ctor]