123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687openCoreopenAsyncopenVcamlletwith_process_cleanup~namepid~f=letreap=ref(`Need_to_reap`Impatient)inletprint_and_returnexit_or_signal=print_s[%message(sprintf"%s exited"name)(exit_or_signal:Unix.Exit_or_signal.t)];return()inMonitor.protect(fun()->Deferred.map(f())~f:(Ref.(:=)reap))~finally:(fun()->match!reapwith|`Already_reapedexit_or_signal->print_and_returnexit_or_signal|`Need_to_reappatience->letwaitpid=Unix.waitpidpidinlettimeout=matchpatiencewith|`Impatient->Time_float.Span.zero|`Patient->Time_float.Span.of_int_sec20in(match%bindwith_timeouttimeoutwaitpidwith|`Resultexit_or_signal->print_and_returnexit_or_signal|`Timeout->Signal_unix.send_iSignal.term(`Pidpid);waitpid>>=print_and_return));;letspin_until_nvim_creates_socket_filepid~socket=Deferred.repeat_until_finished1(funattempt->matchattemptwith|10000->return(`Finished`Socket_missing)|_->(matchCore_unix.wait_nohang(`Pidpid)with|Some(_,exit_or_signal)->return(`Finished(`Nvim_crashedexit_or_signal))|None->(match%bindSys.file_exists_exnsocketwith|true->return(`Finished`Socket_created)|false->let%bind()=Clock_ns.afterTime_ns.Span.millisecondinreturn(`Repeat(attempt+1)))));;(* We use [writefile] to enable Neovim to communicate from an atomic context that it is
about to enter a state after which it will not be able to communicate (e.g., because
it is exiting or because it will be uninterruptible for some reason). This function
lets our tests wait for Neovim to reach this point before proceeding. *)letwritefilefile~contents=Nvim.eval(sprintf"writefile(['%s'], '%s', 's')"contentsfile)~result_type:Integer|>Api_call.map~f:(function|Ok0->Ok()|Ok-1->Or_error.error_s[%message"[writefile]: write failed"(file:string)(contents:string)]|Okerror_code->Or_error.error_s[%message"[writefile]: unknown error code"(error_code:int)(file:string)(contents:string)]|Error_aserror->error);;(* This function lets us attempt to exit Neovim cleanly. It uses [writefile] to confirm
that we have reached the [quit] command. By trying to exit Neovim cleanly instead of
just sending it SIGTERM we can distinguish between cases where Neovim is able to handle
incoming commands successfully and cases where Neovim is unresponsive. *)letattempt_to_quit~tmp_dir~client=letfifo=tmp_dir^/"quit"inlet%bind()=Unix.mkfifofifoindon't_wait_for(run_join[%here]client([writefilefifo~contents:":q";Nvim.command"quit"]|>Api_call.Or_error.all_unit)|>Deferred.Or_error.ignore_m|>Deferred.Or_error.ok_exn);Deferred.any[Clock_ns.afterTime_ns.Span.second;(let%bindreader=Reader.open_filefifoinlet%bind(_:stringReader.Read_result.t)=Reader.read_linereaderinReader.closereader)];;