From 033d0ae610c62118409841136047b3dbd0a9691f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 22:07:40 +0200 Subject: [PATCH 1/4] Remember default prettier and its plugin installation --- crates/project/src/project.rs | 37 +++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3e5bcef00cd91d901639bd6f1849d6dede92420f..879f061219980a60c566754ff25aa9cfefcb3e67 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -162,6 +162,7 @@ pub struct Project { copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, node: Option>, + default_prettier_plugins: Option>, prettier_instances: HashMap< (Option, PathBuf), Shared, Arc>>>, @@ -677,6 +678,7 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: Some(node), + default_prettier_plugins: None, prettier_instances: HashMap::default(), } }) @@ -776,6 +778,7 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: None, + default_prettier_plugins: None, prettier_instances: HashMap::default(), }; for worktree in worktrees { @@ -8497,7 +8500,7 @@ impl Project { #[cfg(any(test, feature = "test-support"))] fn install_default_formatters( - &self, + &mut self, _worktree: Option, _new_language: &Language, _language_settings: &LanguageSettings, @@ -8508,7 +8511,7 @@ impl Project { #[cfg(not(any(test, feature = "test-support")))] fn install_default_formatters( - &self, + &mut self, worktree: Option, new_language: &Language, language_settings: &LanguageSettings, @@ -8537,6 +8540,30 @@ impl Project { return Task::ready(Ok(())); }; + let mut plugins_to_install = prettier_plugins; + if let Some(already_installed) = &self.default_prettier_plugins { + plugins_to_install.retain(|plugin| !already_installed.contains(plugin)); + } + if plugins_to_install.is_empty() && self.default_prettier_plugins.is_some() { + return Task::ready(Ok(())); + } + + let previous_plugins = self.default_prettier_plugins.clone(); + self.default_prettier_plugins + .get_or_insert_with(HashSet::default) + .extend(plugins_to_install.iter()); + let (mut install_success_tx, mut install_success_rx) = + futures::channel::mpsc::channel::<()>(1); + cx.spawn(|this, mut cx| async move { + if install_success_rx.next().await.is_none() { + this.update(&mut cx, |this, _| { + log::warn!("Prettier plugin installation did not finish successfully, restoring previous installed plugins {previous_plugins:?}"); + this.default_prettier_plugins = previous_plugins; + }) + } + }) + .detach(); + let default_prettier_dir = util::paths::DEFAULT_PRETTIER_DIR.as_path(); let already_running_prettier = self .prettier_instances @@ -8552,7 +8579,7 @@ impl Project { .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier::PRETTIER_SERVER_FILE))?; let packages_to_versions = future::try_join_all( - prettier_plugins + plugins_to_install .iter() .chain(Some(&"prettier")) .map(|package_name| async { @@ -8573,8 +8600,10 @@ impl Project { (package.as_str(), version.as_str()) }).collect::>(); node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; + let installed_packages = !plugins_to_install.is_empty(); + install_success_tx.try_send(()).ok(); - if !prettier_plugins.is_empty() { + if !installed_packages { if let Some(prettier) = already_running_prettier { prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; } From 29a32039bab78bda4caf52fe90ba232203425462 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 22:16:00 +0200 Subject: [PATCH 2/4] Start message numbering during prettier init, log error message text --- crates/prettier/src/prettier_server.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/prettier/src/prettier_server.js b/crates/prettier/src/prettier_server.js index a56c220f208607d00d1ef413c8529dd4244008b4..9967aec50f4b9497af33d8811167b48a7a17f0f1 100644 --- a/crates/prettier/src/prettier_server.js +++ b/crates/prettier/src/prettier_server.js @@ -55,8 +55,11 @@ async function handleBuffer(prettier) { } // allow concurrent request handling by not `await`ing the message handling promise (async function) handleMessage(message, prettier).catch(e => { - sendResponse({ id: message.id, ...makeError(`error during message handling: ${e}`) }); - }); + const errorMessage = message; + if ((errorMessage.params || {}).text !== undefined) { + errorMessage.params.text = "..snip.."; + } + sendResponse({ id: message.id, ...makeError(`error during message '${JSON.stringify(errorMessage)}' handling: ${e}`) }); }); } } @@ -172,7 +175,7 @@ async function handleMessage(message, prettier) { sendResponse({ id, result: null }); } else if (method === 'initialize') { sendResponse({ - id, + id: id || 0, result: { "capabilities": {} } From fd6f6cc9f8b10d7bd4307e89099b2c1e5b0d31a5 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 22:33:44 +0200 Subject: [PATCH 3/4] Return proper full paths for single file workspaces --- crates/project/src/worktree.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index f6fae0c98ba520a789fb9da156fd5a20cc392efc..80fd44761c7ca3d1afee4247cab5b019da5d111b 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -2662,12 +2662,12 @@ impl language::File for File { impl language::LocalFile for File { fn abs_path(&self, cx: &AppContext) -> PathBuf { - self.worktree - .read(cx) - .as_local() - .unwrap() - .abs_path - .join(&self.path) + let worktree_path = &self.worktree.read(cx).as_local().unwrap().abs_path; + if self.path.as_ref() == Path::new("") { + worktree_path.to_path_buf() + } else { + worktree_path.join(&self.path) + } } fn load(&self, cx: &AppContext) -> Task> { From 6ee9beed73dd4c9ed3a13a05bc56c561c566d958 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 31 Oct 2023 11:54:40 +0200 Subject: [PATCH 4/4] Enqueue default prettier installations --- crates/project/src/project.rs | 164 +++++++++++++++++++++------------- 1 file changed, 102 insertions(+), 62 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 879f061219980a60c566754ff25aa9cfefcb3e67..b38bcd1db24b351f33347942f9c5cf0974e8dcdd 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -162,13 +162,20 @@ pub struct Project { copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, node: Option>, - default_prettier_plugins: Option>, + #[cfg(not(any(test, feature = "test-support")))] + default_prettier: Option, prettier_instances: HashMap< (Option, PathBuf), Shared, Arc>>>, >, } +#[cfg(not(any(test, feature = "test-support")))] +struct DefaultPrettier { + installation_process: Option>>, + installed_plugins: HashSet<&'static str>, +} + struct DelayedDebounced { task: Option>, cancel_channel: Option>, @@ -678,7 +685,8 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: Some(node), - default_prettier_plugins: None, + #[cfg(not(any(test, feature = "test-support")))] + default_prettier: None, prettier_instances: HashMap::default(), } }) @@ -778,7 +786,8 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: None, - default_prettier_plugins: None, + #[cfg(not(any(test, feature = "test-support")))] + default_prettier: None, prettier_instances: HashMap::default(), }; for worktree in worktrees { @@ -8541,76 +8550,107 @@ impl Project { }; let mut plugins_to_install = prettier_plugins; - if let Some(already_installed) = &self.default_prettier_plugins { - plugins_to_install.retain(|plugin| !already_installed.contains(plugin)); - } - if plugins_to_install.is_empty() && self.default_prettier_plugins.is_some() { - return Task::ready(Ok(())); - } - - let previous_plugins = self.default_prettier_plugins.clone(); - self.default_prettier_plugins - .get_or_insert_with(HashSet::default) - .extend(plugins_to_install.iter()); let (mut install_success_tx, mut install_success_rx) = - futures::channel::mpsc::channel::<()>(1); - cx.spawn(|this, mut cx| async move { - if install_success_rx.next().await.is_none() { - this.update(&mut cx, |this, _| { - log::warn!("Prettier plugin installation did not finish successfully, restoring previous installed plugins {previous_plugins:?}"); - this.default_prettier_plugins = previous_plugins; - }) - } - }) - .detach(); + futures::channel::mpsc::channel::>(1); + let new_installation_process = cx + .spawn(|this, mut cx| async move { + if let Some(installed_plugins) = install_success_rx.next().await { + this.update(&mut cx, |this, _| { + let default_prettier = + this.default_prettier + .get_or_insert_with(|| DefaultPrettier { + installation_process: None, + installed_plugins: HashSet::default(), + }); + if !installed_plugins.is_empty() { + log::info!("Installed new prettier plugins: {installed_plugins:?}"); + default_prettier.installed_plugins.extend(installed_plugins); + } + }) + } + }) + .shared(); + let previous_installation_process = + if let Some(default_prettier) = &mut self.default_prettier { + plugins_to_install + .retain(|plugin| !default_prettier.installed_plugins.contains(plugin)); + if plugins_to_install.is_empty() { + return Task::ready(Ok(())); + } + std::mem::replace( + &mut default_prettier.installation_process, + Some(new_installation_process.clone()), + ) + } else { + None + }; let default_prettier_dir = util::paths::DEFAULT_PRETTIER_DIR.as_path(); let already_running_prettier = self .prettier_instances .get(&(worktree, default_prettier_dir.to_path_buf())) .cloned(); - let fs = Arc::clone(&self.fs); - cx.background() - .spawn(async move { - let prettier_wrapper_path = default_prettier_dir.join(prettier::PRETTIER_SERVER_FILE); - // method creates parent directory if it doesn't exist - fs.save(&prettier_wrapper_path, &text::Rope::from(prettier::PRETTIER_SERVER_JS), text::LineEnding::Unix).await - .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier::PRETTIER_SERVER_FILE))?; + cx.spawn(|this, mut cx| async move { + if let Some(previous_installation_process) = previous_installation_process { + previous_installation_process.await; + } + let mut everything_was_installed = false; + this.update(&mut cx, |this, _| { + match &mut this.default_prettier { + Some(default_prettier) => { + plugins_to_install + .retain(|plugin| !default_prettier.installed_plugins.contains(plugin)); + everything_was_installed = plugins_to_install.is_empty(); + }, + None => this.default_prettier = Some(DefaultPrettier { installation_process: Some(new_installation_process), installed_plugins: HashSet::default() }), + } + }); + if everything_was_installed { + return Ok(()); + } - let packages_to_versions = future::try_join_all( - plugins_to_install - .iter() - .chain(Some(&"prettier")) - .map(|package_name| async { - let returned_package_name = package_name.to_string(); - let latest_version = node.npm_package_latest_version(package_name) - .await - .with_context(|| { - format!("fetching latest npm version for package {returned_package_name}") - })?; - anyhow::Ok((returned_package_name, latest_version)) - }), - ) - .await - .context("fetching latest npm versions")?; - - log::info!("Fetching default prettier and plugins: {packages_to_versions:?}"); - let borrowed_packages = packages_to_versions.iter().map(|(package, version)| { - (package.as_str(), version.as_str()) - }).collect::>(); - node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; - let installed_packages = !plugins_to_install.is_empty(); - install_success_tx.try_send(()).ok(); - - if !installed_packages { - if let Some(prettier) = already_running_prettier { - prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; + cx.background() + .spawn(async move { + let prettier_wrapper_path = default_prettier_dir.join(prettier::PRETTIER_SERVER_FILE); + // method creates parent directory if it doesn't exist + fs.save(&prettier_wrapper_path, &text::Rope::from(prettier::PRETTIER_SERVER_JS), text::LineEnding::Unix).await + .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier::PRETTIER_SERVER_FILE))?; + + let packages_to_versions = future::try_join_all( + plugins_to_install + .iter() + .chain(Some(&"prettier")) + .map(|package_name| async { + let returned_package_name = package_name.to_string(); + let latest_version = node.npm_package_latest_version(package_name) + .await + .with_context(|| { + format!("fetching latest npm version for package {returned_package_name}") + })?; + anyhow::Ok((returned_package_name, latest_version)) + }), + ) + .await + .context("fetching latest npm versions")?; + + log::info!("Fetching default prettier and plugins: {packages_to_versions:?}"); + let borrowed_packages = packages_to_versions.iter().map(|(package, version)| { + (package.as_str(), version.as_str()) + }).collect::>(); + node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; + let installed_packages = !plugins_to_install.is_empty(); + install_success_tx.try_send(plugins_to_install).ok(); + + if !installed_packages { + if let Some(prettier) = already_running_prettier { + prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; + } } - } - anyhow::Ok(()) - }) + anyhow::Ok(()) + }).await + }) } }