Source file inuit_cursor.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
open Inuit_base

type 'flags cursor = {
  region : 'flags Inuit_region.t;
  flags  : 'flags list;
  indent : int;
}

type 'flags clickable = [> `Clickable | `Clicked] as 'flags

let null = { region = Inuit_region.null; flags = []; indent = 0 }

let count_char str chr =
  let count = ref 0 in
  for i = 0 to String.length str - 1 do
    if str.[i] = chr then incr count;
  done;
  !count

let indent_text col text =
  if col <= 0 then text else
    let count = count_char text '\n' in
    if count = 0 then text else
      let buf = Bytes.make (String.length text + col * count) ' ' in
      let rec fill src dst =
        match String.index_from text src '\n' with
        | exception Not_found ->
          Bytes.blit_string text src buf dst (String.length text - src)
        | src' ->
          let len = src' - src + 1 in
          Bytes.blit_string text src buf dst len;
          fill (src' + 1) (dst + len + col)
      in
      fill 0 0;
      Bytes.unsafe_to_string buf

let text t ?(flags=t.flags) text =
  Inuit_region.append t.region flags (indent_text t.indent text)

let clear t =
  Inuit_region.clear t.region

let kill t =
  Inuit_region.kill t.region

let sub t = { t with region = Inuit_region.sub t.region }

let observe { region; flags; indent } f =
  let observer region =
    let t' = { region; flags; indent } in
    fun side patch -> f t' side patch
  in
  { region = Inuit_region.sub ~observer region; flags; indent }

let is_closed t = Inuit_region.is_closed t.region

let mem_flag flag cursor =
  List.mem flag cursor.flags

let add_flag flag cursor =
  if mem_flag flag cursor
  then cursor
  else {cursor with flags = flag :: cursor.flags}

let rem_flag flag cursor =
  if mem_flag flag cursor
  then {cursor with flags = List.filter ((<>) flag) cursor.flags}
  else cursor

let get_flags t = t.flags

let with_flags flags t = { t with flags }

let region t = t.region

let clickable t f =
  let t = add_flag `Clickable t in
  observe t (
    fun t' side patch ->
      let {Patch. flags} =  patch in
      if List.mem `Clicked flags then
        (List.filter ((<>) `Clicked) flags, Some (fun () -> f t'))
      else
        (flags, None)
  )

let printf t ?flags fmt =
  Printf.ksprintf (text t ?flags) fmt

let link t ?flags fmt =
  Printf.ksprintf (fun str f -> text (clickable t f) ?flags str) fmt

let cursor_of_region ?(flags=[]) ?(indent=0) region =
  { region; flags = flags; indent }

let make () =
  let region, pipe = Inuit_region.make () in
  cursor_of_region region, pipe

let get_indent t = t.indent

let with_indent t indent = {t with indent}

let shift_indent t indent = {t with indent = max 0 (t.indent + indent) }