use std::io::{BufWriter, Write};
use std::net::TcpStream;
use std::sync::{Arc, Mutex, RwLock};
use std::{io, num};
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::workspace::Workspace;
pub(crate) static CLIENT: RwLock<Option<Arc<LspClient>>> = RwLock::new(None);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProgressMessage {
Begin { id: i32, title: String },
Report { id: i32, message: String },
End { id: i32 },
}
pub(crate) struct LspClient {
writer: Mutex<BufWriter<TcpStream>>,
}
impl LspClient {
pub(crate) fn connect(workspace: &Workspace) -> Result<Self, ConnectError> {
let port_path = super::lsp_port_path(workspace);
let port: u16 = std::fs::read_to_string(&port_path)
.map_err(ConnectError::ReadPort)?
.trim()
.parse()
.map_err(ConnectError::ParsePort)?;
let stream = TcpStream::connect(("127.0.0.1", port))
.map_err(|source| ConnectError::Connect { port, source })?;
Ok(Self {
writer: Mutex::new(BufWriter::new(stream)),
})
}
pub(crate) fn send(&self, msg: &ProgressMessage) {
if let Ok(mut w) = self.writer.lock() {
let _ = serde_json::to_writer(&mut *w, msg);
let _ = writeln!(w);
let _ = w.flush();
}
}
}
#[derive(Debug, Diagnostic, Error)]
pub(crate) enum ConnectError {
#[error("failed to read LSP port file")]
ReadPort(#[from] io::Error),
#[error("invalid port in LSP port file")]
ParsePort(#[from] num::ParseIntError),
#[error("failed to connect to lx-lsp at 127.0.0.1:{port}")]
Connect {
port: u16,
#[source]
source: io::Error,
},
}