char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
c7a9569
main
1use crate ::{ BACKEND_VERSION , Result , wire}; 2use serde:: de:: DeserializeOwned ; 3use serde_json::{ Value , json}; 4use std::{ 5 collections:: HashMap , 6 path:: Path , 7 process:: Stdio , 8 sync::{ Arc , Mutex }, 9 time:: Duration , 10}; 11use tokio::{ 12 io:: BufReader , 13 process::{ Child , Command }, 14 sync::{ mpsc, oneshot}, 15 task::{ JoinHandle , JoinSet }, 16}; 17 18pub trait Callbacks : Send +Sync +' static { 19fn call ( & self , method : & str , params : Value ) ->Result < Value >; 20} 21 22type Reply =Result < Value >; 23# [ derive ( Default )] 24struct State { 25next : u64 , 26failed : Option < String >, 27pending : HashMap < u64 , oneshot:: Sender < Reply >>, 28} 29impl State { 30fn fail ( & mut self , error : String ) { 31self . failed . get_or_insert ( error. clone ()); 32for ( _, reply) in self . pending . drain () { 33let _ = reply. send ( Err ( error. clone (). into ())); 34} 35} 36} 37 38const WRITER_CAPACITY : usize =16 ; 39 40type Writer = mpsc:: Sender < WriteRequest >; 41struct WriteRequest { 42value : Value , 43result : oneshot:: Sender < Result <()>>, 44} 45 46async fn send ( writer : & Writer , value : Value ) ->Result <()> { 47let ( result, rx) = oneshot:: channel (); 48 writer. send ( WriteRequest { value, result}). await ?; 49 rx. await ? 50} 51 52async fn writer_loop < W : tokio:: io:: AsyncWrite +Unpin >( 53mut output : W , 54mut requests : mpsc:: Receiver < WriteRequest >, 55state : Arc < Mutex < State >>, 56) { 57while let Some ( request) = requests. recv (). await { 58let result = wire:: write ( & mut output, request. value ). await ; 59let failed = result. as_ref (). err (). map ( ToString :: to_string); 60let _ = request. result . send ( result); 61if let Some ( error) = failed{ 62 state. lock (). unwrap (). fail ( error); 63break ; 64} 65} 66} 67 68pub struct Backend { 69child : Child , 70state : Arc < Mutex < State >>, 71writer : Option < Writer >, 72writer_task : Option < JoinHandle <()>>, 73reader : Option < JoinHandle <()>>, 74} 75 76impl Backend { 77pub async fn start ( path : & Path , host : Arc < dyn Callbacks >) ->Result < Self > { 78let version = tokio:: time:: timeout ( 79Duration :: from_secs ( 10 ), 80Command :: new ( path) 81. env_clear () 82. kill_on_drop ( true ) 83. arg ( "--version" ) 84. output (), 85) 86. await ??; 87if !version. status . success () 88 ||String :: from_utf8_lossy ( & version. stdout ). trim () 89 !=format! ( "Version {BACKEND_VERSION}" ) 90{ 91return Err ( format! ( "Expected TypeScript {BACKEND_VERSION} native executable" ). into ()); 92} 93let mut child =Command :: new ( path) 94. env_clear () 95. kill_on_drop ( true ) 96. args ([ 97"--api" , 98"--async" , 99"--cwd" , 100"/avril-lsp" , 101"--callbacks" , 102concat! ( 103"readFile,fileExists,directoryExists,getAccessibleEntries," , 104"realpath,stat:fakeStat,writeFile:error,removeFile:error" 105), 106]) 107. stdin ( Stdio :: piped ()) 108. stdout ( Stdio :: piped ()) 109. stderr ( Stdio :: inherit ()) 110. spawn () ?; 111let stdin = child. stdin . take (). ok_or ( "No backend stdin" ) ?; 112let ( writer, requests) = mpsc:: channel ::< WriteRequest >( WRITER_CAPACITY ); 113let mut stdout =BufReader :: new ( child. stdout . take (). ok_or ( "No backend stdout" ) ?); 114let state =Arc :: new ( Mutex :: new ( State :: default ())); 115let writer_task = tokio:: spawn ( writer_loop ( stdin, requests, state. clone ())); 116let reader = tokio:: spawn ({ 117let state = state. clone (); 118let writer = writer. clone (); 119async move { 120let mut callbacks =JoinSet :: new (); 121let result: Result <()> =async { 122loop { 123 tokio:: select!{ 124 completed = callbacks. join_next (), if !callbacks. is_empty () =>{ completed. ok_or ( "Missing callback task" ) ???; } 125 message = wire:: read ( & mut stdout) =>{ 126let message = message?. ok_or ( "TypeScript backend closed stdout" ) ?; 127if let Some ( method) = message[ "method" ]. as_str () { 128if let Some ( id) = message. get ( "id" ). cloned () { 129let method = method. to_owned (); 130let host = host. clone (); 131let writer = writer. clone (); 132 callbacks. spawn ( async move { 133let response =match host. call ( & method, message[ "params" ]. clone ()) { 134Ok ( result) =>json! ({ "id" : id, "result" : result}), 135Err ( error) =>json! ({ "id" : id, " error " :{ " code " : -32603 , " message " : error. to_string ()}}), 136}; 137send ( & writer, response). await 138}); 139} 140} else if let Some ( id) = message[ "id" ]. as_u64 () { 141if let Some ( reply) = state. lock (). unwrap (). pending . remove ( & id) { 142let result =if let Some ( error) = message. get ( "error" ) { 143Err ( serde_json:: from_value ::< wire:: RpcError >( error. clone ()) ?. into ()) 144} else { 145Ok ( message[ "result" ]. clone ()) 146}; 147let _ = reply. send ( result); 148} 149} else { return Err ( "Invalid backend response ID" . into ()); } 150} 151} 152} 153}. await ; 154 callbacks. abort_all (); 155 state. lock (). unwrap (). fail ( 156 result 157. err () 158. map_or ( "Backend reader stopped" . into (), |e| e. to_string ()), 159); 160} 161}); 162Ok ( Self { 163 child, 164 state, 165writer : Some ( writer), 166writer_task : Some ( writer_task), 167reader : Some ( reader), 168}) 169} 170 171pub fn failed ( & self ) ->bool { 172self . state . lock (). unwrap (). failed . is_some () 173} 174 175pub async fn call < T : DeserializeOwned >( & self , method : & str , params : Value ) ->Result < T > { 176let ( tx, rx) = oneshot:: channel (); 177let id ={ 178let mut state =self . state . lock (). unwrap (); 179if let Some ( error) =& state. failed { 180return Err ( error. clone (). into ()); 181} 182 state. next +=1 ; 183let id = state. next ; 184 state. pending . insert ( id, tx); 185 id 186}; 187// Remove registrations even if the editor cancels this future. 188struct Registration ( Arc < Mutex < State >>, u64 , Writer ); 189impl Drop for Registration { 190fn drop ( & mut self ) { 191if self . 0 . lock (). unwrap (). pending . remove ( & self . 1 ). is_some () { 192let ( result, _) = oneshot:: channel (); 193let _ =self . 2 . try_send ( WriteRequest { 194value : json! ({ "method" : "$/ cancelRequest " , "params" :{ " id " : self . 1 }}), 195 result, 196}); 197} 198} 199} 200let writer =self . writer . as_ref (). ok_or ( "Backend stdin closed" ) ?. clone (); 201let _registration =Registration ( self . state . clone (), id, writer. clone ()); 202let operation =async { 203send ( & writer, json! ({ "id" : id, "method" : method, "params" : params})). await ?; 204let value = rx. await ??; 205Ok ::< T , crate :: Error >( serde_json:: from_value ( value) ?) 206}; 207match tokio:: time:: timeout ( Duration :: from_secs ( 30 ), operation). await { 208Ok ( result) => result, 209Err ( _) =>{ 210self . state 211. lock () 212. unwrap () 213. fail ( format! ( "TypeScript API timed out: {method}" )); 214Err ( format! ( "TypeScript API timed out: {method}" ). into ()) 215} 216} 217} 218 219pub async fn close ( & mut self ) ->Result <()> { 220self . state 221. lock () 222. unwrap () 223. fail ( "Backend shutting down" . into ()); 224if let Some ( reader) =self . reader . take () { 225 reader. abort (); 226let _ = reader. await ; 227} 228self . writer . take (); 229if let Some ( mut writer_task) =self . writer_task . take () { 230match tokio:: time:: timeout ( Duration :: from_secs ( 2 ), & mut writer_task). await { 231Ok ( Ok (())) =>{} 232Ok ( Err ( error)) =>return Err ( error. into ()), 233Err ( _) =>{ 234 writer_task. abort (); 235let _ = writer_task. await ; 236self . child . kill (). await ?; 237self . child . wait (). await ?; 238return Err ( "TypeScript backend writer did not shut down" . into ()); 239} 240} 241} 242match tokio:: time:: timeout ( Duration :: from_secs ( 2 ), self . child . wait ()). await { 243Ok ( status) =>{ 244if status?. success () { 245Ok (()) 246} else { 247Err ( "TypeScript backend exited unsuccessfully" . into ()) 248} 249} 250Err ( _) =>{ 251self . child . kill (). await ?; 252self . child . wait (). await ?; 253Err ( "TypeScript backend did not shut down" . into ()) 254} 255} 256} 257} 258impl Drop for Backend { 259fn drop ( & mut self ) { 260if let Some ( reader) =& self . reader { 261 reader. abort (); 262} 263if let Some ( writer) =& self . writer_task { 264 writer. abort (); 265} 266} 267} 268 269# [ cfg ( test )] 270mod tests{ 271use super :: * ; 272use tokio:: io::{ AsyncReadExt , BufReader }; 273 274# [ tokio :: test ] 275async fn canceled_frame_does_not_corrupt_next_frame () { 276let state =Arc :: new ( Mutex :: new ( State :: default ())); 277let ( writer, requests) = mpsc:: channel ( WRITER_CAPACITY ); 278let ( backend, mut output) = tokio:: io:: duplex ( 8 ); 279let task = tokio:: spawn ( writer_loop ( backend, requests, state)); 280 281let large =json! ({ "id" : 1 , "method" : "large" , " params " :{ " text " : "x" . repeat ( 1024 * 1024 )}}); 282let first_call = tokio:: spawn ({ 283let writer = writer. clone (); 284async move { send ( & writer, large). await } 285}); 286 287let mut prefix =[ 0 ; 8 ]; 288 output. read_exact ( & mut prefix). await . unwrap (); 289 first_call. abort (); 290let _ = first_call. await ; 291 292let ( second_result, second_rx) = oneshot:: channel (); 293 writer 294. send ( WriteRequest { 295value : json! ({ "id" : 2 , "method" : "query" , " params " :{}}), 296result : second_result, 297}) 298. await 299. unwrap (); 300drop ( writer); 301let mut bytes = prefix. to_vec (); 302let ( second_result, read_result) = tokio:: join!( second_rx, output. read_to_end ( & mut bytes)); 303 second_result. unwrap (). unwrap (); 304 read_result. unwrap (); 305 task. await . unwrap (); 306let mut input =BufReader :: new ( bytes. as_slice ()); 307let first = wire:: read ( & mut input). await . unwrap (). unwrap (); 308let second = wire:: read ( & mut input). await . unwrap (). unwrap (); 309assert_eq! ( first[ "id" ], 1 ); 310assert_eq! ( second[ "id" ], 2 ); 311assert_eq! ( second[ "method" ], "query" ); 312} 313}