@@ -2,7 +2,7 @@ use std::{convert::Infallible, process::Stdio, str::FromStr};
22
33use futures_util:: {
44 future:: { select, Either } ,
5- SinkExt , StreamExt ,
5+ stream , SinkExt , StreamExt ,
66} ;
77use tokio:: { fs, process:: Command } ;
88use url:: Url ;
@@ -108,7 +108,19 @@ async fn connected(
108108 let mut server_send = lsp:: framed:: writer ( server. stdin . take ( ) . unwrap ( ) ) ;
109109 let mut server_recv = lsp:: framed:: reader ( server. stdout . take ( ) . unwrap ( ) ) ;
110110 let ( mut client_send, client_recv) = ws. split ( ) ;
111- let mut client_recv = client_recv. filter_map ( filter_map_warp_ws_message) . boxed ( ) ;
111+ let client_recv = client_recv
112+ . filter_map ( filter_map_warp_ws_message)
113+ // Chain this with `Done` so we know when the client disconnects
114+ . chain ( stream:: once ( async { Ok ( Message :: Done ) } ) ) ;
115+ // Tick every 30s so we can ping the client to keep the connection alive
116+ let ticks = stream:: unfold (
117+ tokio:: time:: interval ( std:: time:: Duration :: from_secs ( 30 ) ) ,
118+ |mut interval| async move {
119+ interval. tick ( ) . await ;
120+ Some ( ( Ok ( Message :: Tick ) , interval) )
121+ } ,
122+ ) ;
123+ let mut client_recv = stream:: select ( client_recv, ticks) . boxed ( ) ;
112124
113125 let mut client_msg = client_recv. next ( ) ;
114126 let mut server_msg = server_recv. next ( ) ;
@@ -145,15 +157,26 @@ async fn connected(
145157 tracing:: info!( "received Close message" ) ;
146158 }
147159
160+ // Ping the client to keep the connection alive
161+ Some ( Ok ( Message :: Tick ) ) => {
162+ tracing:: debug!( "pinging the client" ) ;
163+ client_send. send ( warp:: ws:: Message :: ping ( vec ! [ ] ) ) . await ?;
164+ }
165+
166+ // Connection closed
167+ Some ( Ok ( Message :: Done ) ) => {
168+ tracing:: info!( "connection closed" ) ;
169+ break ;
170+ }
171+
148172 // WebSocket Error
149173 Some ( Err ( err) ) => {
150174 tracing:: error!( "websocket error: {}" , err) ;
151175 }
152176
153- // Connection closed
154177 None => {
155- tracing :: info! ( "connection closed" ) ;
156- break ;
178+ // Unreachable because of the interval stream
179+ unreachable ! ( "should never yield None" ) ;
157180 }
158181 }
159182
@@ -206,13 +229,19 @@ async fn connected(
206229}
207230
208231// Type to describe a message from the client conveniently.
232+ #[ allow( clippy:: large_enum_variant) ]
209233enum Message {
210234 // Valid LSP message
211235 Message ( lsp:: Message ) ,
212236 // Invalid JSON
213237 Invalid ( String ) ,
214238 // Close message
215239 Close ,
240+ // Ping the client to keep the connection alive.
241+ // Note that this is from the interval stream and not actually from client.
242+ Tick ,
243+ // Client disconnected. Necessary because the combined stream is infinite.
244+ Done ,
216245}
217246
218247// Parse the message and ignore anything we don't care.
0 commit comments