//! A visitor for downcasting arbitrary request (JSON) into a specific type.
-use std::panic;
+use std::{fmt, panic};
use serde::{de::DeserializeOwned, Serialize};
) -> Result<&mut Self>
where
R: lsp_types::request::Request + 'static,
- R::Params: DeserializeOwned + Send + 'static,
+ R::Params: DeserializeOwned + Send + fmt::Debug + 'static,
R::Result: Serialize + 'static,
{
let (id, params) = match self.parse::<R>() {
self.global_state.task_pool.handle.spawn({
let world = self.global_state.snapshot();
+
move || {
+ let _ctx =
+ stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params));
let result = f(world, params);
Task::Response(result_to_response::<R>(id, result))
}
--- /dev/null
+//! A micro-crate to enhance panic messages with context info.
+//!
+//! FIXME: upstream to https://github.com/kriomant/panic-context ?
+
+use std::{cell::RefCell, panic, sync::Once};
+
+pub fn enter(context: String) -> impl Drop {
+ static ONCE: Once = Once::new();
+ ONCE.call_once(PanicContext::init);
+
+ with_ctx(|ctx| ctx.push(context));
+ PanicContext { _priv: () }
+}
+
+#[must_use]
+struct PanicContext {
+ _priv: (),
+}
+
+impl PanicContext {
+ fn init() {
+ let default_hook = panic::take_hook();
+ let hook = move |panic_info: &panic::PanicInfo<'_>| {
+ with_ctx(|ctx| {
+ if !ctx.is_empty() {
+ eprintln!("Panic context:");
+ for frame in ctx.iter() {
+ eprintln!("> {}\n", frame)
+ }
+ }
+ default_hook(panic_info)
+ })
+ };
+ panic::set_hook(Box::new(hook))
+ }
+}
+
+impl Drop for PanicContext {
+ fn drop(&mut self) {
+ with_ctx(|ctx| assert!(ctx.pop().is_some()))
+ }
+}
+
+fn with_ctx(f: impl FnOnce(&mut Vec<String>)) {
+ thread_local! {
+ static CTX: RefCell<Vec<String>> = RefCell::new(Vec::new());
+ }
+ CTX.with(|ctx| f(&mut *ctx.borrow_mut()))
+}