package vcaml

  1. Overview
  2. Docs
OCaml bindings for the Neovim API

Install

Dune Dependency

Authors

Maintainers

Sources

vcaml-v0.16.0.tar.gz
sha256=dd123302c46af7ca6eda8a7806c78236fd217a8c73a2e1cd7da85f1d69ed1ae4

doc/src/vcaml/keymap.ml.html

Source file keymap.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
open Core

(** Note that (1) this is not the full set of modes that [nvim_get_mode] can return, and
    (2) the string representations of these modes and the modes returned by
    [nvim_get_mode] are different. Thus we should not try to unify the types. *)
module Mode = struct
  type t =
    | Normal
    | Operator_pending
    | Insert
    | Cmd_line
    | Select
    | Visual
    | Terminal
    | Visual_and_select
    | Normal_and_visual_and_operator_pending
    | Insert_and_command_line
    | Language
  [@@deriving compare, enumerate, equal, sexp_of]

  let of_string = function
    | " " -> Normal_and_visual_and_operator_pending
    | "!" -> Insert_and_command_line
    | "n" -> Normal
    | "v" -> Visual_and_select
    | "o" -> Operator_pending
    | "i" -> Insert
    | "c" -> Cmd_line
    | "s" -> Select
    | "x" -> Visual
    | "l" -> Language
    | "t" -> Terminal
    | mode -> raise_s [%message "Unrecognized mode" (mode : string)]
  ;;

  let of_string_or_error t = Or_error.try_with (fun () -> of_string t)

  let to_string = function
    | Normal -> "n"
    | Operator_pending -> "o"
    | Insert -> "i"
    | Cmd_line -> "c"
    | Select -> "s"
    | Visual -> "x"
    | Terminal -> "t"
    | Visual_and_select -> "v"
    | Normal_and_visual_and_operator_pending -> ""
    | Insert_and_command_line -> "!"
    | Language -> "l"
  ;;
end

let scope_of_msgpack msg =
  (* Buffer indexing starts at 1, so a buffer number of 0 indicates a global mapping. *)
  match (msg : Msgpack.t) with
  | Integer 0 -> Ok `Global
  | _ ->
    let open Or_error.Let_syntax in
    let%map buffer = Extract.value Buffer msg in
    `Buffer_local buffer
;;

type t =
  { description : string option [@sexp.option]
  ; lhs : string
  ; rhs : string
  ; mode : Mode.t
  ; scope : [ `Global | `Buffer_local of Nvim_internal.Buffer.t ]
  ; expr : bool
  ; nowait : bool
  ; silent : bool
  ; recursive : bool
  ; sid : int
  }
[@@deriving sexp_of]

let of_msgpack msg =
  let open Or_error.Let_syntax in
  let to_or_error = function
    | Some i -> Ok i
    | None -> Or_error.error_string "malformed keycommand message"
  in
  let%bind result = Extract.map_of_msgpack_map msg in
  let lookup_exn key extract = Map.find result key |> to_or_error >>= extract in
  let%bind modes =
    lookup_exn "mode" (fun msg ->
      let%bind modes = Extract.string msg in
      List.map (String.to_list modes) ~f:(fun mode ->
        Mode.of_string_or_error (Char.to_string mode))
      |> Or_error.combine_errors)
  in
  let%bind silent = lookup_exn "silent" Extract.bool in
  let%bind noremap = lookup_exn "noremap" Extract.bool in
  let%bind nowait = lookup_exn "nowait" Extract.bool in
  let%bind expr = lookup_exn "expr" Extract.bool in
  let%bind sid = lookup_exn "sid" Extract.int in
  let%bind lhs = lookup_exn "lhs" Extract.string in
  let%bind rhs = lookup_exn "rhs" Extract.string in
  let%bind description =
    match Map.find result "desc" with
    | None -> Ok None
    | Some description ->
      let%map description = Extract.string description in
      Some description
  in
  let%bind scope = lookup_exn "buffer" scope_of_msgpack in
  let recursive = not noremap in
  let keymaps =
    List.map modes ~f:(fun mode ->
      { description; lhs; rhs; mode; scope; expr; nowait; silent; recursive; sid })
  in
  return keymaps
;;

(* The semantics of [nvim_get_keymap] and [nvim_buf_get_keymap] are a bit undocumented
   and unintuitive in the following ways:

   1. Simple-mode queries return all mappings that apply in that mode. However, language
   mappings are not returned in queries for insert and command modes. To fix this, we
   need to explicitly query for language mappings and join the results.

   2. There is no way to query for mappings defined with '!' (see `:h mapmode-ic`).
   These queries (appear to) work by taking the first character in the query, returning
   the appropriate mappings if the character is a recognized mode, and returning
   mappings for mapmode-nvo by default. '!' is not a recognized mode, so it silently
   returns mappings for 'nvo' instead of 'ic'. To fix this, we need to individually
   query for 'i' and 'c' (and 'l') and join the results together. *)
let get ~scope ~mode =
  let query =
    match scope with
    | `Global -> Nvim_internal.nvim_get_keymap
    | `Buffer_local buffer -> Nvim_internal.nvim_buf_get_keymap ~buffer
  in
  let modes =
    match (mode : Mode.t) with
    | Normal
    | Operator_pending
    | Select
    | Visual
    | Terminal
    | Visual_and_select
    | Normal_and_visual_and_operator_pending
    | Language -> [ mode ]
    | Insert -> [ Insert; Language ]
    | Cmd_line -> [ Cmd_line; Language ]
    | Insert_and_command_line -> [ Insert; Cmd_line; Language ]
  in
  let open Api_call.Or_error.Let_syntax in
  modes
  |> List.map ~f:(fun mode -> query ~mode:(Mode.to_string mode) |> Api_call.of_api_result)
  |> Api_call.Or_error.all
  >>| List.concat
  (* Because 'i' and 'c' will produce duplicate entries for '!' mappings, we need to
     dedup the results after querying each. *)
  >>| List.dedup_and_sort ~compare:[%compare: Msgpack.t]
  >>| List.map ~f:of_msgpack
  >>| Or_error.combine_errors
  >>| Or_error.map ~f:List.concat
  |> Api_call.map ~f:Or_error.join
;;

let set
      ?(recursive = false)
      ?(expr = false)
      ?(unique = false)
      ?(nowait = false)
      ?(silent = false)
      ?(description = "")
      ~scope
      ~lhs
      ~rhs
      ~mode
      ()
  =
  let query =
    match scope with
    | `Global -> Nvim_internal.nvim_set_keymap
    | `Buffer_local buffer -> Nvim_internal.nvim_buf_set_keymap ~buffer
  in
  let mode = Mode.to_string mode in
  let opts =
    let boolean_opts =
      [ "noremap", not recursive
      ; "expr", expr
      ; "unique", unique
      ; "nowait", nowait
      ; "silent", silent
      ]
      |> List.map ~f:(fun (key, value) -> Msgpack.String key, Msgpack.Boolean value)
    in
    match description with
    | "" -> boolean_opts
    | _ -> (String "desc", String description) :: boolean_opts
  in
  query ~mode ~lhs ~rhs ~opts |> Api_call.of_api_result
;;

let unset ~scope ~lhs ~mode =
  let query =
    match scope with
    | `Global -> Nvim_internal.nvim_del_keymap
    | `Buffer_local buffer -> Nvim_internal.nvim_buf_del_keymap ~buffer
  in
  let mode = Mode.to_string mode in
  query ~mode ~lhs |> Api_call.of_api_result
;;
OCaml

Innovation. Community. Security.