diff --git a/cli/lsp/client.rs b/cli/lsp/client.rs index 84953c4d456a51..915af68313bc77 100644 --- a/cli/lsp/client.rs +++ b/cli/lsp/client.rs @@ -96,6 +96,17 @@ impl Client { }); } + pub fn send_did_upgrade_check_notification( + &self, + params: lsp_custom::DidUpgradeCheckNotificationParams, + ) { + // do on a task in case the caller currently is in the lsp lock + let client = self.0.clone(); + spawn(async move { + client.send_did_upgrade_check_notification(params).await; + }); + } + pub fn show_message( &self, message_type: lsp::MessageType, @@ -165,6 +176,10 @@ trait ClientTrait: Send + Sync { &self, params: lsp_custom::DidChangeDenoConfigurationNotificationParams, ); + async fn send_did_upgrade_check_notification( + &self, + params: lsp_custom::DidUpgradeCheckNotificationParams, + ); async fn workspace_configuration( &self, scopes: Vec>, @@ -249,6 +264,16 @@ impl ClientTrait for TowerClient { .await } + async fn send_did_upgrade_check_notification( + &self, + params: lsp_custom::DidUpgradeCheckNotificationParams, + ) { + self + .0 + .send_notification::(params) + .await + } + async fn workspace_configuration( &self, scopes: Vec>, @@ -350,6 +375,12 @@ impl ClientTrait for ReplClient { ) { } + async fn send_did_upgrade_check_notification( + &self, + _params: lsp_custom::DidUpgradeCheckNotificationParams, + ) { + } + async fn workspace_configuration( &self, scopes: Vec>, diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 667df74bb5246f..6f0c96746da1d5 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -112,6 +112,8 @@ use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::tools::fmt::format_file; use crate::tools::fmt::format_parsed_source; +use crate::tools::upgrade::check_for_upgrades_for_lsp; +use crate::tools::upgrade::upgrade_check_enabled; use crate::util::fs::remove_dir_all_if_exists; use crate::util::path::is_importable_ext; use crate::util::path::specifier_to_file_path; @@ -3189,6 +3191,25 @@ impl tower_lsp::LanguageServer for LanguageServer { } lsp_log!("Server ready."); + + if upgrade_check_enabled() { + let http_client = self.0.read().await.http_client.clone(); + match check_for_upgrades_for_lsp(http_client).await { + Ok(version_info) => { + client.send_did_upgrade_check_notification( + lsp_custom::DidUpgradeCheckNotificationParams { + upgrade_available: version_info.map( + |(latest_version, is_canary)| lsp_custom::UpgradeAvailable { + latest_version, + is_canary, + }, + ), + }, + ); + } + Err(err) => lsp_warn!("Failed to check for upgrades: {err}"), + } + } } async fn shutdown(&self) -> LspResult<()> { diff --git a/cli/lsp/logging.rs b/cli/lsp/logging.rs index 8b703f2a5a8d68..b8c8c99e60e369 100644 --- a/cli/lsp/logging.rs +++ b/cli/lsp/logging.rs @@ -62,11 +62,13 @@ macro_rules! lsp_log { /// for downgrading these logs to another log level in the REPL. macro_rules! lsp_warn { ($($arg:tt)+) => ( - let lsp_log_level = $crate::lsp::logging::lsp_warn_level(); - if lsp_log_level == log::Level::Debug { - $crate::lsp::logging::lsp_debug!($($arg)+) - } else { - log::log!(lsp_log_level, $($arg)+) + { + let lsp_log_level = $crate::lsp::logging::lsp_warn_level(); + if lsp_log_level == log::Level::Debug { + $crate::lsp::logging::lsp_debug!($($arg)+) + } else { + log::log!(lsp_log_level, $($arg)+) + } } ) } diff --git a/cli/lsp/lsp_custom.rs b/cli/lsp/lsp_custom.rs index 685da6b14ae957..96f7be79077cd2 100644 --- a/cli/lsp/lsp_custom.rs +++ b/cli/lsp/lsp_custom.rs @@ -91,6 +91,27 @@ impl lsp::notification::Notification const METHOD: &'static str = "deno/didChangeDenoConfiguration"; } +pub enum DidUpgradeCheckNotification {} + +impl lsp::notification::Notification for DidUpgradeCheckNotification { + type Params = DidUpgradeCheckNotificationParams; + + const METHOD: &'static str = "deno/didUpgradeCheck"; +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct UpgradeAvailable { + pub latest_version: String, + pub is_canary: bool, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DidUpgradeCheckNotificationParams { + pub upgrade_available: Option, +} + /// This notification is only sent for testing purposes /// in order to know what the latest diagnostics are. pub enum DiagnosticBatchNotification {} diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index d2cb58d28fb6f2..dccde44e242ed0 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -74,9 +74,9 @@ impl UpdateCheckerEnvironment for RealUpdateCheckerEnvironment { let http_client = self.http_client.clone(); async move { if version::is_canary() { - get_latest_canary_version(&http_client).await + get_latest_canary_version(&http_client, false).await } else { - get_latest_release_version(&http_client).await + get_latest_release_version(&http_client, false).await } } .boxed() @@ -186,11 +186,18 @@ fn print_release_notes(current_version: &str, new_version: &str) { } } +pub fn upgrade_check_enabled() -> bool { + matches!( + env::var("DENO_NO_UPDATE_CHECK"), + Err(env::VarError::NotPresent) + ) +} + pub fn check_for_upgrades( http_client: Arc, cache_file_path: PathBuf, ) { - if env::var("DENO_NO_UPDATE_CHECK").is_ok() { + if !upgrade_check_enabled() { return; } @@ -239,6 +246,32 @@ pub fn check_for_upgrades( } } +pub async fn check_for_upgrades_for_lsp( + http_client: Arc, +) -> Result, AnyError> { + if !upgrade_check_enabled() { + return Ok(None); + } + let is_canary = version::is_canary(); + let latest_version; + let mut is_upgrade; + if is_canary { + latest_version = get_latest_canary_version(&http_client, true).await?; + is_upgrade = latest_version != version::GIT_COMMIT_HASH; + } else { + latest_version = get_latest_release_version(&http_client, true).await?; + is_upgrade = true; + if let Ok(current) = Version::parse_standard(version::deno()) { + if let Ok(latest) = Version::parse_standard(&latest_version) { + if current >= latest { + is_upgrade = false; + } + } + } + }; + Ok(is_upgrade.then_some((latest_version, is_canary))) +} + async fn fetch_and_store_latest_version< TEnvironment: UpdateCheckerEnvironment, >( @@ -334,10 +367,10 @@ pub async fn upgrade( None => { let latest_version = if upgrade_flags.canary { log::info!("Looking up latest canary version"); - get_latest_canary_version(client).await? + get_latest_canary_version(client, false).await? } else { log::info!("Looking up latest version"); - get_latest_release_version(client).await? + get_latest_release_version(client, false).await? }; let current_is_most_recent = if upgrade_flags.canary { @@ -448,20 +481,26 @@ pub async fn upgrade( async fn get_latest_release_version( client: &HttpClient, + for_lsp: bool, ) -> Result { - let text = client - .download_text("https://dl.deno.land/release-latest.txt") - .await?; + let mut url = "https://dl.deno.land/release-latest.txt".to_string(); + if for_lsp { + url.push_str("?lsp"); + } + let text = client.download_text(url).await?; let version = text.trim().to_string(); Ok(version.replace('v', "")) } async fn get_latest_canary_version( client: &HttpClient, + for_lsp: bool, ) -> Result { - let text = client - .download_text("https://dl.deno.land/canary-latest.txt") - .await?; + let mut url = "https://dl.deno.land/canary-latest.txt".to_string(); + if for_lsp { + url.push_str("?lsp"); + } + let text = client.download_text(url).await?; let version = text.trim().to_string(); Ok(version) } diff --git a/test_util/src/lsp.rs b/test_util/src/lsp.rs index 352f5d3656ec67..2fb227709fdd53 100644 --- a/test_util/src/lsp.rs +++ b/test_util/src/lsp.rs @@ -529,6 +529,7 @@ impl LspClientBuilder { "DENO_DONT_USE_INTERNAL_LSP_DIAGNOSTIC_SYNC_FLAG", if self.use_diagnostic_sync { "1" } else { "" }, ) + .env("DENO_NO_UPDATE_CHECK", "1") .arg("lsp") .stdin(Stdio::piped()) .stdout(Stdio::piped());