sync_ls/server/
dap_srv.rs

1#[cfg(feature = "system")]
2use std::sync::atomic::Ordering;
3
4use dapts::IRequest;
5
6use super::*;
7
8impl LspClient {
9    /// Sends a dap event to the client.
10    pub fn send_dap_event<E: dapts::IEvent>(&self, body: E::Body) {
11        let req_id = self.req_queue.lock().outgoing.alloc_request_id();
12
13        self.send_dap_event_(dap::Event::new(req_id as i64, E::EVENT.to_owned(), body));
14    }
15
16    /// Sends an untyped dap_event to the client.
17    pub fn send_dap_event_(&self, evt: dap::Event) {
18        self.sender.send_message(evt.into());
19    }
20}
21
22impl<Args: Initializer> LsBuilder<DapMessage, Args>
23where
24    Args::S: 'static,
25{
26    /// Registers an async command handler.
27    pub fn with_command<R: Serialize + 'static>(
28        mut self,
29        cmd: &'static str,
30        handler: AsyncHandler<Args::S, Vec<JsonValue>, R>,
31    ) -> Self {
32        self.command_handlers.insert(
33            cmd,
34            Box::new(move |s, _req_id, req| erased_response(handler(s, req))),
35        );
36        self
37    }
38
39    /// Registers a raw request handler that handlers a kind of untyped lsp
40    /// request.
41    pub fn with_raw_request<R: IRequest>(
42        mut self,
43        handler: RawHandler<Args::S, JsonValue>,
44    ) -> Self {
45        self.req_handlers
46            .insert(R::COMMAND, Box::new(move |s, _req_id, req| handler(s, req)));
47        self
48    }
49
50    // todo: unsafe typed
51    /// Registers an raw request handler that handlers a kind of typed lsp
52    /// request.
53    pub fn with_request_<R: IRequest>(
54        mut self,
55        handler: fn(&mut Args::S, RequestId, R::Arguments) -> ScheduledResult,
56    ) -> Self {
57        self.req_handlers.insert(
58            R::COMMAND,
59            Box::new(move |s, req_id, req| scheduled_response(handler(s, req_id, from_json(req)?))),
60        );
61        self
62    }
63
64    /// Registers a typed request handler.
65    pub fn with_request<R: IRequest>(
66        mut self,
67        handler: AsyncHandler<Args::S, R::Arguments, R::Response>,
68    ) -> Self {
69        self.req_handlers.insert(
70            R::COMMAND,
71            Box::new(move |s, _req_id, req| erased_response(handler(s, from_json(req)?))),
72        );
73        self
74    }
75}
76
77#[cfg(feature = "system")]
78impl<Args: Initializer> LsDriver<DapMessage, Args>
79where
80    Args::S: 'static,
81{
82    /// Starts the debug adaptor on the given connection.
83    ///
84    /// If `is_replay` is true, the server will wait for all pending requests to
85    /// finish before exiting. This is useful for testing the language server.
86    ///
87    /// See [`transport::MirrorArgs`] for information about the record-replay
88    /// feature.
89    pub fn start(
90        &mut self,
91        inbox: TConnectionRx<DapMessage>,
92        is_replay: bool,
93    ) -> anyhow::Result<()> {
94        let res = self.start_(inbox);
95
96        if is_replay {
97            let client = self.client.clone();
98            let _ = std::thread::spawn(move || {
99                let since = tinymist_std::time::Instant::now();
100                let timeout = std::env::var("REPLAY_TIMEOUT")
101                    .ok()
102                    .and_then(|s| s.parse().ok())
103                    .unwrap_or(60);
104                client.handle.block_on(async {
105                    while client.has_pending_requests() {
106                        if since.elapsed().as_secs() > timeout {
107                            log::error!("replay timeout reached, {timeout}s");
108                            client.begin_panic();
109                        }
110
111                        tokio::time::sleep(tinymist_std::time::Duration::from_millis(10)).await;
112                    }
113                })
114            })
115            .join();
116        }
117
118        res
119    }
120
121    /// Starts the debug adaptor on the given connection.
122    pub fn start_(&mut self, inbox: TConnectionRx<DapMessage>) -> anyhow::Result<()> {
123        use EventOrMessage::*;
124
125        while let Ok(msg) = inbox.recv() {
126            let loop_start = tinymist_std::time::now();
127            match msg {
128                Evt(event) => {
129                    let Some(event_handler) = self.events.get(&event.as_ref().type_id()) else {
130                        log::warn!("unhandled event: {:?}", event.as_ref().type_id());
131                        continue;
132                    };
133
134                    let s = match &mut self.state {
135                        State::Uninitialized(u) => ServiceState::Uninitialized(u.as_deref_mut()),
136                        State::Initializing(s) | State::Ready(s) => ServiceState::Ready(s),
137                        State::ShuttingDown => {
138                            log::warn!("server is shutting down");
139                            continue;
140                        }
141                    };
142
143                    event_handler(s, &self.client, event)?;
144                }
145                Msg(DapMessage::Request(req)) => {
146                    let client = self.client.clone();
147                    let req_id = (req.seq as i32).into();
148                    client.register_request(&req.command, &req_id, loop_start);
149                    let fut = client.schedule_tail(req_id.clone(), self.on_request(req_id, req));
150                    self.client.handle.spawn(fut);
151                }
152                Msg(DapMessage::Event(not)) => {
153                    self.on_event(loop_start, not)?;
154                }
155                Msg(
156                    DapMessage::Response(resp)
157                    | DapMessage::ResponseWithCommand(dap::ResponseWithCommand {
158                        response: resp,
159                        ..
160                    }),
161                ) => {
162                    let s = match &mut self.state {
163                        State::Ready(s) => s,
164                        _ => {
165                            log::warn!("server is not ready yet");
166                            continue;
167                        }
168                    };
169
170                    self.client.clone().complete_dap_request(s, resp)
171                }
172            }
173        }
174
175        log::warn!("client exited without proper shutdown sequence");
176        Ok(())
177    }
178
179    /// Registers and handles a request. This should only be called once per
180    /// incoming request.
181    fn on_request(&mut self, req_id: RequestId, req: dap::Request) -> ScheduleResult {
182        match (&mut self.state, &*req.command) {
183            (State::Uninitialized(args), dapts::request::Initialize::COMMAND) => {
184                // todo: what will happen if the request cannot be deserialized?
185                let params = serde_json::from_value::<Args::I>(req.arguments);
186                match params {
187                    Ok(params) => {
188                        let args = args.take().expect("already initialized");
189                        let (s, res) = args.initialize(params);
190                        self.state = State::Ready(s);
191                        res
192                    }
193                    Err(e) => just_result(Err(invalid_request(e))),
194                }
195            }
196            // (state, dap::events::Initialized::METHOD) => {
197            //     let mut s = State::ShuttingDown;
198            //     std::mem::swap(state, &mut s);
199            //     match s {
200            //         State::Initializing(s) => {
201            //             *state = State::Ready(s);
202            //         }
203            //         _ => {
204            //             std::mem::swap(state, &mut s);
205            //         }
206            //     }
207
208            //     let s = match state {
209            //         State::Ready(s) => s,
210            //         _ => {
211            //             log::warn!("server is not ready yet");
212            //             return Ok(());
213            //         }
214            //     };
215            //     handle(s, not)
216            // }
217            (State::Uninitialized(..) | State::Initializing(..), _) => {
218                just_result(Err(not_initialized()))
219            }
220            (_, dapts::request::Initialize::COMMAND) => {
221                just_result(Err(invalid_request("server is already initialized")))
222            }
223            // todo: generalize this
224            // (State::Ready(..), request::ExecuteCommand::METHOD) => {
225            // reschedule!(self.on_execute_command(req))
226            // }
227            (State::Ready(s), _) => 'serve_req: {
228                let method = req.command.as_str();
229
230                let is_disconnect = method == dapts::request::Disconnect::COMMAND;
231
232                let Some(handler) = self.requests.get(method) else {
233                    log::warn!("unhandled dap request: {method}");
234                    break 'serve_req just_result(Err(method_not_found()));
235                };
236
237                let resp = handler(s, req_id, req.arguments);
238
239                if is_disconnect {
240                    self.state = State::ShuttingDown;
241                }
242
243                resp
244            }
245            (State::ShuttingDown, _) => {
246                just_result(Err(invalid_request("server is shutting down")))
247            }
248        }
249    }
250
251    /// Handles an incoming event.
252    fn on_event(&mut self, received_at: Time, not: dap::Event) -> anyhow::Result<()> {
253        let track_id = self.next_not_id.fetch_add(1, Ordering::Relaxed);
254        self.client.hook.start_notification(track_id, &not.event);
255        let handle = |s,
256                      dap::Event {
257                          seq: _,
258                          event,
259                          body,
260                      }: dap::Event| {
261            let Some(handler) = self.notifications.get(event.as_str()) else {
262                log::warn!("unhandled event: {event}");
263                return Ok(());
264            };
265
266            let result = handler(s, body);
267            self.client
268                .hook
269                .stop_notification(track_id, &event, received_at, result);
270
271            Ok(())
272        };
273
274        match (&mut self.state, &*not.event) {
275            (State::Ready(state), _) => handle(state, not),
276            // todo: whether it is safe to ignore events
277            (State::Uninitialized(..) | State::Initializing(..), method) => {
278                log::warn!("server is not ready yet, while received event {method}");
279                Ok(())
280            }
281            (State::ShuttingDown, method) => {
282                log::warn!("server is shutting down, while received event {method}");
283                Ok(())
284            }
285        }
286    }
287}