Source file vcaml_plugin.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
open! Core
open! Async
open Vcaml
open Vcaml_plugin_intf
module Oneshot = struct
include Oneshot
module Make (O : Arg) = struct
let command ~summary =
Async.Command.async_or_error
~summary
(Core.Command.Param.return (fun () ->
let open Deferred.Or_error.Let_syntax in
let client = Client.create ~on_error:O.on_error in
let shutdown = Ivar.create () in
let invoked_rpc = Set_once.create () in
let oneshot ~name f =
match Set_once.get invoked_rpc with
| Some previously_invoked ->
Deferred.Or_error.error_s
[%message
"Already invoked an RPC"
~invoking:(name : string)
(previously_invoked : string)]
| None ->
Set_once.set_exn invoked_rpc [%here] name;
let%map result = f () in
Ivar.fill shutdown ();
result
in
List.iter O.rpc_handlers ~f:(fun (Sync_rpc { name; type_; f }) ->
Private.register_request_blocking
client
~name
~type_
~f
~wrap_f:(oneshot ~name));
let%bind (_ : [ `connected ] Client.t) =
Client.attach client Stdio ~time_source:(Time_source.wall_clock ())
in
Ivar.read shutdown |> Deferred.ok))
;;
end
end
module Persistent = struct
include Persistent
module Make (P : Arg) = struct
let register_handlers ~client ~state ~shutdown =
let shutdown = Ivar.fill_if_empty shutdown in
List.iter P.rpc_handlers ~f:(function
| Sync_rpc { name; type_; f } ->
register_request_blocking client ~name ~type_ ~f:(f state ~shutdown)
| Async_rpc { name; type_; f } ->
Private.register_request_async
client
~name
~type_
~f:(f state ~shutdown)
~wrap_f:(fun f -> f () |> Deferred.Or_error.tag ~tag:P.name))
;;
let display_error_in_neovim ~client error =
error
|> Error.tag ~tag:P.name
|> Error.to_string_hum
|> (fun str -> Nvim.err_writeln ~str)
|> run_join [%here] client
|> (Deferred.ignore_m : unit Deferred.Or_error.t -> unit Deferred.t)
;;
let start ~client ~state ~shutdown =
let%bind result =
let open Deferred.Or_error.Let_syntax in
let shutdown = Ivar.fill_if_empty shutdown in
let chan_id = Client.rpc_channel_id client in
let%bind () = P.on_startup client state ~shutdown in
match P.vimscript_notify_fn with
| None -> return ()
| Some function_name ->
(match%bind
wrap_viml_function
~type_:Defun.Vim.(Integer @-> return Object)
~function_name
chan_id
|> run_join [%here] client
with
| Integer 0 -> return ()
| value ->
Deferred.Or_error.error_s
[%message
(sprintf "%s returned a value" function_name) ~_:(value : Msgpack.t)])
in
let%bind () =
match result with
| Ok _ -> return ()
| Error error -> display_error_in_neovim ~client error
in
return result
;;
let on_shutdown client state =
let%bind result = P.on_shutdown client state in
let%bind () =
match result with
| Ok _ -> return ()
| Error error -> display_error_in_neovim ~client error
in
return result
;;
let command =
Async.Command.async_or_error
~summary:P.description
(Core.Command.Param.return (fun () ->
let open Deferred.Or_error.Let_syntax in
let state = P.init_state () in
let shutdown = Ivar.create () in
let client = Vcaml.Client.create ~on_error:P.on_error in
register_handlers ~client ~state ~shutdown;
let%bind client =
Vcaml.Client.attach
client
(Unix `Child)
~time_source:(Time_source.wall_clock ())
in
let%bind () = start ~client ~state ~shutdown in
let%bind () = Ivar.read shutdown |> Deferred.ok in
on_shutdown client state))
;;
module For_testing = struct
type plugin_state = P.state [@@deriving sexp_of]
module State = struct
type t =
{ plugin_state : plugin_state
; shutdown : unit -> unit
; wait_for_shutdown : unit Or_error.t Deferred.t
}
end
let start ~client =
let open Deferred.Or_error.Let_syntax in
let state = P.init_state () in
let shutdown_started = Ivar.create () in
let shutdown_finished = Ivar.create () in
don't_wait_for
(let%bind.Deferred () = Ivar.read shutdown_started in
let%map.Deferred result = on_shutdown client state in
Ivar.fill shutdown_finished result);
register_handlers ~client ~state ~shutdown:shutdown_started;
let%bind () = start ~client ~state ~shutdown:shutdown_started in
return
{ State.plugin_state = state
; shutdown = Ivar.fill shutdown_started
; wait_for_shutdown = Ivar.read shutdown_finished
}
;;
end
end
end