1 //! See [RequestDispatcher].
2 use std::{fmt, panic, thread};
5 use lsp_server::ExtractError;
6 use serde::{de::DeserializeOwned, Serialize};
9 global_state::{GlobalState, GlobalStateSnapshot},
15 /// A visitor for routing a raw JSON request to an appropriate handler function.
17 /// Most requests are read-only and async and are handled on the threadpool
20 /// Some read-only requests are latency sensitive, and are immediately handled
21 /// on the main loop thread (`on_sync`). These are typically typing-related
24 /// Some requests modify the state, and are run on the main thread to get
25 /// `&mut` (`on_sync_mut`).
27 /// Read-only requests are wrapped into `catch_unwind` -- they don't modify the
28 /// state, so it's OK to recover from their failures.
29 pub(crate) struct RequestDispatcher<'a> {
30 pub(crate) req: Option<lsp_server::Request>,
31 pub(crate) global_state: &'a mut GlobalState,
34 impl<'a> RequestDispatcher<'a> {
35 /// Dispatches the request onto the current thread, given full access to
36 /// mutable global state. Unlike all other methods here, this one isn't
37 /// guarded by `catch_unwind`, so, please, don't make bugs :-)
38 pub(crate) fn on_sync_mut<R>(
40 f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
43 R: lsp_types::request::Request,
44 R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
47 let (req, params, panic_context) = match self.parse::<R>() {
52 let _pctx = stdx::panic_context::enter(panic_context);
53 f(self.global_state, params)
55 if let Ok(response) = result_to_response::<R>(req.id.clone(), result) {
56 self.global_state.respond(response);
62 /// Dispatches the request onto the current thread.
63 pub(crate) fn on_sync<R>(
65 f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
68 R: lsp_types::request::Request,
69 R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
72 let (req, params, panic_context) = match self.parse::<R>() {
76 let global_state_snapshot = self.global_state.snapshot();
78 let result = panic::catch_unwind(move || {
79 let _pctx = stdx::panic_context::enter(panic_context);
80 f(global_state_snapshot, params)
83 if let Ok(response) = thread_result_to_response::<R>(req.id.clone(), result) {
84 self.global_state.respond(response);
90 /// Dispatches the request onto thread pool
93 f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
96 R: lsp_types::request::Request + 'static,
97 R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
100 let (req, params, panic_context) = match self.parse::<R>() {
105 self.global_state.task_pool.handle.spawn({
106 let world = self.global_state.snapshot();
108 let result = panic::catch_unwind(move || {
109 let _pctx = stdx::panic_context::enter(panic_context);
112 match thread_result_to_response::<R>(req.id.clone(), result) {
113 Ok(response) => Task::Response(response),
114 Err(_) => Task::Retry(req),
122 pub(crate) fn finish(&mut self) {
123 if let Some(req) = self.req.take() {
124 tracing::error!("unknown request: {:?}", req);
125 let response = lsp_server::Response::new_err(
127 lsp_server::ErrorCode::MethodNotFound as i32,
128 "unknown request".to_string(),
130 self.global_state.respond(response);
134 fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)>
136 R: lsp_types::request::Request,
137 R::Params: DeserializeOwned + fmt::Debug,
139 let req = match &self.req {
140 Some(req) if req.method == R::METHOD => self.req.take()?,
144 let res = crate::from_json(R::METHOD, &req.params);
148 format!("\nversion: {}\nrequest: {} {:#?}", version(), R::METHOD, params);
149 Some((req, params, panic_context))
152 let response = lsp_server::Response::new_err(
154 lsp_server::ErrorCode::InvalidParams as i32,
157 self.global_state.respond(response);
164 fn thread_result_to_response<R>(
165 id: lsp_server::RequestId,
166 result: thread::Result<Result<R::Result>>,
167 ) -> Result<lsp_server::Response, Cancelled>
169 R: lsp_types::request::Request,
170 R::Params: DeserializeOwned,
171 R::Result: Serialize,
174 Ok(result) => result_to_response::<R>(id, result),
176 let panic_message = panic
177 .downcast_ref::<String>()
179 .or_else(|| panic.downcast_ref::<&str>().copied());
181 let mut message = "request handler panicked".to_string();
182 if let Some(panic_message) = panic_message {
183 message.push_str(": ");
184 message.push_str(panic_message)
187 Ok(lsp_server::Response::new_err(
189 lsp_server::ErrorCode::InternalError as i32,
196 fn result_to_response<R>(
197 id: lsp_server::RequestId,
198 result: Result<R::Result>,
199 ) -> Result<lsp_server::Response, Cancelled>
201 R: lsp_types::request::Request,
202 R::Params: DeserializeOwned,
203 R::Result: Serialize,
205 let res = match result {
206 Ok(resp) => lsp_server::Response::new_ok(id, &resp),
207 Err(e) => match e.downcast::<LspError>() {
208 Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
209 Err(e) => match e.downcast::<Cancelled>() {
210 Ok(cancelled) => return Err(*cancelled),
211 Err(e) => lsp_server::Response::new_err(
213 lsp_server::ErrorCode::InternalError as i32,
222 pub(crate) struct NotificationDispatcher<'a> {
223 pub(crate) not: Option<lsp_server::Notification>,
224 pub(crate) global_state: &'a mut GlobalState,
227 impl<'a> NotificationDispatcher<'a> {
230 f: fn(&mut GlobalState, N::Params) -> Result<()>,
231 ) -> Result<&mut Self>
233 N: lsp_types::notification::Notification,
234 N::Params: DeserializeOwned + Send,
236 let not = match self.not.take() {
238 None => return Ok(self),
240 let params = match not.extract::<N::Params>(N::METHOD) {
242 Err(ExtractError::JsonError { method, error }) => {
243 panic!("Invalid request\nMethod: {method}\n error: {error}",)
245 Err(ExtractError::MethodMismatch(not)) => {
246 self.not = Some(not);
250 let _pctx = stdx::panic_context::enter(format!(
251 "\nversion: {}\nnotification: {}",
255 f(self.global_state, params)?;
259 pub(crate) fn finish(&mut self) {
260 if let Some(not) = &self.not {
261 if !not.method.starts_with("$/") {
262 tracing::error!("unhandled notification: {:?}", not);