mirror of
https://github.com/c-cube/linol.git
synced 2025-12-06 11:15:46 -05:00
move to lsp 1.14
This commit is contained in:
parent
db3fde45f3
commit
8cbe2b3519
3 changed files with 122 additions and 122 deletions
|
|
@ -14,7 +14,7 @@ depends: [
|
||||||
"dune" { >= "2.0" }
|
"dune" { >= "2.0" }
|
||||||
"yojson" { >= "1.6" }
|
"yojson" { >= "1.6" }
|
||||||
"logs"
|
"logs"
|
||||||
"lsp" { >= "1.11" & < "1.12" }
|
"lsp" { >= "1.14" & < "1.15" }
|
||||||
"ocaml" { >= "4.12" }
|
"ocaml" { >= "4.12" }
|
||||||
"odoc" { with-doc }
|
"odoc" { with-doc }
|
||||||
]
|
]
|
||||||
|
|
|
||||||
229
src/jsonrpc2.ml
229
src/jsonrpc2.ml
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
(** {1 Simple JSON-RPC2 implementation}
|
(** {1 Simple JSON-RPC2 implementation}
|
||||||
See {{: https://www.jsonrpc.org/specification} the spec} *)
|
See {{: https://www.jsonrpc.org/specification} the spec} *)
|
||||||
|
|
||||||
|
|
@ -15,29 +14,21 @@ module type S = sig
|
||||||
type t
|
type t
|
||||||
(** A jsonrpc2 connection. *)
|
(** A jsonrpc2 connection. *)
|
||||||
|
|
||||||
include module type of Server.Make(IO)
|
include module type of Server.Make (IO)
|
||||||
|
|
||||||
val create :
|
val create : ic:IO.in_channel -> oc:IO.out_channel -> server -> t
|
||||||
ic:IO.in_channel ->
|
|
||||||
oc:IO.out_channel ->
|
|
||||||
server ->
|
|
||||||
t
|
|
||||||
(** Create a connection from the pair of channels *)
|
(** Create a connection from the pair of channels *)
|
||||||
|
|
||||||
val create_stdio : server -> t
|
val create_stdio : server -> t
|
||||||
(** Create a connection using stdin/stdout *)
|
(** Create a connection using stdin/stdout *)
|
||||||
|
|
||||||
val run :
|
val run : ?shutdown:(unit -> bool) -> t -> unit IO.t
|
||||||
?shutdown:(unit -> bool) ->
|
|
||||||
t -> unit IO.t
|
|
||||||
(** Listen for incoming messages and responses *)
|
(** Listen for incoming messages and responses *)
|
||||||
end
|
end
|
||||||
|
|
||||||
module Make(IO : IO)
|
module Make (IO : IO) : S with module IO = IO = struct
|
||||||
: S with module IO = IO
|
|
||||||
= struct
|
|
||||||
module IO = IO
|
module IO = IO
|
||||||
include Server.Make(IO)
|
include Server.Make (IO)
|
||||||
open IO
|
open IO
|
||||||
|
|
||||||
type json = J.t
|
type json = J.t
|
||||||
|
|
@ -71,63 +62,63 @@ module Make(IO : IO)
|
||||||
s: server;
|
s: server;
|
||||||
}
|
}
|
||||||
|
|
||||||
let create ~ic ~oc server : t = {ic; oc; s=server}
|
let create ~ic ~oc server : t = { ic; oc; s = server }
|
||||||
|
let create_stdio server : t = create ~ic:IO.stdin ~oc:IO.stdout server
|
||||||
let create_stdio server : t =
|
|
||||||
create ~ic:IO.stdin ~oc:IO.stdout server
|
|
||||||
|
|
||||||
(* send a single message *)
|
(* send a single message *)
|
||||||
let send_json_ (self:t) (j:json) : unit IO.t =
|
let send_json_ (self : t) (j : json) : unit IO.t =
|
||||||
let json = J.to_string j in
|
let json = J.to_string j in
|
||||||
Log.debug (fun k->k "jsonrpc2: send json: %s" json);
|
Log.debug (fun k -> k "jsonrpc2: send json: %s" json);
|
||||||
let full_s =
|
let full_s =
|
||||||
Printf.sprintf "Content-Length: %d\r\n\r\n%s"
|
Printf.sprintf "Content-Length: %d\r\n\r\n%s" (String.length json) json
|
||||||
(String.length json) json
|
|
||||||
in
|
in
|
||||||
IO.write_string self.oc full_s
|
IO.write_string self.oc full_s
|
||||||
|
|
||||||
let send_response (self:t) (m:Jsonrpc.Response.t) : unit IO.t =
|
let send_response (self : t) (m : Jsonrpc.Response.t) : unit IO.t =
|
||||||
let json = Jsonrpc.Response.yojson_of_t m in
|
let json = Jsonrpc.Response.yojson_of_t m in
|
||||||
send_json_ self json
|
send_json_ self json
|
||||||
|
|
||||||
let send_server_notif (self:t) (m:Jsonrpc.Message.notification) : unit IO.t =
|
let send_server_notif (self : t) (m : Jsonrpc.Notification.t) : unit IO.t =
|
||||||
let json = Jsonrpc.Message.yojson_of_notification m in
|
let json = Jsonrpc.Notification.yojson_of_t m in
|
||||||
send_json_ self json
|
send_json_ self json
|
||||||
|
|
||||||
let try_ f =
|
let try_ f =
|
||||||
IO.catch
|
IO.catch
|
||||||
(fun () -> let+ x = f() in Ok x)
|
(fun () ->
|
||||||
|
let+ x = f () in
|
||||||
|
Ok x)
|
||||||
(fun e -> IO.return (Error e))
|
(fun e -> IO.return (Error e))
|
||||||
|
|
||||||
(* read a full message *)
|
(* read a full message *)
|
||||||
let read_msg (self:t) : (Jsonrpc.Message.either, exn) result IO.t =
|
let read_msg (self : t) : (Jsonrpc.Packet.t, exn) result IO.t =
|
||||||
let rec read_headers acc =
|
let rec read_headers acc =
|
||||||
let*? line =
|
let*? line = try_ @@ fun () -> IO.read_line self.ic in
|
||||||
try_ @@ fun () -> IO.read_line self.ic
|
|
||||||
in
|
|
||||||
match String.trim line with
|
match String.trim line with
|
||||||
| "" -> IO.return (Ok acc) (* last separator *)
|
| "" -> IO.return (Ok acc) (* last separator *)
|
||||||
| line ->
|
| line ->
|
||||||
begin match
|
(match
|
||||||
let i = String.index line ':' in
|
let i = String.index line ':' in
|
||||||
if i<0 || String.get line (i+1) <> ' ' then raise Not_found;
|
if i < 0 || String.get line (i + 1) <> ' ' then raise Not_found;
|
||||||
let key = String.lowercase_ascii @@ String.sub line 0 i in
|
let key = String.lowercase_ascii @@ String.sub line 0 i in
|
||||||
let v =
|
let v =
|
||||||
String.lowercase_ascii @@
|
String.lowercase_ascii
|
||||||
String.trim (String.sub line (i+1) (String.length line-i-1))
|
@@ String.trim
|
||||||
in
|
(String.sub line (i + 1) (String.length line - i - 1))
|
||||||
key, v
|
in
|
||||||
with
|
key, v
|
||||||
| pair -> read_headers (pair :: acc)
|
with
|
||||||
| exception _ ->
|
| pair -> read_headers (pair :: acc)
|
||||||
IO.return (Error (E(ErrorCode.ParseError, spf "invalid header: %S" line)))
|
| exception _ ->
|
||||||
end
|
IO.return
|
||||||
|
(Error (E (ErrorCode.ParseError, spf "invalid header: %S" line))))
|
||||||
in
|
in
|
||||||
let*? headers = read_headers [] in
|
let*? headers = read_headers [] in
|
||||||
Log.debug (fun k->k "jsonrpc2: read headers: [%s]"
|
Log.debug (fun k ->
|
||||||
(String.concat ";" @@
|
k "jsonrpc2: read headers: [%s]"
|
||||||
List.map (fun (a,b)->Printf.sprintf "(%S,%S)" a b) headers));
|
(String.concat ";"
|
||||||
let ok = match List.assoc "content-type" headers with
|
@@ List.map (fun (a, b) -> Printf.sprintf "(%S,%S)" a b) headers));
|
||||||
|
let ok =
|
||||||
|
match List.assoc "content-type" headers with
|
||||||
| "utf8" | "utf-8" -> true
|
| "utf8" | "utf-8" -> true
|
||||||
| _ -> false
|
| _ -> false
|
||||||
| exception Not_found -> true
|
| exception Not_found -> true
|
||||||
|
|
@ -135,111 +126,107 @@ module Make(IO : IO)
|
||||||
if ok then (
|
if ok then (
|
||||||
match int_of_string (List.assoc "content-length" headers) with
|
match int_of_string (List.assoc "content-length" headers) with
|
||||||
| n ->
|
| n ->
|
||||||
Log.debug (fun k->k "jsonrpc2: read %d bytes..." n);
|
Log.debug (fun k -> k "jsonrpc2: read %d bytes..." n);
|
||||||
let buf = Bytes.make n '\000' in
|
let buf = Bytes.make n '\000' in
|
||||||
let*? () =
|
let*? () = try_ @@ fun () -> IO.read self.ic buf 0 n in
|
||||||
try_ @@ fun () -> IO.read self.ic buf 0 n
|
|
||||||
in
|
|
||||||
(* log_lsp_ "got bytes %S" (Bytes.unsafe_to_string buf); *)
|
(* log_lsp_ "got bytes %S" (Bytes.unsafe_to_string buf); *)
|
||||||
let*? j =
|
let*? j =
|
||||||
try_ @@ fun () ->
|
try_ @@ fun () ->
|
||||||
IO.return @@ J.from_string (Bytes.unsafe_to_string buf)
|
IO.return @@ J.from_string (Bytes.unsafe_to_string buf)
|
||||||
in
|
in
|
||||||
Log.debug (fun k->k "got json %s" (J.to_string j));
|
Log.debug (fun k -> k "got json %s" (J.to_string j));
|
||||||
begin match Jsonrpc.Message.either_of_yojson j with
|
(match Jsonrpc.Packet.t_of_yojson j with
|
||||||
| m -> IO.return @@ Ok m
|
| m -> IO.return @@ Ok m
|
||||||
| exception _ ->
|
| exception _ ->
|
||||||
Log.err (fun k->k "cannot decode json message");
|
Log.err (fun k -> k "cannot decode json message");
|
||||||
IO.return (Error (E(ErrorCode.ParseError, "cannot decode json")))
|
IO.return (Error (E (ErrorCode.ParseError, "cannot decode json"))))
|
||||||
end
|
|
||||||
| exception _ ->
|
| exception _ ->
|
||||||
IO.return @@
|
IO.return
|
||||||
Error (E(ErrorCode.ParseError, "missing content-length' header"))
|
@@ Error (E (ErrorCode.ParseError, "missing content-length' header"))
|
||||||
) else (
|
) else
|
||||||
IO.return @@
|
IO.return
|
||||||
Error (E(ErrorCode.InvalidRequest, "content-type must be 'utf-8'"))
|
@@ Error (E (ErrorCode.InvalidRequest, "content-type must be 'utf-8'"))
|
||||||
)
|
|
||||||
|
|
||||||
let run ?(shutdown=fun _ -> false) (self:t) : unit IO.t =
|
let run ?(shutdown = fun _ -> false) (self : t) : unit IO.t =
|
||||||
let process_msg r =
|
let process_msg r =
|
||||||
let module M = Jsonrpc.Message in
|
let module M = Jsonrpc.Packet in
|
||||||
let protect ~id f =
|
let protect ~id f =
|
||||||
IO.catch f
|
IO.catch f (fun e ->
|
||||||
(fun e ->
|
let message =
|
||||||
let message = spf "%s\n%s" (Printexc.to_string e) (Printexc.get_backtrace()) in
|
spf "%s\n%s" (Printexc.to_string e) (Printexc.get_backtrace ())
|
||||||
Log.err (fun k->k "error: %s" message);
|
in
|
||||||
let r = Jsonrpc.Response.error id
|
Log.err (fun k -> k "error: %s" message);
|
||||||
(Jsonrpc.Response.Error.make
|
let r =
|
||||||
~code:Jsonrpc.Response.Error.Code.InternalError
|
Jsonrpc.Response.error id
|
||||||
~message ())
|
(Jsonrpc.Response.Error.make
|
||||||
|
~code:Jsonrpc.Response.Error.Code.InternalError ~message ())
|
||||||
in
|
in
|
||||||
send_response self r)
|
send_response self r)
|
||||||
in
|
in
|
||||||
match r.M.id with
|
match r with
|
||||||
| None ->
|
| M.Notification n ->
|
||||||
(* notification *)
|
(* notification *)
|
||||||
begin match Lsp.Client_notification.of_jsonrpc {r with M.id=()} with
|
(match Lsp.Client_notification.of_jsonrpc n with
|
||||||
| Ok n ->
|
| Ok n ->
|
||||||
IO.catch
|
IO.catch
|
||||||
(fun () ->
|
(fun () ->
|
||||||
(self.s)#on_notification n
|
self.s#on_notification n ~notify_back:(fun n ->
|
||||||
~notify_back:(fun n ->
|
let msg = Lsp.Server_notification.to_jsonrpc n in
|
||||||
let msg = Lsp.Server_notification.to_jsonrpc n in
|
send_server_notif self msg))
|
||||||
send_server_notif self msg))
|
(fun e ->
|
||||||
(fun e ->
|
let msg =
|
||||||
let msg =
|
Lsp.Types.LogMessageParams.create
|
||||||
Lsp.Types.LogMessageParams.create ~type_:Lsp.Types.MessageType.Error
|
~type_:Lsp.Types.MessageType.Error
|
||||||
~message:(Printexc.to_string e)
|
~message:(Printexc.to_string e)
|
||||||
in
|
in
|
||||||
let msg =
|
let msg =
|
||||||
Lsp.Server_notification.LogMessage msg
|
Lsp.Server_notification.LogMessage msg
|
||||||
|> Lsp.Server_notification.to_jsonrpc
|
|> Lsp.Server_notification.to_jsonrpc
|
||||||
in
|
in
|
||||||
send_server_notif self msg)
|
send_server_notif self msg)
|
||||||
| Error e ->
|
| Error e -> IO.failwith (spf "cannot decode notification: %s" e))
|
||||||
IO.failwith (spf "cannot decode notification: %s" e)
|
| M.Request r ->
|
||||||
end
|
|
||||||
| Some id ->
|
|
||||||
(* request, so we need to reply *)
|
(* request, so we need to reply *)
|
||||||
|
let id = r.id in
|
||||||
IO.catch
|
IO.catch
|
||||||
(fun () ->
|
(fun () ->
|
||||||
begin match Lsp.Client_request.of_jsonrpc {r with M.id} with
|
match Lsp.Client_request.of_jsonrpc r with
|
||||||
| Ok (Lsp.Client_request.E r) ->
|
| Ok (Lsp.Client_request.E r) ->
|
||||||
protect ~id (fun () ->
|
protect ~id (fun () ->
|
||||||
let* reply = self.s#on_request r ~id
|
let* reply =
|
||||||
~notify_back:(fun n ->
|
self.s#on_request r ~id ~notify_back:(fun n ->
|
||||||
let msg = Lsp.Server_notification.to_jsonrpc n in
|
let msg = Lsp.Server_notification.to_jsonrpc n in
|
||||||
send_server_notif self msg)
|
send_server_notif self msg)
|
||||||
in
|
in
|
||||||
let reply_json = Lsp.Client_request.yojson_of_result r reply in
|
let reply_json =
|
||||||
|
Lsp.Client_request.yojson_of_result r reply
|
||||||
|
in
|
||||||
let response = Jsonrpc.Response.ok id reply_json in
|
let response = Jsonrpc.Response.ok id reply_json in
|
||||||
send_response self response
|
send_response self response)
|
||||||
)
|
| Error e -> IO.failwith (spf "cannot decode request: %s" e))
|
||||||
| Error e ->
|
|
||||||
IO.failwith (spf "cannot decode request: %s" e)
|
|
||||||
end)
|
|
||||||
(fun e ->
|
(fun e ->
|
||||||
let message = spf "%s\n%s" (Printexc.to_string e) (Printexc.get_backtrace()) in
|
let message =
|
||||||
Log.err (fun k->k "error: %s" message);
|
spf "%s\n%s" (Printexc.to_string e) (Printexc.get_backtrace ())
|
||||||
|
in
|
||||||
|
Log.err (fun k -> k "error: %s" message);
|
||||||
let r =
|
let r =
|
||||||
Jsonrpc.Response.error id
|
Jsonrpc.Response.error id
|
||||||
(Jsonrpc.Response.Error.make
|
(Jsonrpc.Response.Error.make
|
||||||
~code:Jsonrpc.Response.Error.Code.InternalError
|
~code:Jsonrpc.Response.Error.Code.InternalError ~message ())
|
||||||
~message ())
|
|
||||||
in
|
in
|
||||||
send_response self r)
|
send_response self r)
|
||||||
|
| _p -> IO.failwith "neither notification nor request"
|
||||||
in
|
in
|
||||||
let rec loop () =
|
let rec loop () =
|
||||||
if shutdown() then IO.return ()
|
if shutdown () then
|
||||||
else (
|
IO.return ()
|
||||||
|
else
|
||||||
let* r = read_msg self in
|
let* r = read_msg self in
|
||||||
match r with
|
match r with
|
||||||
| Ok r ->
|
| Ok r ->
|
||||||
IO.spawn (fun () -> process_msg r);
|
IO.spawn (fun () -> process_msg r);
|
||||||
loop()
|
loop ()
|
||||||
| Error e -> IO.fail e
|
| Error e -> IO.fail e
|
||||||
)
|
|
||||||
in
|
in
|
||||||
loop()
|
loop ()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,14 @@ module Make(IO : IO) = struct
|
||||||
| Lsp.Client_request.SemanticTokensDelta _
|
| Lsp.Client_request.SemanticTokensDelta _
|
||||||
| Lsp.Client_request.SemanticTokensFull _
|
| Lsp.Client_request.SemanticTokensFull _
|
||||||
| Lsp.Client_request.SemanticTokensRange _
|
| Lsp.Client_request.SemanticTokensRange _
|
||||||
|
| Lsp.Client_request.TextDocumentImplementation _
|
||||||
|
| Lsp.Client_request.TextDocumentPrepareCallHierarchy _
|
||||||
|
| Lsp.Client_request.TextDocumentRangeFormatting _
|
||||||
|
| Lsp.Client_request.CallHierarchyIncomingCalls _
|
||||||
|
| Lsp.Client_request.CallHierarchyOutgoingCalls _
|
||||||
|
| Lsp.Client_request.WillCreateFiles _
|
||||||
|
| Lsp.Client_request.WillDeleteFiles _
|
||||||
|
| Lsp.Client_request.WillRenameFiles _
|
||||||
| Lsp.Client_request.UnknownRequest _ ->
|
| Lsp.Client_request.UnknownRequest _ ->
|
||||||
let notify_back = new notify_back ~notify_back () in
|
let notify_back = new notify_back ~notify_back () in
|
||||||
self#on_request_unhandled ~notify_back ~id r
|
self#on_request_unhandled ~notify_back ~id r
|
||||||
|
|
@ -435,6 +443,11 @@ module Make(IO : IO) = struct
|
||||||
| Lsp.Client_notification.CancelRequest _
|
| Lsp.Client_notification.CancelRequest _
|
||||||
| Lsp.Client_notification.WorkDoneProgressCancel _
|
| Lsp.Client_notification.WorkDoneProgressCancel _
|
||||||
| Lsp.Client_notification.SetTrace _
|
| Lsp.Client_notification.SetTrace _
|
||||||
|
| Lsp.Client_notification.DidChangeWatchedFiles _
|
||||||
|
| Lsp.Client_notification.DidCreateFiles _
|
||||||
|
| Lsp.Client_notification.DidDeleteFiles _
|
||||||
|
| Lsp.Client_notification.DidRenameFiles _
|
||||||
|
| Lsp.Client_notification.LogTrace _
|
||||||
->
|
->
|
||||||
let notify_back = new notify_back ~notify_back () in
|
let notify_back = new notify_back ~notify_back () in
|
||||||
self#on_notification_unhandled ~notify_back n
|
self#on_notification_unhandled ~notify_back n
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue