use std::sync::OnceLock; use lsp_types::notification::Notification; use crate::server::ClientSender; static MESSENGER: OnceLock = OnceLock::new(); pub(crate) fn init_messenger(client_sender: &ClientSender) { MESSENGER .set(client_sender.clone()) .expect("messenger should only be initialized once"); // unregister any previously registered panic hook let _ = std::panic::take_hook(); // When we panic, try to notify the client. std::panic::set_hook(Box::new(move |panic_info| { if let Some(messenger) = MESSENGER.get() { let _ = messenger.send(lsp_server::Message::Notification( lsp_server::Notification { method: lsp_types::notification::ShowMessage::METHOD.into(), params: serde_json::to_value(lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message: String::from( "The Ruff language server exited with a panic. See the logs for more details." ), }) .unwrap_or_default(), }, )); } let backtrace = std::backtrace::Backtrace::force_capture(); tracing::error!("{panic_info}\n{backtrace}"); })); } pub(crate) fn show_message(message: String, message_type: lsp_types::MessageType) { MESSENGER .get() .expect("messenger should be initialized") .send(lsp_server::Message::Notification( lsp_server::Notification { method: lsp_types::notification::ShowMessage::METHOD.into(), params: serde_json::to_value(lsp_types::ShowMessageParams { typ: message_type, message, }) .unwrap(), }, )) .expect("message should send"); } /// Sends an error to the client with a formatted message. The error is sent in a /// `window/showMessage` notification. macro_rules! show_err_msg { ($msg:expr$(, $($arg:tt),*)?) => { crate::message::show_message(::core::format_args!($msg, $($($arg),*)?).to_string(), lsp_types::MessageType::ERROR) }; }