Source file keyseq_terminal.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
let hide_cursor = "\027[?25l"
let show_cursor = "\027[?25h"
let write (out : out_channel) (s : string) : unit =
output_string out s;
flush out
let render_insert (out : out_channel) (u : Uchar.t) : unit =
let buf = Buffer.create 4 in
Buffer.add_utf_8_uchar buf u;
write out (Buffer.contents buf)
let render_delete (out : out_channel) : unit = write out "\b \b"
let render_idle (out : out_channel) (d : float) : unit =
let half = 0.5 in
let hidden = ref false in
let remaining = ref d in
while !remaining >= half do
Unix.sleepf half;
remaining := !remaining -. half;
if !hidden then (
write out show_cursor;
hidden := false)
else (
write out hide_cursor;
hidden := true)
done;
if !remaining > 0.0 then Unix.sleepf !remaining;
if !hidden then write out show_cursor
let run ?(out = stdout) ?(on_sound = fun (_ : Keyseq.sound) -> ())
(events : Keyseq.event list) : unit =
List.iter
(fun (e : Keyseq.event) ->
match e with
| Keyseq.Insert u -> render_insert out u
| Keyseq.Delete -> render_delete out
| Keyseq.Sleep d -> Unix.sleepf d
| Keyseq.Idle d -> render_idle out d
| Keyseq.Sound s -> on_sound s)
events
let is_ws (c : char) : bool =
match c with ' ' | '\t' | '\n' | '\r' -> true | _ -> false
let split_words (text : string) : string list =
let len = String.length text in
let rec loop i acc =
if i >= len then List.rev acc
else if is_ws text.[i] then loop (i + 1) acc
else
let j = ref i in
while !j < len && not (is_ws text.[!j]) do
incr j
done;
loop !j (String.sub text i (!j - i) :: acc)
in
loop 0 []
let utf8_length (s : string) : int =
let len = String.length s in
let rec loop i n =
if i >= len then n
else
let d = String.get_utf_8_uchar s i in
loop (i + Uchar.utf_decode_length d) (n + 1)
in
loop 0 0
let wrap ~(width : int) (text : string) : string =
if width <= 0 then invalid_arg "Keyseq_terminal.wrap: width must be > 0";
let buf = Buffer.create (String.length text) in
let col = ref 0 in
let first_on_line = ref true in
List.iter
(fun word ->
let wlen = utf8_length word in
if (not !first_on_line) && !col + 1 + wlen > width then begin
Buffer.add_char buf '\n';
col := 0;
first_on_line := true
end;
if not !first_on_line then begin
Buffer.add_char buf ' ';
incr col
end;
Buffer.add_string buf word;
col := !col + wlen;
first_on_line := false)
(split_words text);
Buffer.contents buf