package inquirer_oc

  1. Overview
  2. Docs

Source file terminal_util.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
open Ochalk

let get_1_char =
  let termio = Unix.tcgetattr Unix.stdin in
  let () =
    termio.Unix.c_icanon <- false;
    termio.Unix.c_vmin <- 1;
    termio.Unix.c_vtime <- 0;
    termio.Unix.c_echo <- false;
    Unix.tcsetattr Unix.stdin Unix.TCSADRAIN termio
  in
  fun () -> input_char stdin

let is_enter c = Char.code c = 10 (* ASCII code for '\n' *)

let read_arrow_key () =
  let esc = get_1_char () in
  if is_enter esc then Some "Enter"
  else if esc = '\027' then
    (* escape character *)
    let lb = get_1_char () in
    if lb = '[' then
      (* left bracket *)
      let c = get_1_char () in
      match c with
      | 'A' -> Some "Up"
      | 'B' -> Some "Down"
      | 'C' -> Some "Right"
      | 'D' -> Some "Left"
      | _ -> None
    else None
  else None

(* x方向の画面中央 *)
let clear_screen () = print_endline "\027[1;1H\027[2J"

let read_and_print () =
  (* 最初に改行する *)
  print_newline ();
  (* 入力を受け付ける *)
  let rec loop chars finished count =
    (* 入力が終了したらリストの内容を逆順にして文字列に変換して返す *)
    if finished then
      String.concat "" (List.map (String.make 1) (List.rev chars))
    else
      (* 標準入力から1文字読み込む *)
      let c = input_char stdin in
      (* 文字に応じて処理する *)
      match c with
      | '\n' ->
          (* Enterキーが押されたとき *)
          (* 入力を終了する *)
          let finished = true in

          (* ここで\rと\027[2Kを同時に使用するとおかしくなった *)

          (* カーソルを行頭に戻す *)
          print_string "\r";
          (* 行全体を消去する *)
          print_string "\027[2K";

          (* 現在の行を消す *)
          print_string
            (to_emerald
               (String.concat "" (List.map (String.make 1) (List.rev chars))));
          (* 改行を出力する *)
          print_newline ();
          (* 再帰呼び出しする *)
          loop chars finished count
      | '\b' | '\127' ->
          (* BSかdelが押されたとき *)
          (* 保存したリストから1文字削除する *)
          let chars =
            match chars with [] -> [] (* リストが空なら何もしない *) | _ :: rest -> rest
          in
          (* リストの先頭要素を捨てる *)
          (* コンソールに表示されている文字数を減らす *)
          let count = max 0 (count - 1) in
          (* BSと空白とBSを出力して1文字消す *)
          if count >= 0 then (
            print_string "\b \b";
            flush stdout);
          (* 再帰呼び出しする *)
          loop chars finished count
      | '\027' ->
          (* エスケープ記号が出たとき *)
          (* エスケープシーケンスを読み飛ばす関数を定義する *)
          let rec skip_escape () =
            let c = input_char stdin in
            match c with
            | 'A' .. 'Z' | 'a' .. 'z' -> () (* エスケープシーケンスの終わりを見つけたら何もしない *)
            | _ -> skip_escape ()
          in
          (* それ以外の文字は読み飛ばす *)
          skip_escape ();

          loop chars finished count (* 再帰呼び出しする *)
      | _ ->
          (* それ以外の文字が出たとき *)
          (* 保存したリストに追加する *)
          let chars = c :: chars in
          (* コンソールに表示する *)
          let next_count = List.length chars in
          print_char c;
          flush stdout;
          (* コンソールに表示されている文字数を増やす *)
          (* 再帰呼び出しする *)
          loop chars finished next_count
  in
  loop [] false 0
OCaml

Innovation. Community. Security.