diff --git a/ocaml-lsp-server/bin/main.ml b/ocaml-lsp-server/bin/main.ml index 141490296..48cb4bd54 100644 --- a/ocaml-lsp-server/bin/main.ml +++ b/ocaml-lsp-server/bin/main.ml @@ -1,16 +1,45 @@ +open Stdune + let () = Printexc.record_backtrace true; let version = ref false in let read_dot_merlin = ref false in - Arg.parse + let pipe = ref None in + let port = ref None in + let stdio = ref false in + let spec = [ ("--version", Arg.Set version, "print version") + ; ("--pipe", Arg.String (fun p -> pipe := Some p), "set pipe path") + ; ("--socket", Arg.Int (fun p -> port := Some p), "set port") + ; ("--stdio", Arg.Set stdio, "set stdio") + ; ( "--node-ipc" + , Arg.Unit (fun () -> raise @@ Arg.Bad "node-ipc isn't supported") + , "not supported" ) ; ( "--fallback-read-dot-merlin" , Arg.Set read_dot_merlin , "read Merlin config from .merlin files. The `dot-merlin-reader` \ package must be installed" ) ] - (fun _ -> raise (Arg.Bad "anonymous arguments are not accepted")) - "ocamllsp"; + in + let usage = + "ocamllsp [ --stdio | --socket SOCKET --port PORT | --pipe PIPE ]" + in + Arg.parse + spec + (fun _ -> raise @@ Arg.Bad "anonymous arguments aren't allowed") + usage; + let channel : Ocaml_lsp_server.channel = + try + match (!pipe, !port, !stdio) with + | None, None, _ -> Stdio + | Some p, None, false -> Pipe p + | None, Some s, false -> Socket s + | _, _, _ -> raise @@ Arg.Bad "invalid arguments" + with Arg.Bad s -> + Format.eprintf "%s@.%!" s; + Arg.usage spec usage; + exit 1 + in let version = !version in if version then let version = Ocaml_lsp_server.Version.get () in @@ -19,7 +48,7 @@ let () = let module Exn_with_backtrace = Stdune.Exn_with_backtrace in match Exn_with_backtrace.try_with - (Ocaml_lsp_server.run ~read_dot_merlin:!read_dot_merlin) + (Ocaml_lsp_server.run channel ~read_dot_merlin:!read_dot_merlin) with | Ok () -> () | Error exn -> diff --git a/ocaml-lsp-server/src/ocaml_lsp_server.ml b/ocaml-lsp-server/src/ocaml_lsp_server.ml index 21ce1b868..b25f5b84f 100644 --- a/ocaml-lsp-server/src/ocaml_lsp_server.ml +++ b/ocaml-lsp-server/src/ocaml_lsp_server.ml @@ -1010,7 +1010,7 @@ let on_notification server (notification : Client_notification.t) : in state -let start () = +let start stream = let detached = Fiber.Pool.create () in let server = Fdecl.create Dyn.opaque in let store = Document_store.make server detached in @@ -1018,11 +1018,6 @@ let start () = let on_request = { Server.Handler.on_request } in Server.Handler.make ~on_request ~on_notification () in - let* stream = - let* stdin = Lev_fiber.Io.stdin in - let+ stdout = Lev_fiber.Io.stdout in - Lsp_fiber.Fiber_io.make stdin stdout - in let ocamlformat_rpc = Ocamlformat_rpc.create () in let* configuration = Configuration.default () in let wheel = Configuration.wheel configuration in @@ -1105,8 +1100,42 @@ let start () = let metrics = Metrics.create () in Metrics.with_metrics metrics run -let run ~read_dot_merlin () = +type channel = + | Stdio + | Pipe of string + | Socket of int + +let socket sockaddr = + let domain = Unix.domain_of_sockaddr sockaddr in + let fd = + Lev_fiber.Fd.create + (Unix.socket ~cloexec:true domain Unix.SOCK_STREAM 0) + (`Non_blocking false) + in + let* () = Lev_fiber.Socket.connect fd sockaddr in + Lev_fiber.Io.create_rw fd + +let stream_of_channel = function + | Stdio -> + let* stdin = Lev_fiber.Io.stdin in + let+ stdout = Lev_fiber.Io.stdout in + (stdin, stdout) + | Pipe path -> + if Sys.win32 then ( + Format.eprintf "windows pipes are not supported"; + exit 1) + else + let sockaddr = Unix.ADDR_UNIX path in + socket sockaddr + | Socket port -> + let sockaddr = Unix.ADDR_INET (Unix.inet_addr_loopback, port) in + socket sockaddr + +let run channel ~read_dot_merlin () = Merlin_utils.Lib_config.set_program_name "ocamllsp"; Merlin_config.should_read_dot_merlin := read_dot_merlin; Unix.putenv "__MERLIN_MASTER_PID" (string_of_int (Unix.getpid ())); - Lev_fiber.run ~sigpipe:`Ignore start |> Lev_fiber.Error.ok_exn + Lev_fiber.run ~sigpipe:`Ignore (fun () -> + let* input, output = stream_of_channel channel in + start (Lsp_fiber.Fiber_io.make input output)) + |> Lev_fiber.Error.ok_exn diff --git a/ocaml-lsp-server/src/ocaml_lsp_server.mli b/ocaml-lsp-server/src/ocaml_lsp_server.mli index 17e6f16ab..af663fabb 100644 --- a/ocaml-lsp-server/src/ocaml_lsp_server.mli +++ b/ocaml-lsp-server/src/ocaml_lsp_server.mli @@ -1,4 +1,9 @@ -val run : read_dot_merlin:bool -> unit -> unit +type channel = + | Stdio + | Pipe of string + | Socket of int + +val run : channel -> read_dot_merlin:bool -> unit -> unit module Diagnostics = Diagnostics module Version = Version