Do not attempt to run default prettier if it's not installed yet

This commit is contained in:
Kirill Bulatov 2023-11-28 18:45:12 +02:00
parent 938f2531c4
commit 46ac82f498

View file

@ -86,7 +86,7 @@ pub struct DefaultPrettier {
pub enum PrettierInstallation { pub enum PrettierInstallation {
NotInstalled { NotInstalled {
attempts: usize, attempts: usize,
installation_process: Option<Shared<Task<Result<(), Arc<anyhow::Error>>>>>, installation_task: Option<Shared<Task<Result<(), Arc<anyhow::Error>>>>>,
}, },
Installed(PrettierInstance), Installed(PrettierInstance),
} }
@ -104,7 +104,7 @@ impl Default for DefaultPrettier {
Self { Self {
prettier: PrettierInstallation::NotInstalled { prettier: PrettierInstallation::NotInstalled {
attempts: 0, attempts: 0,
installation_process: None, installation_task: None,
}, },
installed_plugins: HashSet::default(), installed_plugins: HashSet::default(),
} }
@ -128,9 +128,7 @@ impl DefaultPrettier {
) -> Option<Task<anyhow::Result<PrettierTask>>> { ) -> Option<Task<anyhow::Result<PrettierTask>>> {
match &mut self.prettier { match &mut self.prettier {
PrettierInstallation::NotInstalled { .. } => { PrettierInstallation::NotInstalled { .. } => {
// `start_default_prettier` will start the installation process if it's not already running and wait for it to finish Some(start_default_prettier(Arc::clone(node), worktree_id, cx))
let new_task = start_default_prettier(Arc::clone(node), worktree_id, cx);
Some(cx.spawn(|_, _| async move { new_task.await }))
} }
PrettierInstallation::Installed(existing_instance) => { PrettierInstallation::Installed(existing_instance) => {
existing_instance.prettier_task(node, None, worktree_id, cx) existing_instance.prettier_task(node, None, worktree_id, cx)
@ -193,23 +191,31 @@ fn start_default_prettier(
) -> Task<anyhow::Result<PrettierTask>> { ) -> Task<anyhow::Result<PrettierTask>> {
cx.spawn(|project, mut cx| async move { cx.spawn(|project, mut cx| async move {
loop { loop {
let installation_process = project.update(&mut cx, |project, _| { let installation_task = project.update(&mut cx, |project, _| {
match &project.default_prettier.prettier { match &project.default_prettier.prettier {
PrettierInstallation::NotInstalled { PrettierInstallation::NotInstalled {
installation_process, installation_task, ..
.. } => ControlFlow::Continue(installation_task.clone()),
} => ControlFlow::Continue(installation_process.clone()),
PrettierInstallation::Installed(default_prettier) => { PrettierInstallation::Installed(default_prettier) => {
ControlFlow::Break(default_prettier.clone()) ControlFlow::Break(default_prettier.clone())
} }
} }
}); });
match installation_process { match installation_task {
ControlFlow::Continue(None) => { ControlFlow::Continue(None) => {
anyhow::bail!("Default prettier is not installed and cannot be started") anyhow::bail!("Default prettier is not installed and cannot be started")
} }
ControlFlow::Continue(Some(installation_process)) => { ControlFlow::Continue(Some(installation_task)) => {
if let Err(e) = installation_process.await { log::info!("Waiting for default prettier to install");
if let Err(e) = installation_task.await {
project.update(&mut cx, |project, _| {
if let PrettierInstallation::NotInstalled {
installation_task, ..
} = &mut project.default_prettier.prettier
{
*installation_task = None;
}
});
anyhow::bail!( anyhow::bail!(
"Cannot start default prettier due to its installation failure: {e:#}" "Cannot start default prettier due to its installation failure: {e:#}"
); );
@ -254,6 +260,7 @@ fn start_prettier(
cx: &mut ModelContext<'_, Project>, cx: &mut ModelContext<'_, Project>,
) -> PrettierTask { ) -> PrettierTask {
cx.spawn(|project, mut cx| async move { cx.spawn(|project, mut cx| async move {
log::info!("Starting prettier at path {prettier_dir:?}");
let new_server_id = project.update(&mut cx, |project, _| { let new_server_id = project.update(&mut cx, |project, _| {
project.languages.next_language_server_id() project.languages.next_language_server_id()
}); });
@ -319,10 +326,9 @@ fn register_new_prettier(
} }
} }
async fn install_default_prettier( async fn install_prettier_packages(
plugins_to_install: HashSet<&'static str>, plugins_to_install: HashSet<&'static str>,
node: Arc<dyn NodeRuntime>, node: Arc<dyn NodeRuntime>,
fs: Arc<dyn Fs>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let packages_to_versions = let packages_to_versions =
future::try_join_all(plugins_to_install.iter().chain(Some(&"prettier")).map( future::try_join_all(plugins_to_install.iter().chain(Some(&"prettier")).map(
@ -563,23 +569,24 @@ impl Project {
} }
} }
#[cfg(any(test, feature = "test-support"))] // TODO kb uncomment
pub fn install_default_prettier( // #[cfg(any(test, feature = "test-support"))]
&mut self, // pub fn install_default_prettier(
_worktree: Option<WorktreeId>, // &mut self,
_new_language: &Language, // _worktree: Option<WorktreeId>,
language_settings: &LanguageSettings, // _new_language: &Language,
_cx: &mut ModelContext<Self>, // language_settings: &LanguageSettings,
) { // _cx: &mut ModelContext<Self>,
// suppress unused code warnings // ) {
match &language_settings.formatter { // // suppress unused code warnings
Formatter::Prettier { .. } | Formatter::Auto => {} // match &language_settings.formatter {
Formatter::LanguageServer | Formatter::External { .. } => return, // Formatter::Prettier { .. } | Formatter::Auto => {}
}; // Formatter::LanguageServer | Formatter::External { .. } => return,
let _ = &self.default_prettier.installed_plugins; // };
} // let _ = &self.default_prettier.installed_plugins;
// }
#[cfg(not(any(test, feature = "test-support")))] // #[cfg(not(any(test, feature = "test-support")))]
pub fn install_default_prettier( pub fn install_default_prettier(
&mut self, &mut self,
worktree: Option<WorktreeId>, worktree: Option<WorktreeId>,
@ -632,13 +639,13 @@ impl Project {
plugins_to_install plugins_to_install
.retain(|plugin| !self.default_prettier.installed_plugins.contains(plugin)); .retain(|plugin| !self.default_prettier.installed_plugins.contains(plugin));
let mut installation_attempts = 0; let mut installation_attempts = 0;
let previous_installation_process = match &self.default_prettier.prettier { let previous_installation_task = match &self.default_prettier.prettier {
PrettierInstallation::NotInstalled { PrettierInstallation::NotInstalled {
installation_process, installation_task,
attempts, attempts,
} => { } => {
installation_attempts = *attempts; installation_attempts = *attempts;
installation_process.clone() installation_task.clone()
} }
PrettierInstallation::Installed { .. } => { PrettierInstallation::Installed { .. } => {
if plugins_to_install.is_empty() { if plugins_to_install.is_empty() {
@ -656,61 +663,66 @@ impl Project {
} }
let fs = Arc::clone(&self.fs); let fs = Arc::clone(&self.fs);
self.default_prettier.prettier = PrettierInstallation::NotInstalled { let new_installation_task = cx
attempts: installation_attempts + 1, .spawn(|this, mut cx| async move {
installation_process: Some( match locate_prettier_installation
cx.spawn(|this, mut cx| async move { .await
match locate_prettier_installation .context("locate prettier installation")
.await .map_err(Arc::new)?
.context("locate prettier installation") {
.map_err(Arc::new)? ControlFlow::Break(()) => return Ok(()),
{ ControlFlow::Continue(Some(_non_default_prettier)) => {
ControlFlow::Break(()) => return Ok(()), save_prettier_server_file(fs.as_ref()).await?;
ControlFlow::Continue(Some(_non_default_prettier)) => { return Ok(());
save_prettier_server_file(fs.as_ref()).await?; }
return Ok(()); ControlFlow::Continue(None) => {
} let mut needs_install = match previous_installation_task {
ControlFlow::Continue(None) => { Some(previous_installation_task) => {
let mut needs_install = match previous_installation_process { match previous_installation_task.await {
Some(previous_installation_process) => { Ok(()) => false,
previous_installation_process.await.is_err() Err(e) => {
log::error!("Failed to install default prettier: {e:#}");
true
}
} }
None => true,
};
this.update(&mut cx, |this, _| {
plugins_to_install.retain(|plugin| {
!this.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()
// TODO kb instead of always installing, try to start the existing installation first?
.spawn(async move {
save_prettier_server_file(fs.as_ref()).await?;
install_default_prettier(plugins_to_install, node, fs).await
})
.await
.context("prettier & plugins install")
.map_err(Arc::new)?;
this.update(&mut cx, |this, _| {
this.default_prettier.prettier =
PrettierInstallation::Installed(PrettierInstance {
attempt: 0,
prettier: None,
});
this.default_prettier
.installed_plugins
.extend(installed_plugins);
});
} }
None => true,
};
this.update(&mut cx, |this, _| {
plugins_to_install.retain(|plugin| {
!this.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 {
save_prettier_server_file(fs.as_ref()).await?;
install_prettier_packages(plugins_to_install, node).await
})
.await
.context("prettier & plugins install")
.map_err(Arc::new)?;
this.update(&mut cx, |this, _| {
this.default_prettier.prettier =
PrettierInstallation::Installed(PrettierInstance {
attempt: 0,
prettier: None,
});
this.default_prettier
.installed_plugins
.extend(installed_plugins);
});
} }
} }
Ok(()) }
}) Ok(())
.shared(), })
), .shared();
self.default_prettier.prettier = PrettierInstallation::NotInstalled {
attempts: installation_attempts + 1,
installation_task: Some(new_installation_task),
}; };
} }
} }