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
module Id_map = Map.Make (String)
type t = {
order : Cell.id list;
by_id : Cell.t Id_map.t;
metadata : (string * string) list;
}
let empty () = { order = []; by_id = Id_map.empty; metadata = [] }
let of_cells ?(metadata = []) cs =
let by_id =
List.fold_left (fun m c -> Id_map.add (Cell.id c) c m) Id_map.empty cs
in
let order = List.map Cell.id cs in
{ order; by_id; metadata }
let cells d = List.filter_map (fun id -> Id_map.find_opt id d.by_id) d.order
let length d = List.length d.order
let metadata d = d.metadata
let set_metadata metadata d = { d with metadata }
let nth i d =
let rec loop j = function
| [] -> None
| id :: rest ->
if j = i then Id_map.find_opt id d.by_id else loop (j + 1) rest
in
if i < 0 then None else loop 0 d.order
let find id d = Id_map.find_opt id d.by_id
let find_index id d =
let rec loop i = function
| [] -> None
| hd :: rest -> if String.equal hd id then Some i else loop (i + 1) rest
in
loop 0 d.order
let insert ~pos cell d =
let id = Cell.id cell in
let by_id = Id_map.add id cell d.by_id in
let pos = max 0 pos in
let rec loop i acc = function
| rest when i = pos -> List.rev_append acc (id :: rest)
| hd :: rest -> loop (i + 1) (hd :: acc) rest
| [] -> List.rev (id :: acc)
in
{ d with order = loop 0 [] d.order; by_id }
let remove id d =
if not (Id_map.mem id d.by_id) then d
else
let by_id = Id_map.remove id d.by_id in
let order = List.filter (fun i -> not (String.equal i id)) d.order in
{ d with order; by_id }
let replace id cell d =
if not (Id_map.mem id d.by_id) then d
else
let new_id = Cell.id cell in
let by_id = Id_map.remove id d.by_id in
let by_id = Id_map.add new_id cell by_id in
let order =
if String.equal id new_id then d.order
else List.map (fun i -> if String.equal i id then new_id else i) d.order
in
{ d with order; by_id }
let move id ~pos d =
match find_index id d with
| None -> d
| Some i ->
if i = pos then d
else
let order = List.filter (fun x -> not (String.equal x id)) d.order in
let pos = if pos > i then pos - 1 else pos in
let pos = max 0 pos in
let rec loop j acc = function
| rest when j = pos -> List.rev_append acc (id :: rest)
| hd :: rest -> loop (j + 1) (hd :: acc) rest
| [] -> List.rev (id :: acc)
in
{ d with order = loop 0 [] order }
let update id f d =
match find id d with None -> d | Some c -> replace id (f c) d
let clear_all_outputs d =
let by_id = Id_map.map Cell.clear_outputs d.by_id in
{ d with by_id }