use std::ops::ControlFlow; use std::time::Duration; use async_lsp::client_monitor::ClientProcessMonitorLayer; use async_lsp::concurrency::ConcurrencyLayer; use async_lsp::panic::CatchUnwindLayer; use async_lsp::router::Router; use async_lsp::server::LifecycleLayer; use async_lsp::tracing::TracingLayer; use async_lsp::{ClientSocket, LanguageClient, LanguageServer, ResponseError}; use futures::future::BoxFuture; use lsp_types::{ DidChangeConfigurationParams, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult, MarkedString, MessageType, OneOf, ServerCapabilities, ShowMessageParams, }; use tower::ServiceBuilder; use tracing::{info, Level}; struct ServerState { client: ClientSocket, counter: i32, } impl LanguageServer for ServerState { type Error = ResponseError; type NotifyResult = ControlFlow>; fn initialize( &mut self, params: InitializeParams, ) -> BoxFuture<'static, Result> { eprintln!("Initialize with {params:?}"); Box::pin(async move { Ok(InitializeResult { capabilities: ServerCapabilities { hover_provider: Some(HoverProviderCapability::Simple(true)), definition_provider: Some(OneOf::Left(true)), ..ServerCapabilities::default() }, server_info: None, }) }) } fn hover(&mut self, _: HoverParams) -> BoxFuture<'static, Result, Self::Error>> { let mut client = self.client.clone(); let counter = self.counter; Box::pin(async move { tokio::time::sleep(Duration::from_secs(1)).await; client .show_message(ShowMessageParams { typ: MessageType::INFO, message: "Hello LSP".into(), }) .unwrap(); Ok(Some(Hover { contents: HoverContents::Scalar(MarkedString::String(format!( "I am a hover text {counter}!" ))), range: None, })) }) } fn definition( &mut self, _: GotoDefinitionParams, ) -> BoxFuture<'static, Result, ResponseError>> { unimplemented!("Not yet implemented!"); } fn did_change_configuration( &mut self, _: DidChangeConfigurationParams, ) -> ControlFlow> { ControlFlow::Continue(()) } } struct TickEvent; impl ServerState { fn new_router(client: ClientSocket) -> Router { let mut router = Router::from_language_server(Self { client, counter: 0 }); router.event(Self::on_tick); router } fn on_tick(&mut self, _: TickEvent) -> ControlFlow> { info!("tick"); self.counter += 1; ControlFlow::Continue(()) } } #[tokio::main(flavor = "current_thread")] async fn main() { let (server, _) = async_lsp::MainLoop::new_server(|client| { tokio::spawn({ let client = client.clone(); async move { let mut interval = tokio::time::interval(Duration::from_secs(1)); loop { interval.tick().await; if client.emit(TickEvent).is_err() { break; } } } }); ServiceBuilder::new() .layer(TracingLayer::default()) .layer(LifecycleLayer::default()) .layer(CatchUnwindLayer::default()) .layer(ConcurrencyLayer::default()) .layer(ClientProcessMonitorLayer::new(client.clone())) .service(ServerState::new_router(client)) }); tracing_subscriber::fmt() .with_max_level(Level::INFO) .with_ansi(false) .with_writer(std::io::stderr) .init(); // Prefer truly asynchronous piped stdin/stdout without blocking tasks. #[cfg(unix)] let (stdin, stdout) = ( async_lsp::stdio::PipeStdin::lock_tokio().unwrap(), async_lsp::stdio::PipeStdout::lock_tokio().unwrap(), ); // Fallback to spawn blocking read/write otherwise. #[cfg(not(unix))] let (stdin, stdout) = ( tokio_util::compat::TokioAsyncReadCompatExt::compat(tokio::io::stdin()), tokio_util::compat::TokioAsyncWriteCompatExt::compat_write(tokio::io::stdout()), ); server.run_buffered(stdin, stdout).await.unwrap(); }