pub mod cmd_args;
mod context;
mod handlers;
mod logger;
mod meta_text;
mod util;
use crate::handlers::{
initialized_handler, on_notification_handler, on_request_handler, on_response_handler,
};
pub use clap::Parser;
pub use cmd_args::*;
use handlers::server_capabilities;
use lsp_server::{Connection, Message, Response};
use lsp_types::InitializeParams;
use std::sync::Arc;
use std::{env, error::Error};
use tokio::sync::{mpsc, oneshot};
#[macro_use]
extern crate rust_i18n;
rust_i18n::i18n!("./locales", fallback = "en");
const CRATE_NAME: &str = env!("CARGO_PKG_NAME");
const CRATE_VERSION: &str = env!("CARGO_PKG_VERSION");
pub struct AsyncConnection {
connection: Arc<Connection>,
receiver: mpsc::UnboundedReceiver<Message>,
_receiver_task: tokio::task::JoinHandle<()>,
}
impl AsyncConnection {
pub fn from_sync(connection: Connection) -> Self {
let (tx, rx) = mpsc::unbounded_channel();
let connection = Arc::new(connection);
let connection_clone = connection.clone();
let receiver_task = tokio::task::spawn_blocking(move || {
for msg in &connection_clone.receiver {
if tx.send(msg).is_err() {
break; }
}
});
Self {
connection,
receiver: rx,
_receiver_task: receiver_task,
}
}
pub async fn recv(&mut self) -> Option<Message> {
self.receiver.recv().await
}
pub fn send(&self, msg: Message) -> Result<(), Box<dyn Error + Send + Sync>> {
self.connection
.sender
.send(msg)
.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
}
pub async fn handle_shutdown(
&mut self,
req: &lsp_server::Request,
) -> Result<bool, Box<dyn Error + Send + Sync>> {
if req.method != "shutdown" {
return Ok(false);
}
let resp = Response::new_ok(req.id.clone(), ());
let _ = self.connection.sender.send(resp.into());
match tokio::time::timeout(std::time::Duration::from_secs(30), self.receiver.recv()).await {
Ok(Some(Message::Notification(n))) if n.method == "exit" => (),
Ok(Some(msg)) => {
return Err(Box::new(ExitError(format!(
"unexpected message during shutdown: {msg:?}"
))));
}
Ok(None) => {
return Err(Box::new(ExitError(
"channel closed while waiting for exit notification".to_owned(),
)));
}
Err(_) => {
return Err(Box::new(ExitError(
"timed out waiting for exit notification".to_owned(),
)));
}
}
Ok(true)
}
}
pub struct ExitError(pub String);
impl std::fmt::Debug for ExitError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ExitError: {}", self.0)
}
}
impl std::fmt::Display for ExitError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Error for ExitError {}
struct ServerMessageProcessor {
initialization_complete: bool,
pending_messages: Vec<Message>,
init_rx: oneshot::Receiver<()>,
}
impl ServerMessageProcessor {
fn new(init_rx: oneshot::Receiver<()>) -> Self {
Self {
initialization_complete: false,
pending_messages: Vec::new(),
init_rx,
}
}
fn can_process_during_init(&self, msg: &Message) -> bool {
match msg {
Message::Response(_) => true,
Message::Notification(notify) => {
matches!(
notify.method.as_str(),
"workspace/didChangeConfiguration" | "$/cancelRequest" | "initialized"
)
}
Message::Request(_) => false,
}
}
async fn process_message(
&mut self,
msg: Message,
connection: &mut AsyncConnection,
server_context: &mut context::ServerContext,
) -> Result<bool, Box<dyn Error + Sync + Send>> {
self.handle_message(msg, connection, server_context).await
}
fn check_initialization_complete(&mut self) -> Result<bool, Box<dyn Error + Sync + Send>> {
if !self.initialization_complete {
match self.init_rx.try_recv() {
Ok(_) => {
self.initialization_complete = true;
return Ok(true); }
Err(oneshot::error::TryRecvError::Empty) => {
}
Err(oneshot::error::TryRecvError::Closed) => {
self.initialization_complete = true;
return Ok(true); }
}
}
Ok(false)
}
async fn process_pending_messages(
&mut self,
connection: &mut AsyncConnection,
server_context: &mut context::ServerContext,
) -> Result<bool, Box<dyn Error + Sync + Send>> {
let messages = std::mem::take(&mut self.pending_messages);
for msg in messages {
if self.handle_message(msg, connection, server_context).await? {
return Ok(true); }
}
Ok(false)
}
async fn handle_message(
&self,
msg: Message,
connection: &mut AsyncConnection,
server_context: &mut context::ServerContext,
) -> Result<bool, Box<dyn Error + Sync + Send>> {
match msg {
Message::Request(req) => {
if connection.handle_shutdown(&req).await? {
server_context.close().await;
return Ok(true); }
on_request_handler(req, server_context).await?;
}
Message::Notification(notify) => {
on_notification_handler(notify, server_context).await?;
}
Message::Response(response) => {
on_response_handler(response, server_context).await?;
}
}
Ok(false)
}
}
#[allow(unused)]
pub async fn run_ls(cmd_args: CmdArgs) -> Result<(), Box<dyn Error + Sync + Send>> {
let (connection, threads) = match cmd_args.communication {
cmd_args::Communication::Stdio => Connection::stdio(),
cmd_args::Communication::Tcp => {
let port = cmd_args.port;
let ip = cmd_args.ip.clone();
let addr = (ip.as_str(), port);
Connection::listen(addr).unwrap()
}
};
let (id, params) = connection.initialize_start()?;
let initialization_params: InitializeParams = serde_json::from_value(params).unwrap();
let server_capbilities = server_capabilities(&initialization_params.capabilities);
let initialize_data = serde_json::json!({
"capabilities": server_capbilities,
"serverInfo": {
"name": CRATE_NAME,
"version": CRATE_VERSION
}
});
connection.initialize_finish(id, initialize_data)?;
let mut async_connection = AsyncConnection::from_sync(connection);
main_loop(async_connection, initialization_params, cmd_args).await?;
threads.join()?;
eprintln!("Server shutting down.");
Ok(())
}
struct LspServer {
connection: AsyncConnection,
server_context: context::ServerContext,
processor: ServerMessageProcessor,
}
impl LspServer {
fn new(
connection: AsyncConnection,
params: &InitializeParams,
init_rx: oneshot::Receiver<()>,
) -> Self {
let server_context = context::ServerContext::new(
Connection {
sender: connection.connection.sender.clone(),
receiver: connection.connection.receiver.clone(),
},
params.capabilities.clone(),
);
Self {
connection,
server_context,
processor: ServerMessageProcessor::new(init_rx),
}
}
async fn run(mut self) -> Result<(), Box<dyn Error + Sync + Send>> {
self.wait_for_initialization().await?;
if self
.processor
.process_pending_messages(&mut self.connection, &mut self.server_context)
.await?
{
self.server_context.close().await;
return Ok(()); }
while let Some(msg) = self.connection.recv().await {
if self
.processor
.process_message(msg, &mut self.connection, &mut self.server_context)
.await?
{
break; }
}
self.server_context.close().await;
Ok(())
}
async fn wait_for_initialization(&mut self) -> Result<(), Box<dyn Error + Sync + Send>> {
loop {
if self.processor.check_initialization_complete()? {
break; }
match tokio::time::timeout(
tokio::time::Duration::from_millis(50),
self.connection.recv(),
)
.await
{
Ok(Some(msg)) => {
if self.processor.can_process_during_init(&msg) {
self.processor
.handle_message(msg, &mut self.connection, &mut self.server_context)
.await?;
} else {
self.processor.pending_messages.push(msg);
}
}
Ok(None) => {
return Ok(());
}
Err(_) => {
continue;
}
}
}
Ok(())
}
}
async fn main_loop(
connection: AsyncConnection,
params: InitializeParams,
cmd_args: CmdArgs,
) -> Result<(), Box<dyn Error + Sync + Send>> {
let (init_tx, init_rx) = oneshot::channel::<()>();
let server = LspServer::new(connection, ¶ms, init_rx);
let server_context_snapshot = server.server_context.snapshot();
tokio::spawn(async move {
initialized_handler(server_context_snapshot, params, cmd_args).await;
let _ = init_tx.send(());
});
server.run().await
}