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
type id = string
let () = Random.self_init ()
let fresh_id () =
let n = 12 in
let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in
let b = Bytes.create (n + 2) in
Bytes.unsafe_set b 0 'c';
Bytes.unsafe_set b 1 '_';
for i = 0 to n - 1 do
Bytes.unsafe_set b (i + 2) chars.[Random.int 36]
done;
Bytes.unsafe_to_string b
type output =
| Stdout of string
| Stderr of string
| Error of string
| Display of { mime : string; data : string }
type Format.stag += Display_tag of { mime : string; data : string }
type attrs = { collapsed : bool; hide_source : bool }
let default_attrs = { collapsed = false; hide_source = false }
type t =
| Code of {
id : id;
source : string;
language : string;
outputs : output list;
execution_count : int;
attrs : attrs;
}
| Text of { id : id; source : string; attrs : attrs }
let code ?id ?(language = "ocaml") ?(attrs = default_attrs) source =
let id = match id with Some id -> id | None -> fresh_id () in
Code { id; source; language; outputs = []; execution_count = 0; attrs }
let text ?id ?(attrs = default_attrs) source =
let id = match id with Some id -> id | None -> fresh_id () in
Text { id; source; attrs }
let id = function Code c -> c.id | Text t -> t.id
let source = function Code c -> c.source | Text t -> t.source
let attrs = function Code c -> c.attrs | Text t -> t.attrs
let set_source s = function
| Code c -> Code { c with source = s }
| Text t -> Text { t with source = s }
let set_attrs a = function
| Code c -> Code { c with attrs = a }
| Text t -> Text { t with attrs = a }
let set_outputs os = function
| Code c -> Code { c with outputs = os }
| Text _ as t -> t
let apply_cr s =
let lines = String.split_on_char '\n' s in
let apply_line line =
match String.rindex_opt line '\r' with
| None -> line
| Some i -> String.sub line (i + 1) (String.length line - i - 1)
in
String.concat "\n" (List.map apply_line lines)
let rec append_or_coalesce o acc = function
| [] -> List.rev (o :: acc)
| [ Stdout prev ] -> begin
match o with
| Stdout next -> List.rev (Stdout (apply_cr (prev ^ next)) :: acc)
| _ -> List.rev (o :: Stdout prev :: acc)
end
| out :: rest -> append_or_coalesce o (out :: acc) rest
let append_output o = function
| Code c -> Code { c with outputs = append_or_coalesce o [] c.outputs }
| Text _ as t -> t
let clear_outputs = function
| Code c -> Code { c with outputs = [] }
| Text _ as t -> t
let increment_execution_count = function
| Code c -> Code { c with execution_count = c.execution_count + 1 }
| Text _ as t -> t