]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! See [RequestDispatcher]. |
2 | use std::{fmt, panic, thread}; | |
3 | ||
4 | use ide::Cancelled; | |
5 | use lsp_server::ExtractError; | |
6 | use serde::{de::DeserializeOwned, Serialize}; | |
7 | ||
8 | use crate::{ | |
9 | global_state::{GlobalState, GlobalStateSnapshot}, | |
10 | main_loop::Task, | |
11 | version::version, | |
12 | LspError, Result, | |
13 | }; | |
14 | ||
15 | /// A visitor for routing a raw JSON request to an appropriate handler function. | |
16 | /// | |
17 | /// Most requests are read-only and async and are handled on the threadpool | |
18 | /// (`on` method). | |
19 | /// | |
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 | |
22 | /// requests. | |
23 | /// | |
24 | /// Some requests modify the state, and are run on the main thread to get | |
25 | /// `&mut` (`on_sync_mut`). | |
26 | /// | |
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, | |
32 | } | |
33 | ||
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>( | |
39 | &mut self, | |
40 | f: fn(&mut GlobalState, R::Params) -> Result<R::Result>, | |
41 | ) -> &mut Self | |
42 | where | |
43 | R: lsp_types::request::Request, | |
44 | R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug, | |
45 | R::Result: Serialize, | |
46 | { | |
47 | let (req, params, panic_context) = match self.parse::<R>() { | |
48 | Some(it) => it, | |
49 | None => return self, | |
50 | }; | |
51 | let result = { | |
52 | let _pctx = stdx::panic_context::enter(panic_context); | |
53 | f(self.global_state, params) | |
54 | }; | |
2b03887a | 55 | if let Ok(response) = result_to_response::<R>(req.id, result) { |
064997fb FG |
56 | self.global_state.respond(response); |
57 | } | |
58 | ||
59 | self | |
60 | } | |
61 | ||
62 | /// Dispatches the request onto the current thread. | |
63 | pub(crate) fn on_sync<R>( | |
64 | &mut self, | |
65 | f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>, | |
66 | ) -> &mut Self | |
67 | where | |
68 | R: lsp_types::request::Request, | |
69 | R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug, | |
70 | R::Result: Serialize, | |
71 | { | |
72 | let (req, params, panic_context) = match self.parse::<R>() { | |
73 | Some(it) => it, | |
74 | None => return self, | |
75 | }; | |
76 | let global_state_snapshot = self.global_state.snapshot(); | |
77 | ||
78 | let result = panic::catch_unwind(move || { | |
79 | let _pctx = stdx::panic_context::enter(panic_context); | |
80 | f(global_state_snapshot, params) | |
81 | }); | |
82 | ||
2b03887a | 83 | if let Ok(response) = thread_result_to_response::<R>(req.id, result) { |
064997fb FG |
84 | self.global_state.respond(response); |
85 | } | |
86 | ||
87 | self | |
88 | } | |
89 | ||
353b0b11 FG |
90 | /// Dispatches the request onto thread pool |
91 | pub(crate) fn on_no_retry<R>( | |
92 | &mut self, | |
93 | f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>, | |
94 | ) -> &mut Self | |
95 | where | |
96 | R: lsp_types::request::Request + 'static, | |
97 | R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, | |
98 | R::Result: Serialize, | |
99 | { | |
100 | let (req, params, panic_context) = match self.parse::<R>() { | |
101 | Some(it) => it, | |
102 | None => return self, | |
103 | }; | |
104 | ||
105 | self.global_state.task_pool.handle.spawn({ | |
106 | let world = self.global_state.snapshot(); | |
107 | move || { | |
108 | let result = panic::catch_unwind(move || { | |
109 | let _pctx = stdx::panic_context::enter(panic_context); | |
110 | f(world, params) | |
111 | }); | |
112 | match thread_result_to_response::<R>(req.id.clone(), result) { | |
113 | Ok(response) => Task::Response(response), | |
114 | Err(_) => Task::Response(lsp_server::Response::new_err( | |
115 | req.id, | |
116 | lsp_server::ErrorCode::ContentModified as i32, | |
117 | "content modified".to_string(), | |
118 | )), | |
119 | } | |
120 | } | |
121 | }); | |
122 | ||
123 | self | |
124 | } | |
125 | ||
064997fb FG |
126 | /// Dispatches the request onto thread pool |
127 | pub(crate) fn on<R>( | |
128 | &mut self, | |
129 | f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>, | |
130 | ) -> &mut Self | |
131 | where | |
132 | R: lsp_types::request::Request + 'static, | |
133 | R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, | |
134 | R::Result: Serialize, | |
135 | { | |
136 | let (req, params, panic_context) = match self.parse::<R>() { | |
137 | Some(it) => it, | |
138 | None => return self, | |
139 | }; | |
140 | ||
141 | self.global_state.task_pool.handle.spawn({ | |
142 | let world = self.global_state.snapshot(); | |
143 | move || { | |
144 | let result = panic::catch_unwind(move || { | |
145 | let _pctx = stdx::panic_context::enter(panic_context); | |
146 | f(world, params) | |
147 | }); | |
148 | match thread_result_to_response::<R>(req.id.clone(), result) { | |
149 | Ok(response) => Task::Response(response), | |
150 | Err(_) => Task::Retry(req), | |
151 | } | |
152 | } | |
153 | }); | |
154 | ||
155 | self | |
156 | } | |
157 | ||
158 | pub(crate) fn finish(&mut self) { | |
159 | if let Some(req) = self.req.take() { | |
160 | tracing::error!("unknown request: {:?}", req); | |
161 | let response = lsp_server::Response::new_err( | |
162 | req.id, | |
163 | lsp_server::ErrorCode::MethodNotFound as i32, | |
164 | "unknown request".to_string(), | |
165 | ); | |
166 | self.global_state.respond(response); | |
167 | } | |
168 | } | |
169 | ||
170 | fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)> | |
171 | where | |
172 | R: lsp_types::request::Request, | |
173 | R::Params: DeserializeOwned + fmt::Debug, | |
174 | { | |
175 | let req = match &self.req { | |
176 | Some(req) if req.method == R::METHOD => self.req.take()?, | |
177 | _ => return None, | |
178 | }; | |
179 | ||
180 | let res = crate::from_json(R::METHOD, &req.params); | |
181 | match res { | |
182 | Ok(params) => { | |
183 | let panic_context = | |
9c376795 | 184 | format!("\nversion: {}\nrequest: {} {params:#?}", version(), R::METHOD); |
064997fb FG |
185 | Some((req, params, panic_context)) |
186 | } | |
187 | Err(err) => { | |
188 | let response = lsp_server::Response::new_err( | |
189 | req.id, | |
190 | lsp_server::ErrorCode::InvalidParams as i32, | |
191 | err.to_string(), | |
192 | ); | |
193 | self.global_state.respond(response); | |
194 | None | |
195 | } | |
196 | } | |
197 | } | |
198 | } | |
199 | ||
200 | fn thread_result_to_response<R>( | |
201 | id: lsp_server::RequestId, | |
202 | result: thread::Result<Result<R::Result>>, | |
203 | ) -> Result<lsp_server::Response, Cancelled> | |
204 | where | |
205 | R: lsp_types::request::Request, | |
206 | R::Params: DeserializeOwned, | |
207 | R::Result: Serialize, | |
208 | { | |
209 | match result { | |
210 | Ok(result) => result_to_response::<R>(id, result), | |
211 | Err(panic) => { | |
212 | let panic_message = panic | |
213 | .downcast_ref::<String>() | |
214 | .map(String::as_str) | |
215 | .or_else(|| panic.downcast_ref::<&str>().copied()); | |
216 | ||
217 | let mut message = "request handler panicked".to_string(); | |
218 | if let Some(panic_message) = panic_message { | |
219 | message.push_str(": "); | |
220 | message.push_str(panic_message) | |
221 | }; | |
222 | ||
223 | Ok(lsp_server::Response::new_err( | |
224 | id, | |
225 | lsp_server::ErrorCode::InternalError as i32, | |
226 | message, | |
227 | )) | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | fn result_to_response<R>( | |
233 | id: lsp_server::RequestId, | |
234 | result: Result<R::Result>, | |
235 | ) -> Result<lsp_server::Response, Cancelled> | |
236 | where | |
237 | R: lsp_types::request::Request, | |
238 | R::Params: DeserializeOwned, | |
239 | R::Result: Serialize, | |
240 | { | |
241 | let res = match result { | |
242 | Ok(resp) => lsp_server::Response::new_ok(id, &resp), | |
243 | Err(e) => match e.downcast::<LspError>() { | |
244 | Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), | |
245 | Err(e) => match e.downcast::<Cancelled>() { | |
246 | Ok(cancelled) => return Err(*cancelled), | |
247 | Err(e) => lsp_server::Response::new_err( | |
248 | id, | |
249 | lsp_server::ErrorCode::InternalError as i32, | |
250 | e.to_string(), | |
251 | ), | |
252 | }, | |
253 | }, | |
254 | }; | |
255 | Ok(res) | |
256 | } | |
257 | ||
258 | pub(crate) struct NotificationDispatcher<'a> { | |
259 | pub(crate) not: Option<lsp_server::Notification>, | |
260 | pub(crate) global_state: &'a mut GlobalState, | |
261 | } | |
262 | ||
263 | impl<'a> NotificationDispatcher<'a> { | |
264 | pub(crate) fn on<N>( | |
265 | &mut self, | |
266 | f: fn(&mut GlobalState, N::Params) -> Result<()>, | |
267 | ) -> Result<&mut Self> | |
268 | where | |
269 | N: lsp_types::notification::Notification, | |
270 | N::Params: DeserializeOwned + Send, | |
271 | { | |
272 | let not = match self.not.take() { | |
273 | Some(it) => it, | |
274 | None => return Ok(self), | |
275 | }; | |
276 | let params = match not.extract::<N::Params>(N::METHOD) { | |
277 | Ok(it) => it, | |
278 | Err(ExtractError::JsonError { method, error }) => { | |
279 | panic!("Invalid request\nMethod: {method}\n error: {error}",) | |
280 | } | |
281 | Err(ExtractError::MethodMismatch(not)) => { | |
282 | self.not = Some(not); | |
283 | return Ok(self); | |
284 | } | |
285 | }; | |
286 | let _pctx = stdx::panic_context::enter(format!( | |
287 | "\nversion: {}\nnotification: {}", | |
288 | version(), | |
289 | N::METHOD | |
290 | )); | |
291 | f(self.global_state, params)?; | |
292 | Ok(self) | |
293 | } | |
294 | ||
295 | pub(crate) fn finish(&mut self) { | |
296 | if let Some(not) = &self.not { | |
297 | if !not.method.starts_with("$/") { | |
298 | tracing::error!("unhandled notification: {:?}", not); | |
299 | } | |
300 | } | |
301 | } | |
302 | } |