auto_lsp_server/
main_loop.rs

1/*
2This file is part of auto-lsp.
3Copyright (C) 2025 CLAUZEL Adrien
4
5auto-lsp is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <http://www.gnu.org/licenses/>
17*/
18
19use std::panic::RefUnwindSafe;
20
21use crate::{notification_registry::NotificationRegistry, request_registry::RequestRegistry};
22
23use super::Session;
24use anyhow::Error;
25use crossbeam_channel::select;
26use lsp_server::Message;
27
28#[derive(Debug)]
29pub(crate) enum Task {
30    Response(lsp_server::Response),
31    NotificationError(Error),
32}
33
34impl<Db: salsa::Database + Clone + Send + RefUnwindSafe> Session<Db> {
35    /// Main loop of the LSP server, backed by [`lsp-server`] and [`crossbeam-channel`] crates.
36    pub fn main_loop<'a>(
37        &'a mut self,
38        req_registry: &'a RequestRegistry<Db>,
39        not_registry: &'a NotificationRegistry<Db>,
40    ) -> anyhow::Result<()> {
41        loop {
42            select! {
43                recv(self.connection.receiver) -> msg => {
44                    match msg? {
45                        Message::Request(req) => {
46                            if self.connection.handle_shutdown(&req)? {
47                                return Ok(());
48                            };
49
50                            self.req_queue.incoming.register(req.id.clone(), req.method.clone());
51
52                            if let Some(method) = req_registry.get(&req) {
53                                RequestRegistry::exec(self, method, req);
54                            } else if let Some(method) = req_registry.get_sync_mut(&req) {
55                                RequestRegistry::exec_sync_mut(self, method, req)?;
56                            } else {
57                                RequestRegistry::complete(self,
58                                    RequestRegistry::<Db>::request_mismatch(req.id.clone(), anyhow::format_err!("Unknown request: {}", req.method))
59                                )?
60                            }
61                        }
62                        Message::Notification(not) => {
63                            if let Some(method) = not_registry.get(&not) {
64                                NotificationRegistry::exec(self, method, not);
65                            } else if let Some(method) = not_registry.get_sync_mut(&not) {
66                                NotificationRegistry::exec_sync_mut(self, method, not)?;
67                            } else {
68                                NotificationRegistry::handle_error(self, anyhow::format_err!("Unknown notification: {}", not.method))?
69                            }
70                        }
71                        Message::Response(_) => {}
72                    }
73                },
74                recv(self.task_rx) -> task => {
75                    match task? {
76                        Task::Response(resp) => RequestRegistry::complete(self, resp)?,
77                        Task::NotificationError(err) => NotificationRegistry::handle_error(self, err)?,
78                    }
79                }
80            }
81        }
82    }
83
84    /// Send a notification to the client.
85    pub fn send_notification<N: lsp_types::notification::Notification>(
86        &self,
87        params: N::Params,
88    ) -> anyhow::Result<()> {
89        let params = serde_json::to_value(&params)?;
90        let n = lsp_server::Notification {
91            method: N::METHOD.into(),
92            params,
93        };
94        self.connection.sender.send(Message::Notification(n))?;
95        Ok(())
96    }
97}