package vcaml

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

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
      (* We can't really do anything interesting with a failure to display an error. *)
      |> (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
OCaml

Innovation. Community. Security.