| use std::{ |
| io::{self, stdin, stdout}, |
| thread, |
| }; |
| |
| use log::debug; |
| |
| use crossbeam_channel::{bounded, Receiver, Sender}; |
| |
| use crate::Message; |
| |
| /// Creates an LSP connection via stdio. |
| pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThreads) { |
| let (writer_sender, writer_receiver) = bounded::<Message>(0); |
| let writer = thread::Builder::new() |
| .name("LspServerWriter".to_owned()) |
| .spawn(move || { |
| let stdout = stdout(); |
| let mut stdout = stdout.lock(); |
| writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout)) |
| }) |
| .unwrap(); |
| let (reader_sender, reader_receiver) = bounded::<Message>(0); |
| let reader = thread::Builder::new() |
| .name("LspServerReader".to_owned()) |
| .spawn(move || { |
| let stdin = stdin(); |
| let mut stdin = stdin.lock(); |
| while let Some(msg) = Message::read(&mut stdin)? { |
| let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit()); |
| |
| debug!("sending message {:#?}", msg); |
| if let Err(e) = reader_sender.send(msg) { |
| return Err(io::Error::new(io::ErrorKind::Other, e)); |
| } |
| |
| if is_exit { |
| break; |
| } |
| } |
| Ok(()) |
| }) |
| .unwrap(); |
| let threads = IoThreads { reader, writer }; |
| (writer_sender, reader_receiver, threads) |
| } |
| |
| // Creates an IoThreads |
| pub(crate) fn make_io_threads( |
| reader: thread::JoinHandle<io::Result<()>>, |
| writer: thread::JoinHandle<io::Result<()>>, |
| ) -> IoThreads { |
| IoThreads { reader, writer } |
| } |
| |
| pub struct IoThreads { |
| reader: thread::JoinHandle<io::Result<()>>, |
| writer: thread::JoinHandle<io::Result<()>>, |
| } |
| |
| impl IoThreads { |
| pub fn join(self) -> io::Result<()> { |
| match self.reader.join() { |
| Ok(r) => r?, |
| Err(err) => std::panic::panic_any(err), |
| } |
| match self.writer.join() { |
| Ok(r) => r, |
| Err(err) => { |
| std::panic::panic_any(err); |
| } |
| } |
| } |
| } |