// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use std::future::Future; use std::pin::Pin; use std::sync::Arc; use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_core::futures::future; use deno_core::serde_json; use deno_core::serde_json::Value; use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::ConfigurationItem; use crate::lsp::repl::get_repl_workspace_settings; use super::config::SpecifierSettings; use super::config::SETTINGS_SECTION; use super::lsp_custom; use super::testing::lsp_custom as testing_lsp_custom; #[derive(Debug)] pub enum TestingNotification { Module(testing_lsp_custom::TestModuleNotificationParams), DeleteModule(testing_lsp_custom::TestModuleDeleteNotificationParams), Progress(testing_lsp_custom::TestRunProgressParams), } #[derive(Clone)] pub struct Client(Arc); impl std::fmt::Debug for Client { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Client").finish() } } impl Client { pub fn from_tower(client: tower_lsp::Client) -> Self { Self(Arc::new(TowerClient(client))) } pub fn new_for_repl() -> Self { Self(Arc::new(ReplClient)) } pub async fn publish_diagnostics( &self, uri: lsp::Url, diags: Vec, version: Option, ) { self.0.publish_diagnostics(uri, diags, version).await; } pub async fn send_registry_state_notification( &self, params: lsp_custom::RegistryStateNotificationParams, ) { self.0.send_registry_state_notification(params).await; } pub fn send_test_notification(&self, params: TestingNotification) { self.0.send_test_notification(params); } pub async fn specifier_configurations( &self, specifiers: Vec, ) -> Result>, AnyError> { self.0.specifier_configurations(specifiers).await } pub async fn specifier_configuration( &self, specifier: &lsp::Url, ) -> Result { let values = self .0 .specifier_configurations(vec![specifier.clone()]) .await?; if let Some(value) = values.into_iter().next() { value.map_err(|err| { anyhow!( "Error converting specifier settings ({}): {}", specifier, err ) }) } else { bail!( "Expected the client to return a configuration item for specifier: {}", specifier ); } } pub async fn workspace_configuration(&self) -> Result { self.0.workspace_configuration().await } pub async fn show_message( &self, message_type: lsp::MessageType, message: impl std::fmt::Display, ) { self .0 .show_message(message_type, format!("{}", message)) .await } pub async fn register_capability( &self, registrations: Vec, ) -> Result<(), AnyError> { self.0.register_capability(registrations).await } } type AsyncReturn = Pin + 'static + Send>>; trait ClientTrait: Send + Sync { fn publish_diagnostics( &self, uri: lsp::Url, diagnostics: Vec, version: Option, ) -> AsyncReturn<()>; fn send_registry_state_notification( &self, params: lsp_custom::RegistryStateNotificationParams, ) -> AsyncReturn<()>; fn send_test_notification(&self, params: TestingNotification); fn specifier_configurations( &self, uris: Vec, ) -> AsyncReturn>, AnyError>>; fn workspace_configuration(&self) -> AsyncReturn>; fn show_message( &self, message_type: lsp::MessageType, text: String, ) -> AsyncReturn<()>; fn register_capability( &self, registrations: Vec, ) -> AsyncReturn>; } #[derive(Clone)] struct TowerClient(tower_lsp::Client); impl ClientTrait for TowerClient { fn publish_diagnostics( &self, uri: lsp::Url, diagnostics: Vec, version: Option, ) -> AsyncReturn<()> { let client = self.0.clone(); Box::pin(async move { client.publish_diagnostics(uri, diagnostics, version).await }) } fn send_registry_state_notification( &self, params: lsp_custom::RegistryStateNotificationParams, ) -> AsyncReturn<()> { let client = self.0.clone(); Box::pin(async move { client .send_notification::(params) .await }) } fn send_test_notification(&self, notification: TestingNotification) { let client = self.0.clone(); tokio::task::spawn(async move { match notification { TestingNotification::Module(params) => { client .send_notification::( params, ) .await } TestingNotification::DeleteModule(params) => client .send_notification::( params, ) .await, TestingNotification::Progress(params) => client .send_notification::( params, ) .await, } }); } fn specifier_configurations( &self, uris: Vec, ) -> AsyncReturn>, AnyError>> { let client = self.0.clone(); Box::pin(async move { let config_response = client .configuration( uris .into_iter() .map(|uri| ConfigurationItem { scope_uri: Some(uri), section: Some(SETTINGS_SECTION.to_string()), }) .collect(), ) .await?; Ok( config_response .into_iter() .map(|value| { serde_json::from_value::(value).map_err(|err| { anyhow!("Error converting specifier settings: {}", err) }) }) .collect(), ) }) } fn workspace_configuration(&self) -> AsyncReturn> { let client = self.0.clone(); Box::pin(async move { let config_response = client .configuration(vec![ConfigurationItem { scope_uri: None, section: Some(SETTINGS_SECTION.to_string()), }]) .await; match config_response { Ok(value_vec) => match value_vec.get(0).cloned() { Some(value) => Ok(value), None => bail!("Missing response workspace configuration."), }, Err(err) => { bail!("Error getting workspace configuration: {}", err) } } }) } fn show_message( &self, message_type: lsp::MessageType, message: String, ) -> AsyncReturn<()> { let client = self.0.clone(); Box::pin(async move { client.show_message(message_type, message).await }) } fn register_capability( &self, registrations: Vec, ) -> AsyncReturn> { let client = self.0.clone(); Box::pin(async move { client .register_capability(registrations) .await .map_err(|err| anyhow!("{}", err)) }) } } #[derive(Clone)] struct ReplClient; impl ClientTrait for ReplClient { fn publish_diagnostics( &self, _uri: lsp::Url, _diagnostics: Vec, _version: Option, ) -> AsyncReturn<()> { Box::pin(future::ready(())) } fn send_registry_state_notification( &self, _params: lsp_custom::RegistryStateNotificationParams, ) -> AsyncReturn<()> { Box::pin(future::ready(())) } fn send_test_notification(&self, _params: TestingNotification) {} fn specifier_configurations( &self, uris: Vec, ) -> AsyncReturn>, AnyError>> { // all specifiers are enabled for the REPL let settings = uris .into_iter() .map(|_| { Ok(SpecifierSettings { enable: true, ..Default::default() }) }) .collect(); Box::pin(future::ready(Ok(settings))) } fn workspace_configuration(&self) -> AsyncReturn> { Box::pin(future::ready(Ok( serde_json::to_value(get_repl_workspace_settings()).unwrap(), ))) } fn show_message( &self, _message_type: lsp::MessageType, _message: String, ) -> AsyncReturn<()> { Box::pin(future::ready(())) } fn register_capability( &self, _registrations: Vec, ) -> AsyncReturn> { Box::pin(future::ready(Ok(()))) } }