···11+let h = ref []
22+33+let history prefix =
44+ if prefix <> "" then List.filter (fun s -> String.starts_with ~prefix s) !h
55+ else !h
66+77+let complete s =
88+ match String.get s 0 with
99+ | 'h' -> [ "hello"; "hello there" ]
1010+ | _ | (exception Invalid_argument _) -> []
1111+1212+let () =
1313+ Fmt_tty.setup_std_outputs ();
1414+ let rec loop sys_break =
1515+ let prompt =
1616+ if sys_break then "[\x1b[31m130\x1b[0m] \x1b[33m>>\x1b[0m "
1717+ else "\x1b[33m>>\x1b[0m "
1818+ in
1919+ match Bruit.bruit ~history ~complete prompt with
2020+ | String (Some s) ->
2121+ Fmt.pr "%s\n%!" s;
2222+ h := s :: !h;
2323+ loop false
2424+ | String None -> ()
2525+ | Ctrl_c -> loop true
2626+ in
2727+ loop false
+579
vendor/bruit/src/bruit.ml
···11+(* See the end of the file for the original license of Linenoise. *)
22+33+let max_line = 2048
44+55+type key =
66+ | Enter
77+ | Ctrl_a
88+ | Ctrl_b
99+ | Ctrl_c
1010+ | Ctrl_d
1111+ | Ctrl_e
1212+ | Ctrl_f
1313+ | Ctrl_r
1414+ | Ctrl_p
1515+ | Ctrl_g
1616+ | Backspace
1717+ | Escape_sequence
1818+ | Tab
1919+ | Unknown of Uchar.t
2020+2121+let key_of_char c =
2222+ match Char.code c with
2323+ | 1 -> Ctrl_a
2424+ | 2 -> Ctrl_b
2525+ | 3 -> Ctrl_c
2626+ | 4 -> Ctrl_d
2727+ | 5 -> Ctrl_e
2828+ | 6 -> Ctrl_f
2929+ | 7 -> Ctrl_g
3030+ | 16 -> Ctrl_p
3131+ | 18 -> Ctrl_r
3232+ | 9 -> Tab
3333+ | 13 -> Enter
3434+ | 27 -> Escape_sequence
3535+ | 127 -> Backspace
3636+ | _ -> Unknown (Uchar.of_char c)
3737+3838+module State = struct
3939+ type completion = string -> string list
4040+4141+ type t = {
4242+ ifd : Unix.file_descr;
4343+ ofd : Unix.file_descr;
4444+ buf : bytes;
4545+ buf_len : int;
4646+ prompt : bytes;
4747+ plen : int;
4848+ old_pos : int;
4949+ pos : int;
5050+ len : int;
5151+ (* Update later *)
5252+ cols : int;
5353+ old_rows : int;
5454+ old_row_pos : int;
5555+ history_index : int;
5656+ history : string list;
5757+ saved_buf : string;
5858+ read_buf : Bytes.t;
5959+ in_completion : bool;
6060+ completion_idx : int;
6161+ complete : completion option;
6262+ }
6363+6464+ let buf t = Bytes.sub t.buf 0 t.len
6565+6666+ let make ?(in_completion = false) ?(completion_idx = 0) ?complete
6767+ ?(old_pos = 0) ?(pos = 0) ?(len = 0) ?(history = []) ?(ifd = Unix.stdin)
6868+ ?(ofd = Unix.stdout) ~prompt buf =
6969+ {
7070+ in_completion;
7171+ ifd;
7272+ ofd;
7373+ buf;
7474+ buf_len = Bytes.length buf;
7575+ prompt;
7676+ plen = Bytes.length prompt;
7777+ old_pos;
7878+ pos;
7979+ len;
8080+ cols = 0;
8181+ old_row_pos = 1;
8282+ history;
8383+ old_rows = 0;
8484+ history_index = -1;
8585+ saved_buf = "";
8686+ complete;
8787+ completion_idx;
8888+ read_buf = Bytes.make 1 '\000' (* For reading a character *);
8989+ }
9090+9191+ let override ?in_completion ?completion_idx ?complete ?ifd ?ofd ?buf ?buf_len
9292+ ?prompt ?plen ?old_pos ?pos ?len ?cols ?old_rows ?old_row_pos
9393+ ?history_index ?history ?saved_buf (t : t) =
9494+ let () =
9595+ match buf with
9696+ | None -> ()
9797+ | Some buf -> Bytes.blit buf 0 t.buf 0 (Bytes.length buf)
9898+ in
9999+ {
100100+ in_completion = Option.value ~default:t.in_completion in_completion;
101101+ ifd = Option.value ~default:t.ifd ifd;
102102+ ofd = Option.value ~default:t.ofd ofd;
103103+ buf = t.buf;
104104+ buf_len = Option.value ~default:t.buf_len buf_len;
105105+ prompt = Option.value ~default:t.prompt prompt;
106106+ plen = Option.value ~default:t.plen plen;
107107+ old_pos = Option.value ~default:t.old_pos old_pos;
108108+ pos = Option.value ~default:t.pos pos;
109109+ len = Option.value ~default:t.len len;
110110+ cols = Option.value ~default:t.cols cols;
111111+ old_rows = Option.value ~default:t.old_rows old_rows;
112112+ old_row_pos = Option.value ~default:t.old_row_pos old_row_pos;
113113+ history_index = Option.value ~default:t.history_index history_index;
114114+ complete = (match complete with Some f -> Some f | None -> t.complete);
115115+ read_buf = t.read_buf;
116116+ completion_idx = Option.value ~default:t.completion_idx completion_idx;
117117+ history = Option.value ~default:t.history history;
118118+ saved_buf = Option.value ~default:t.saved_buf saved_buf;
119119+ }
120120+end
121121+122122+let get_columns () =
123123+ match Terminal.Size.get_columns () with Some n -> n | None -> 80
124124+125125+let with_raw_mode (state : State.t) fn =
126126+ let saved_tio = Unix.tcgetattr state.ifd in
127127+ let tio : Unix.terminal_io =
128128+ {
129129+ saved_tio with
130130+ c_brkint = false;
131131+ c_icrnl = false;
132132+ c_inpck = false;
133133+ c_istrip = false;
134134+ c_ixon = false;
135135+ c_opost = false;
136136+ c_csize = 8;
137137+ c_echo = false;
138138+ c_icanon = false;
139139+ c_isig = false;
140140+ c_vtime = 0;
141141+ c_vmin = 1;
142142+ }
143143+ in
144144+ Unix.tcsetattr state.ifd TCSAFLUSH tio;
145145+ Fun.protect
146146+ ~finally:(fun () -> Unix.tcsetattr state.ifd TCSADRAIN saved_tio)
147147+ fn
148148+149149+let write_bytes fd s =
150150+ let len = Bytes.length s in
151151+ let wrote = Unix.write fd s 0 len in
152152+ assert (Int.equal len wrote)
153153+154154+let write_uchar fd u =
155155+ let b_len = Uchar.utf_8_byte_length u in
156156+ let bs = Bytes.create b_len in
157157+ let wrote = Bytes.set_utf_8_uchar bs 0 u in
158158+ assert (Int.equal b_len wrote);
159159+ write_bytes fd bs
160160+161161+type edit = Editing of State.t | Finished of bytes option | Ctrl_c
162162+163163+let read_char state =
164164+ try
165165+ let read = Unix.read state.State.ifd state.read_buf 0 1 in
166166+ if read = 0 then `None else `Some (Bytes.unsafe_get state.read_buf 0)
167167+ with Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.EAGAIN), _, _) -> `Editing
168168+169169+let edit_start ~stdin:_ ~stdout:_ state fn =
170170+ with_raw_mode state @@ fun () ->
171171+ let cols = get_columns () in
172172+ (* Bytes.set state.buf 0 '\000'; *)
173173+ let state = State.override ~cols ~buf_len:(state.buf_len - 1) state in
174174+ write_bytes state.ofd state.prompt;
175175+ fn state
176176+177177+let utf8_display_width b len =
178178+ let s = Bytes.to_string b in
179179+ Terminal.guess_printed_width (String.sub s 0 len)
180180+181181+let utf8_next_char_len s off =
182182+ Bytes.get_utf_8_uchar s off
183183+ |> Uchar.utf_decode_uchar |> Uchar.utf_8_byte_length
184184+185185+let utf8_prev_char_len s off =
186186+ let rec loop pos =
187187+ if pos < 0 || pos < off - 4 then
188188+ invalid_arg "UTF8 previous character length";
189189+ let decode = Bytes.get_utf_8_uchar s off in
190190+ if Uchar.utf_decode_is_valid decode then
191191+ Uchar.utf_decode_uchar decode |> Uchar.utf_8_byte_length
192192+ else loop (pos - 1)
193193+ in
194194+ loop (off - 1)
195195+196196+type refresh_flag = Rewrite
197197+198198+let refresh_single_line ?(flags = []) ?prompt (state : State.t) =
199199+ let prompt = match prompt with None -> state.prompt | Some p -> p in
200200+ let pwidth = utf8_display_width prompt state.plen in
201201+ let poscol = ref @@ utf8_display_width state.buf state.pos in
202202+ let lencol = ref @@ utf8_display_width state.buf state.len in
203203+204204+ let rec loop (state : State.t) =
205205+ if pwidth + !poscol >= state.cols then begin
206206+ let clen = utf8_next_char_len state.buf 0 in
207207+ let c_width =
208208+ Uchar.utf_8_byte_length
209209+ (Bytes.get_utf_8_uchar state.buf clen |> Uchar.utf_decode_uchar)
210210+ in
211211+ poscol := !poscol - c_width;
212212+ lencol := !lencol - c_width;
213213+ let state =
214214+ State.override ~len:(state.len - clen) ~pos:(state.pos - clen) state
215215+ in
216216+ loop state
217217+ end
218218+ else state
219219+ in
220220+ let state = loop state in
221221+ let ab = Buffer.create 0 in
222222+ (* Clear line *)
223223+ Buffer.add_char ab '\r';
224224+225225+ (* Add prompt *)
226226+ if List.mem Rewrite flags then begin
227227+ Buffer.add_bytes ab prompt;
228228+ Buffer.add_bytes ab (Bytes.sub state.buf 0 state.len)
229229+ end;
230230+231231+ (* Erase to the right *)
232232+ Buffer.add_string ab "\x1b[0K";
233233+234234+ (* Cursor to the original position *)
235235+ if List.mem Rewrite flags then begin
236236+ Buffer.add_string ab (Format.sprintf "\r\x1b[%dC" (!poscol + pwidth))
237237+ end;
238238+ write_bytes state.ofd (Buffer.to_bytes ab);
239239+ state
240240+241241+let refresh_line state = refresh_single_line ~flags:[ Rewrite ] state
242242+243243+let refresh_line_with_completions (state : State.t) lcs =
244244+ if state.completion_idx < List.length lcs then
245245+ let saved_state = state in
246246+ let saved_buf = Bytes.copy state.buf in
247247+ let state =
248248+ State.override
249249+ ~len:(Bytes.length (List.nth lcs state.completion_idx))
250250+ ~pos:(Bytes.length (List.nth lcs state.completion_idx))
251251+ ~buf:(List.nth lcs state.completion_idx)
252252+ state
253253+ in
254254+ let state : State.t = refresh_line state in
255255+ State.override state ~len:saved_state.len ~pos:saved_state.pos
256256+ ~buf:saved_buf
257257+ else state
258258+259259+let edit_insert (state : State.t) c =
260260+ let clen = Uchar.utf_8_byte_length c in
261261+ (* At the end of the line *)
262262+ if Int.equal state.len state.pos then begin
263263+ let _ : int = Bytes.set_utf_8_uchar state.buf state.pos c in
264264+ let state =
265265+ State.override ~pos:(state.pos + clen) ~len:(state.len + clen) state
266266+ in
267267+ if
268268+ utf8_display_width state.prompt state.plen
269269+ + utf8_display_width state.buf state.len
270270+ < state.cols
271271+ then begin
272272+ write_uchar state.ofd c;
273273+ state
274274+ end
275275+ else refresh_line state
276276+ end
277277+ else begin
278278+ Bytes.blit state.buf state.pos state.buf (state.pos + clen)
279279+ (state.len - state.pos);
280280+ let _ : int = Bytes.set_utf_8_uchar state.buf state.pos c in
281281+ let state =
282282+ State.override ~len:(state.len + clen) ~pos:(state.pos + clen) state
283283+ in
284284+ refresh_line state
285285+ end
286286+287287+let edit_backspace (state : State.t) =
288288+ let state =
289289+ if state.pos > 0 && state.len > 0 then begin
290290+ let clen = utf8_prev_char_len state.buf state.pos in
291291+ let dst = state.pos - clen in
292292+ let src = state.pos in
293293+ let len = state.len - state.pos in
294294+ Bytes.blit state.buf src state.buf dst len;
295295+ State.override ~pos:(state.pos - clen) ~len:(state.len - clen) state
296296+ end
297297+ else state
298298+ in
299299+ refresh_line state
300300+301301+let get_bytes state = Bytes.sub state.State.buf 0 state.len
302302+303303+let complete_line (state : State.t) c cn =
304304+ match cn (String.of_bytes (get_bytes state)) with
305305+ | [] -> (State.override ~in_completion:false state, `Char c)
306306+ | xs ->
307307+ let state, c =
308308+ match key_of_char c with
309309+ | Tab ->
310310+ if not state.in_completion then
311311+ ( State.override ~in_completion:true ~completion_idx:0 state,
312312+ `Edit_more )
313313+ else
314314+ ( State.override
315315+ ~completion_idx:
316316+ ((state.completion_idx + 1) mod (List.length xs + 1))
317317+ state,
318318+ `Edit_more )
319319+ | _ ->
320320+ let state =
321321+ if state.completion_idx < List.length xs then begin
322322+ let to_write = List.nth xs state.completion_idx in
323323+ let to_write_len = String.length to_write in
324324+ Bytes.blit_string to_write 0 state.buf 0 to_write_len;
325325+ State.override ~len:to_write_len ~pos:to_write_len state
326326+ end
327327+ else state
328328+ in
329329+ (State.override ~in_completion:false state, `Char c)
330330+ in
331331+ if state.in_completion && state.completion_idx < List.length xs then begin
332332+ (refresh_line_with_completions state (List.map Bytes.of_string xs), c)
333333+ end
334334+ else begin
335335+ (refresh_line state, c)
336336+ end
337337+338338+let move_left (state : State.t) =
339339+ let s =
340340+ if state.pos > 0 then
341341+ State.override
342342+ ~pos:(state.pos - utf8_prev_char_len state.buf state.pos)
343343+ state
344344+ else state
345345+ in
346346+ refresh_line s
347347+348348+let move_right (state : State.t) =
349349+ let s =
350350+ if state.pos > 0 then
351351+ State.override
352352+ ~pos:(state.pos + utf8_next_char_len state.buf state.pos)
353353+ state
354354+ else state
355355+ in
356356+ refresh_line s
357357+358358+let reverse_incr_search ~history (state : State.t) =
359359+ let has_match = ref true in
360360+ let search_buf = Buffer.create 16 in
361361+ let search_pos = ref 0 in
362362+ let search_dir = ref (-1) in
363363+ let h = history "" in
364364+ let history_len = List.length h in
365365+ let saved_buf = Bytes.copy state.buf in
366366+ let exception Completed of State.t in
367367+ let rec loop state : State.t =
368368+ let prompt =
369369+ if !has_match then
370370+ Fmt.str "(reverse-i-search)`%s': " (Buffer.contents search_buf)
371371+ else
372372+ Fmt.str "(failed-reverse-i-search)`%s': " (Buffer.contents search_buf)
373373+ in
374374+ let new_char = ref false in
375375+ let state = State.override ~pos:0 state in
376376+ let state =
377377+ refresh_single_line ~flags:[ Rewrite ] ~prompt:(String.to_bytes prompt)
378378+ state
379379+ in
380380+ let state =
381381+ match read_char state with
382382+ | `Editing -> loop state
383383+ | `None -> loop state
384384+ | `Some c -> (
385385+ match key_of_char c with
386386+ | Backspace ->
387387+ if Buffer.length search_buf > 0 then begin
388388+ (* Pretty wasteful... *)
389389+ let s = Buffer.contents search_buf in
390390+ Buffer.clear search_buf;
391391+ Buffer.add_substring search_buf s 0 (String.length s - 1);
392392+ search_pos := 0
393393+ end;
394394+ state
395395+ | Ctrl_p ->
396396+ search_dir := -1;
397397+ if !search_pos >= history_len then search_pos := history_len - 1;
398398+ state
399399+ | Ctrl_r ->
400400+ search_dir := 1;
401401+ if !search_pos < 0 then search_pos := 0;
402402+ state
403403+ | Ctrl_g ->
404404+ let l = Bytes.length saved_buf in
405405+ Bytes.blit saved_buf 0 state.buf 0 l;
406406+ let state = refresh_line (State.override ~pos:l ~len:l state) in
407407+ raise (Completed state)
408408+ | Enter ->
409409+ let state = State.override ~pos:state.len state in
410410+ raise (Completed state)
411411+ | _ ->
412412+ if Char.compare c ' ' > 0 then begin
413413+ new_char := true;
414414+ Buffer.add_char search_buf c;
415415+ search_pos := 0;
416416+ state
417417+ end
418418+ else
419419+ State.override ~pos:state.len state |> refresh_line |> fun s ->
420420+ raise (Completed s))
421421+ in
422422+ has_match := false;
423423+ let state =
424424+ if Buffer.length search_buf > 0 then begin
425425+ let rec inner_loop () =
426426+ if !search_pos >= 0 && !search_pos < history_len then begin
427427+ let entry = List.nth h !search_pos in
428428+ match
429429+ ( Astring.String.cut ~sep:(Buffer.contents search_buf) entry,
430430+ !new_char
431431+ || not
432432+ @@ String.equal entry (Bytes.to_string (State.buf state)) )
433433+ with
434434+ | Some (_l, _r), true ->
435435+ has_match := true;
436436+ Bytes.blit_string entry 0 state.buf 0 (String.length entry);
437437+ let state = State.override ~len:(String.length entry) state in
438438+ state
439439+ | _ ->
440440+ search_pos := !search_pos + !search_dir;
441441+ inner_loop ()
442442+ end
443443+ else state
444444+ in
445445+ inner_loop ()
446446+ end
447447+ else state
448448+ in
449449+ loop state
450450+ in
451451+ try loop state with Completed state -> state
452452+453453+let edit_history dir fn (state : State.t) =
454454+ let saved_state = state in
455455+ let current_buf = Bytes.sub_string state.buf 0 state.len in
456456+ let state =
457457+ match (dir, state.history_index) with
458458+ | `Prev, -1 ->
459459+ State.override ~history:(fn current_buf) ~history_index:0
460460+ ~saved_buf:current_buf state
461461+ | `Prev, m ->
462462+ let max_history = List.length state.history in
463463+ if m < max_history - 1 then
464464+ State.override ~history_index:(state.history_index + 1) state
465465+ else state
466466+ | `Next, m when m >= 0 ->
467467+ State.override ~history_index:(state.history_index - 1) state
468468+ | _ -> state
469469+ in
470470+ match (state.history, state.history_index) with
471471+ | [], _ -> saved_state
472472+ | _, -1 ->
473473+ let len = String.length state.saved_buf in
474474+ State.override ~buf:(Bytes.of_string state.saved_buf) ~pos:len ~len state
475475+ |> refresh_line
476476+ | _ ->
477477+ let max_history = List.length state.history in
478478+ let idx = min max_history state.history_index in
479479+ let s = List.nth state.history idx in
480480+ let s_len = String.length s in
481481+ State.override ~buf:(Bytes.of_string s) ~pos:s_len ~len:s_len state
482482+ |> refresh_line
483483+484484+let edit_feed ~history state =
485485+ match read_char state with
486486+ | `Editing -> Editing state
487487+ | `None -> Finished None
488488+ | `Some c -> (
489489+ let uc = Uchar.of_char c in
490490+ let state, c =
491491+ if state.in_completion || key_of_char c = Tab then
492492+ match state.complete with
493493+ | None -> (state, `Char c)
494494+ | Some cn -> complete_line state c cn
495495+ else (state, `Char c)
496496+ in
497497+ match c with
498498+ | `Edit_more -> Editing state
499499+ | `Char c -> (
500500+ match key_of_char c with
501501+ | Enter -> Finished (Some (Bytes.sub state.buf 0 state.len))
502502+ | Ctrl_d ->
503503+ if Int.equal state.len 0 then Finished None else Editing state
504504+ | Ctrl_c -> Ctrl_c
505505+ | Ctrl_b -> Editing (move_left state)
506506+ | Ctrl_f -> Editing (move_right state)
507507+ | Ctrl_r -> Editing (reverse_incr_search ~history state)
508508+ | Backspace -> Editing (edit_backspace state)
509509+ | Tab -> Editing state
510510+ | Escape_sequence -> (
511511+ let c1 =
512512+ read_char state |> function `Some c -> c | _ -> assert false
513513+ in
514514+ let c2 =
515515+ read_char state |> function `Some c -> c | _ -> assert false
516516+ in
517517+ match (c1, c2) with
518518+ | '[', 'A' -> Editing (edit_history `Prev history state)
519519+ | '[', 'B' -> Editing (edit_history `Next history state)
520520+ | '[', 'C' -> Editing (move_right state)
521521+ | '[', 'D' -> Editing (move_left state)
522522+ | _ -> Editing state)
523523+ | Unknown _ | _ ->
524524+ let state = edit_insert state uc in
525525+ Editing state))
526526+527527+type result = String of string option | Ctrl_c
528528+529529+let blocking_edit ?complete ~history ~stdin ~stdout buf ~prompt =
530530+ let state = State.make ?complete ~prompt buf in
531531+ let res =
532532+ edit_start ~stdin ~stdout state @@ fun state ->
533533+ let rec loop = function
534534+ | Editing state -> loop (edit_feed ~history state)
535535+ | Finished s -> String (Option.map Bytes.to_string s)
536536+ | Ctrl_c -> Ctrl_c
537537+ in
538538+ loop (edit_feed ~history state)
539539+ in
540540+ res
541541+542542+type history = string -> string list
543543+544544+let bruit ?complete ?(history = fun _ -> []) prompt =
545545+ let prompt = Bytes.of_string prompt in
546546+ let buf = Bytes.make max_line '\000' in
547547+ if not (Unix.isatty Unix.stdin) then failwith "Stdin is not a tty"
548548+ else
549549+ blocking_edit ?complete ~history ~stdin:Unix.stdin ~stdout:Unix.stdout buf
550550+ ~prompt
551551+552552+(*
553553+ * Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
554554+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
555555+ *
556556+ * All rights reserved.
557557+ *
558558+ * Redistribution and use in source and binary forms, with or without
559559+ * modification, are permitted provided that the following conditions are
560560+ * met:
561561+ *
562562+ * * Redistributions of source code must retain the above copyright
563563+ * notice, this list of conditions and the following disclaimer.
564564+ *
565565+ * * Redistributions in binary form must reproduce the above copyright
566566+ * notice, this list of conditions and the following disclaimer in the
567567+ * documentation and/or other materials provided with the distribution.
568568+ *
569569+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
570570+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
571571+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
572572+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
573573+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
574574+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
575575+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
576576+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
577577+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
578578+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
579579+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *)
+18
vendor/bruit/src/bruit.mli
···11+(** {! Bruit} is a port of the C library
22+ {{:https://github.com/antirez/linenoise} Linenoise} to OCaml. It is not as
33+ feature complete as the library, contributions are very welcome.
44+55+ The main entry point to the library is {! bruit}. *)
66+77+type history = string -> string list
88+(** The history callback that provides the user with the current line and
99+ expects a list of history items to scroll through using the arrow keys. *)
1010+1111+type result = String of string option | Ctrl_c
1212+1313+val bruit :
1414+ ?complete:(string -> string list) -> ?history:history -> string -> result
1515+(** [bruit ?complete prompt] reads from [stdin] and returns the read string if
1616+ any, and on [ctrl+c] returns {! Ctrl_c}.
1717+1818+ @param complete Used for completions, defaults to [None]. *)