123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)(* <O___,, * (see CREDITS file for the list of authors) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)(************************************************************************)(* Coq Language Server Protocol *)(* Copyright 2019 MINES ParisTech -- Dual License LGPL 2.1 / GPL3+ *)(* Copyright 2019-2022 Inria -- Dual License LGPL 2.1 / GPL3+ *)(* Written by: Emilio J. Gallego Arias *)(************************************************************************)moduleF=FormatmoduleJ=Yojson.Safeletfn=ref(fun_->())letset_log_fnf=fn:=fletread_raw_messageic=letcl=input_lineicinletsin=Scanf.Scanning.from_stringclinletraw_obj=Scanf.bscanfsin"Content-Length: %d\r"(funsize->letbuf=Bytes.createsizein(* Consume the second \r\n or header *)letohdr=input_lineicin(* If the second line is a return, then no more headers *)let()=ifohdr.[0]='\r'then()else(* Fixme (or use ocaml-lsp) Skip the Content-type header *)ignore(input_lineic)inreally_inputicbuf0size;Bytes.to_stringbuf)inJ.from_stringraw_objletread_raw_messageic=trySome(Ok(read_raw_messageic))with(* if the end of input is encountered while some more characters are needed to
read the current conversion specification, or the lsp server closes *)|End_of_file->None(* if the input does not match the format. *)|Scanf.Scan_failuremsg(* if a conversion to a number is not possible. *)|Failuremsg(* if the format string is invalid. *)|Invalid_argumentmsg->Some(Errormsg)letmut=Mutex.create()(* This needs a fix as to log protocol stuff not using the protocol *)letlog=ref(fun__->())letsend_jsonfmtobj=Mutex.lockmut;ifFleche.Debug.sendthen!log"send"obj;letmsg=if!Fleche.Config.v.pp_jsonthenF.asprintf"%a"J.(pretty_print~std:true)objelseJ.to_stringobj^"\n"inletsize=String.lengthmsginF.fprintffmt"Content-Length: %d\r\n\r\n%s%!"sizemsg;Mutex.unlockmutletsend_messagefmtmessage=send_jsonfmt(Base.Message.to_yojsonmessage)(** Logging *)moduleTraceValue=structtypet=|Off|Messages|Verboseletof_string=function|"messages"->OkMessages|"verbose"->OkVerbose|"off"->OkOff|v->Error("TraceValue.parse: "^v)letto_string=function|Off->"off"|Messages->"messages"|Verbose->"verbose"endlettrace_value=refTraceValue.Offletset_trace_valuevalue=trace_value:=valuemoduleLvl=struct(* 1-5 *)typet=Fleche.Io.Level.t=|Error|Warning|Info|Log|Debugletto_int=function|Error->1|Warning->2|Info->3|Log->4|Debug->5endmoduleMessageParams=structletmethod_="window/logMessage"typet={type_:int[@key"type"];message:string}[@@derivingyojson]endletmk_logMessage~type_~message=letmoduleM=MessageParamsinletmethod_=M.method_inletparams=M.({type_;message}|>to_yojson|>Yojson.Safe.Util.to_assoc)inBase.Notification.make~method_~params()letlogMessage~lvl~message=lettype_=Lvl.to_intlvlinmk_logMessage~type_~message|>!fnletlogMessageInt~lvl~message=mk_logMessage~type_:lvl~message|>!fnmoduleTraceParams=structletmethod_="$/logTrace"typet={message:string;verbose:stringoption[@defaultNone]}[@@derivingyojson]endletmk_logTrace~message~extra=letmoduleM=TraceParamsinletmethod_=M.method_inletverbose=match(!trace_value,extra)with|Verbose,Someextra->Someextra|_->Noneinletparams=M.({message;verbose}|>to_yojson|>Yojson.Safe.Util.to_assoc)inBase.Notification.make~method_~params()letlogTrace~message~extra=mk_logTrace~message~extra|>!fn(* Disabled for now, see comment above *)(* let () = log := trace_object *)(** Misc helpers *)letread_messageic=matchread_raw_messageicwith|None->None(* EOF *)|Some(Okcom)->ifFleche.Debug.readthen!log"read"com;Some(Base.Message.of_yojsoncom)|Some(Errorerr)->Some(Errorerr)