]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / rust-analyzer / src / dispatch.rs
CommitLineData
064997fb
FG
1//! See [RequestDispatcher].
2use std::{fmt, panic, thread};
3
4use ide::Cancelled;
5use lsp_server::ExtractError;
6use serde::{de::DeserializeOwned, Serialize};
7
8use 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.
29pub(crate) struct RequestDispatcher<'a> {
30 pub(crate) req: Option<lsp_server::Request>,
31 pub(crate) global_state: &'a mut GlobalState,
32}
33
34impl<'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 };
55 if let Ok(response) = result_to_response::<R>(req.id.clone(), result) {
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
83 if let Ok(response) = thread_result_to_response::<R>(req.id.clone(), result) {
84 self.global_state.respond(response);
85 }
86
87 self
88 }
89
90 /// Dispatches the request onto thread pool
91 pub(crate) fn on<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::Retry(req),
115 }
116 }
117 });
118
119 self
120 }
121
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(
126 req.id,
127 lsp_server::ErrorCode::MethodNotFound as i32,
128 "unknown request".to_string(),
129 );
130 self.global_state.respond(response);
131 }
132 }
133
134 fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)>
135 where
136 R: lsp_types::request::Request,
137 R::Params: DeserializeOwned + fmt::Debug,
138 {
139 let req = match &self.req {
140 Some(req) if req.method == R::METHOD => self.req.take()?,
141 _ => return None,
142 };
143
144 let res = crate::from_json(R::METHOD, &req.params);
145 match res {
146 Ok(params) => {
147 let panic_context =
148 format!("\nversion: {}\nrequest: {} {:#?}", version(), R::METHOD, params);
149 Some((req, params, panic_context))
150 }
151 Err(err) => {
152 let response = lsp_server::Response::new_err(
153 req.id,
154 lsp_server::ErrorCode::InvalidParams as i32,
155 err.to_string(),
156 );
157 self.global_state.respond(response);
158 None
159 }
160 }
161 }
162}
163
164fn thread_result_to_response<R>(
165 id: lsp_server::RequestId,
166 result: thread::Result<Result<R::Result>>,
167) -> Result<lsp_server::Response, Cancelled>
168where
169 R: lsp_types::request::Request,
170 R::Params: DeserializeOwned,
171 R::Result: Serialize,
172{
173 match result {
174 Ok(result) => result_to_response::<R>(id, result),
175 Err(panic) => {
176 let panic_message = panic
177 .downcast_ref::<String>()
178 .map(String::as_str)
179 .or_else(|| panic.downcast_ref::<&str>().copied());
180
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)
185 };
186
187 Ok(lsp_server::Response::new_err(
188 id,
189 lsp_server::ErrorCode::InternalError as i32,
190 message,
191 ))
192 }
193 }
194}
195
196fn result_to_response<R>(
197 id: lsp_server::RequestId,
198 result: Result<R::Result>,
199) -> Result<lsp_server::Response, Cancelled>
200where
201 R: lsp_types::request::Request,
202 R::Params: DeserializeOwned,
203 R::Result: Serialize,
204{
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(
212 id,
213 lsp_server::ErrorCode::InternalError as i32,
214 e.to_string(),
215 ),
216 },
217 },
218 };
219 Ok(res)
220}
221
222pub(crate) struct NotificationDispatcher<'a> {
223 pub(crate) not: Option<lsp_server::Notification>,
224 pub(crate) global_state: &'a mut GlobalState,
225}
226
227impl<'a> NotificationDispatcher<'a> {
228 pub(crate) fn on<N>(
229 &mut self,
230 f: fn(&mut GlobalState, N::Params) -> Result<()>,
231 ) -> Result<&mut Self>
232 where
233 N: lsp_types::notification::Notification,
234 N::Params: DeserializeOwned + Send,
235 {
236 let not = match self.not.take() {
237 Some(it) => it,
238 None => return Ok(self),
239 };
240 let params = match not.extract::<N::Params>(N::METHOD) {
241 Ok(it) => it,
242 Err(ExtractError::JsonError { method, error }) => {
243 panic!("Invalid request\nMethod: {method}\n error: {error}",)
244 }
245 Err(ExtractError::MethodMismatch(not)) => {
246 self.not = Some(not);
247 return Ok(self);
248 }
249 };
250 let _pctx = stdx::panic_context::enter(format!(
251 "\nversion: {}\nnotification: {}",
252 version(),
253 N::METHOD
254 ));
255 f(self.global_state, params)?;
256 Ok(self)
257 }
258
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);
263 }
264 }
265 }
266}