turbo_static/
lsp_client.rs

1use std::{path::PathBuf, process, sync::mpsc};
2
3use lsp_server::Message;
4
5/// An LSP client for Rust Analyzer (RA) that launches it as a subprocess.
6pub struct RAClient {
7    /// Handle to the client
8    handle: process::Child,
9    sender: Option<mpsc::SyncSender<Message>>,
10    receiver: Option<mpsc::Receiver<Message>>,
11}
12
13impl RAClient {
14    /// Create a new LSP client for Rust Analyzer.
15    pub fn new() -> Self {
16        let stdin = process::Stdio::piped();
17        let stdout = process::Stdio::piped();
18        let stderr = process::Stdio::inherit();
19
20        let child = process::Command::new("rust-analyzer")
21            .stdin(stdin)
22            .stdout(stdout)
23            .stderr(stderr)
24            // .env("RA_LOG", "info")
25            .env("RUST_BACKTRACE", "1")
26            .spawn()
27            .expect("Failed to start RA LSP server");
28        Self {
29            handle: child,
30            sender: None,
31            receiver: None,
32        }
33    }
34
35    pub fn start(&mut self, folders: &[PathBuf]) {
36        let stdout = self.handle.stdout.take().unwrap();
37        let mut stdin = self.handle.stdin.take().unwrap();
38
39        let (writer_sender, writer_receiver) = mpsc::sync_channel::<Message>(0);
40        _ = std::thread::spawn(move || {
41            writer_receiver
42                .into_iter()
43                .try_for_each(|it| it.write(&mut stdin))
44        });
45
46        let (reader_sender, reader_receiver) = mpsc::sync_channel::<Message>(0);
47        _ = std::thread::spawn(move || {
48            let mut reader = std::io::BufReader::new(stdout);
49            while let Ok(Some(msg)) = Message::read(&mut reader) {
50                reader_sender
51                    .send(msg)
52                    .expect("receiver was dropped, failed to send a message");
53            }
54        });
55
56        self.sender = Some(writer_sender);
57        self.receiver = Some(reader_receiver);
58
59        let workspace_paths = folders
60            .iter()
61            .map(|p| std::fs::canonicalize(p).unwrap())
62            .map(|p| lsp_types::WorkspaceFolder {
63                name: p.file_name().unwrap().to_string_lossy().to_string(),
64                uri: lsp_types::Url::from_file_path(p).unwrap(),
65            })
66            .collect::<Vec<_>>();
67
68        _ = self.request(lsp_server::Request {
69            id: 1.into(),
70            method: "initialize".to_string(),
71            params: serde_json::to_value(lsp_types::InitializeParams {
72                workspace_folders: Some(workspace_paths),
73                process_id: Some(std::process::id()),
74                capabilities: lsp_types::ClientCapabilities {
75                    workspace: Some(lsp_types::WorkspaceClientCapabilities {
76                        workspace_folders: Some(true),
77                        ..Default::default()
78                    }),
79                    ..Default::default()
80                },
81                work_done_progress_params: lsp_types::WorkDoneProgressParams {
82                    work_done_token: Some(lsp_types::ProgressToken::String("prepare".to_string())),
83                },
84                // we use workspace_folders so root_path and root_uri can be
85                // empty
86                ..Default::default()
87            })
88            .unwrap(),
89        });
90
91        self.notify(lsp_server::Notification {
92            method: "initialized".to_string(),
93            params: serde_json::to_value(lsp_types::InitializedParams {}).unwrap(),
94        });
95    }
96
97    /// Send an LSP request to the server. This returns an option
98    /// in the case of an error such as the server being shut down
99    /// from pressing `Ctrl+C`.
100    pub fn request(&mut self, message: lsp_server::Request) -> Option<lsp_server::Response> {
101        tracing::debug!("sending {:?}", message);
102        self.sender
103            .as_mut()
104            .unwrap()
105            .send(Message::Request(message))
106            .ok()?;
107
108        loop {
109            match self.receiver.as_mut().unwrap().recv() {
110                Ok(lsp_server::Message::Response(response)) => {
111                    tracing::debug!("received {:?}", response);
112                    return Some(response);
113                }
114                Ok(m) => tracing::trace!("unexpected message: {:?}", m),
115                Err(_) => {
116                    tracing::trace!("error receiving message");
117                    return None;
118                }
119            }
120        }
121    }
122
123    pub fn notify(&mut self, message: lsp_server::Notification) {
124        self.sender
125            .as_mut()
126            .unwrap()
127            .send(Message::Notification(message))
128            .expect("failed to send message");
129    }
130}
131
132impl Drop for RAClient {
133    fn drop(&mut self) {
134        if self.sender.is_some() {
135            let Some(resp) = self.request(lsp_server::Request {
136                id: 1.into(),
137                method: "shutdown".to_string(),
138                params: serde_json::to_value(()).unwrap(),
139            }) else {
140                return;
141            };
142
143            if resp.error.is_none() {
144                tracing::info!("shutting down RA LSP server");
145                self.notify(lsp_server::Notification {
146                    method: "exit".to_string(),
147                    params: serde_json::to_value(()).unwrap(),
148                });
149                self.handle
150                    .wait()
151                    .expect("failed to wait for RA LSP server");
152                tracing::info!("shut down RA LSP server");
153            } else {
154                tracing::error!("failed to shutdown RA LSP server: {:#?}", resp);
155            }
156        }
157
158        self.sender = None;
159        self.receiver = None;
160    }
161}