aboutsummaryrefslogtreecommitdiff
path: root/lib/input_prompt.ml
blob: a4077419a954d7ac526b91bfa8fb9b0ea64c93c0 (plain)
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
module Grep = Grep
module Common = Common
open Notty

type state =
  { (* Pre = before the cursor, post = after the cursor *)
    user_input_pre : string
  ; user_input_post : string
  ; on_enter : string -> unit
  ; on_cancel : unit -> unit
  ; prompt : string
  ; screen : I.t
  }

let rec render
  t
  ({ user_input_pre; user_input_post; on_enter; on_cancel; prompt; screen } as state)
  =
  let size_x, size_y = Common.Term.size t in
  Common.Term.cursor
    t
    (Some (String.length user_input_pre + String.length prompt + 3, size_y));
  let img =
    let open I in
    I.strf " %s " ~attr:(A.st A.reverse) prompt
    <|> I.strf " %s%s%s" user_input_pre user_input_post (String.make size_x ' ')
    |> I.pad ~l:0 ~t:(size_y - 1)
    </> screen
  in
  Common.Term.image t img;
  match Common.Term.event t with
  | `End | `Key (`Escape, []) | `Key (`ASCII 'G', [ `Ctrl ]) | `Key (`ASCII 'C', [ `Ctrl ])
    -> on_cancel ()
  | `Key (`Enter, []) -> on_enter (user_input_pre ^ user_input_post)
  | `Key (`Backspace, []) ->
    if String.equal "" (user_input_pre ^ user_input_post)
    then on_cancel ()
    else (
      let state =
        { state with
          user_input_pre =
            String.sub user_input_pre 0 (max (String.length user_input_pre - 1) 0)
        }
      in
      render t state)
  | `Resize _ -> render t state
  | `Key (`Arrow `Left, []) ->
    if user_input_pre = ""
    then render t state
    else (
      let char_to_move = Str.last_chars user_input_pre 1
      and new_pre = Str.string_before user_input_pre (String.length user_input_pre - 1) in
      let new_post = char_to_move ^ user_input_post in
      render t { state with user_input_pre = new_pre; user_input_post = new_post })
  | `Key (`Arrow `Right, []) ->
    if user_input_post = ""
    then render t state
    else (
      let char_to_move = Str.first_chars user_input_post 1
      and new_post = Str.string_after user_input_post 1 in
      let new_pre = user_input_pre ^ char_to_move in
      render t { state with user_input_pre = new_pre; user_input_post = new_post })
  | `Key (`ASCII c, []) ->
    let state = { state with user_input_pre = user_input_pre ^ String.make 1 c } in
    render t state
  | _ -> render t state