···11open Notty
22-open Angstrom
3244-type op = Buffer.t -> unit
33+module Internal = struct
44+ type op = Buffer.t -> unit
5566-(* let ( & ) op1 op2 buf =
77- op1 buf;
88- op2 buf
66+ let invalid_arg fmt = Format.kasprintf invalid_arg fmt
971010- let ( <| ), ( <. ), ( <! ) = Buffer.(add_string, add_char, add_decimal) *)
1111-let invalid_arg fmt = Format.kasprintf invalid_arg fmt
1212-let sts = [ ";1"; ";3"; ";4"; ";5"; ";7" ]
88+ let attr_of_ints fg bg st =
99+ A.fg @@ A.unsafe_color_of_int fg
1010+ |> A.( ++ ) (A.bg @@ A.unsafe_color_of_int bg)
1111+ |> A.( ++ ) (A.st @@ A.unsafe_style_of_int st)
1212+ ;;
13131414-let attr_of_ints fg bg st =
1515- A.fg @@ A.unsafe_color_of_int fg
1616- |> A.( ++ ) (A.bg @@ A.unsafe_color_of_int bg)
1717- |> A.( ++ ) (A.st @@ A.unsafe_style_of_int st)
1818-;;
1414+ let fg_int i = A.fg @@ A.unsafe_color_of_int i
1515+ let bg_int i = A.bg @@ A.unsafe_color_of_int i
19162020-let fg_int i = A.fg @@ A.unsafe_color_of_int i
2121-let bg_int i = A.bg @@ A.unsafe_color_of_int i
1717+ let print_image_escaped img =
1818+ print_endline "image:";
1919+ img |> Notty.Render.pp_image @@ Format.str_formatter;
2020+ print_endline (Format.flush_str_formatter () |> String.escaped)
2121+ ;;
22222323-let print_image_escaped img =
2424- print_endline "image:";
2525- img |> Notty.Render.pp_image @@ Format.str_formatter;
2626- print_endline (Format.flush_str_formatter () |> String.escaped)
2727-;;
2323+ let print_image img =
2424+ print_endline "image:";
2525+ img |> Notty.Render.pp_image @@ Format.str_formatter;
2626+ print_endline (Format.flush_str_formatter ())
2727+ ;;
28282929-let print_image img =
3030- print_endline "image:";
3131- img |> Notty.Render.pp_image @@ Format.str_formatter;
3232- print_endline (Format.flush_str_formatter ())
3333-;;
2929+ let print_attr img =
3030+ print_endline "attr:";
3131+ img |> Notty.Render.pp_attr @@ Format.str_formatter;
3232+ print_endline (Format.flush_str_formatter ())
3333+ ;;
34343535-let print_attr img =
3636- print_endline "attr:";
3737- img |> Notty.Render.pp_attr @@ Format.str_formatter;
3838- print_endline (Format.flush_str_formatter ())
3939-;;
3535+ (** Like fold left except we run the first element through init to get the state*)
3636+ let fold_left_pre (f : 'acc -> 'a -> 'acc) (init : 'a -> 'acc) (input : 'a list) =
3737+ match input with
3838+ | [] ->
3939+ invalid_arg "empty list"
4040+ | x :: xs ->
4141+ let state = init x in
4242+ xs |> List.fold_left f state
4343+ ;;
4444+end
40454141-let parse_escape_seq =
4242- let open A in
4343- (* let digit = satisfy (function '0' .. '9' -> true | _ -> false) in *)
4444- let digits = take_while1 (function '0' .. '9' -> true | _ -> false) in
4545- (* let color_code = digits >>| int_of_string in *)
4646- let param = digits <* option ' ' (char ';') in
4747- let params = many (param >>| int_of_string) in
4848- let escape_sequence = char '\027' *> char '[' *> params <* char 'm' in
4949- let attr_of_params = function
5050- | [] ->
5151- empty
5252- | 0 :: _ ->
5353- empty
5454- | 1 :: _ ->
5555- st bold
5656- | 2 :: _ ->
5757- st italic
5858- | 4 :: _ ->
5959- st underline
6060- | 5 :: _ ->
6161- st blink
6262- | 7 :: _ ->
6363- st reverse
6464- | 30 :: _ ->
6565- fg black
6666- | 31 :: _ ->
6767- fg red
6868- | 32 :: _ ->
6969- fg green
7070- | 33 :: _ ->
7171- fg yellow
7272- | 34 :: _ ->
7373- fg blue
7474- | 35 :: _ ->
7575- fg magenta
7676- | 36 :: _ ->
7777- fg cyan
7878- | 37 :: _ ->
7979- fg white
8080- | 38 :: 5 :: color :: _ ->
8181- fg (unsafe_color_of_int (0x01000000 lor color))
8282- | 40 :: _ ->
8383- bg black
8484- | 41 :: _ ->
8585- bg red
8686- | 42 :: _ ->
8787- bg green
8888- | 43 :: _ ->
8989- bg yellow
9090- | 44 :: _ ->
9191- bg blue
9292- | 45 :: _ ->
9393- bg magenta
9494- | 46 :: _ ->
9595- bg cyan
9696- | 47 :: _ ->
9797- bg white
9898- | 48 :: 5 :: color :: _ ->
9999- bg (unsafe_color_of_int (0x01000000 lor color))
100100- | _ ->
101101- empty
102102- in
103103- escape_sequence >>| attr_of_params
104104-;;
4646+module Parser = struct
4747+ open Internal
4848+4949+ let parse_escape_seq =
5050+ let open A in
5151+ let open Angstrom in
5252+ (* let digit = satisfy (function '0' .. '9' -> true | _ -> false) in *)
5353+ let digits = take_while1 (function '0' .. '9' -> true | _ -> false) in
5454+ (* let color_code = digits >>| int_of_string in *)
5555+ let param = digits <* option ' ' (char ';') in
5656+ let params = many (param >>| int_of_string) in
5757+ let escape_sequence = char '\027' *> char '[' *> params <* char 'm' in
5858+ let attr_of_params = function
5959+ | [] ->
6060+ empty
6161+ | 0 :: _ ->
6262+ empty
6363+ | 1 :: _ ->
6464+ st bold
6565+ | 2 :: _ ->
6666+ st italic
6767+ | 4 :: _ ->
6868+ st underline
6969+ | 5 :: _ ->
7070+ st blink
7171+ | 7 :: _ ->
7272+ st reverse
7373+ | 30 :: _ ->
7474+ fg black
7575+ | 31 :: _ ->
7676+ fg red
7777+ | 32 :: _ ->
7878+ fg green
7979+ | 33 :: _ ->
8080+ fg yellow
8181+ | 34 :: _ ->
8282+ fg blue
8383+ | 35 :: _ ->
8484+ fg magenta
8585+ | 36 :: _ ->
8686+ fg cyan
8787+ | 37 :: _ ->
8888+ fg white
8989+ | 38 :: 5 :: color :: _ ->
9090+ fg (unsafe_color_of_int (0x01000000 lor color))
9191+ | 40 :: _ ->
9292+ bg black
9393+ | 41 :: _ ->
9494+ bg red
9595+ | 42 :: _ ->
9696+ bg green
9797+ | 43 :: _ ->
9898+ bg yellow
9999+ | 44 :: _ ->
100100+ bg blue
101101+ | 45 :: _ ->
102102+ bg magenta
103103+ | 46 :: _ ->
104104+ bg cyan
105105+ | 47 :: _ ->
106106+ bg white
107107+ | 48 :: 5 :: color :: _ ->
108108+ bg (unsafe_color_of_int (0x01000000 lor color))
109109+ | _ ->
110110+ empty
111111+ in
112112+ escape_sequence >>| attr_of_params
113113+ ;;
105114106106-let%expect_test "escape_parser" =
107107- let test_str = "\027[32m" in
108108- let res = parse_string ~consume:All parse_escape_seq test_str |> Result.get_ok in
109109- print_attr res;
110110- [%expect {|
115115+ let%expect_test "escape_parser" =
116116+ let test_str = "\027[32m" in
117117+ let res =
118118+ Angstrom.parse_string ~consume:All parse_escape_seq test_str |> Result.get_ok
119119+ in
120120+ print_attr res;
121121+ [%expect {|
111122 attr:
112123 [0m<[0;32mATTR[0m[K[0m>[0m |}]
113113-;;
124124+ ;;
114125115115-let parse_ansi_escape_codes (input : string) =
116116- let attr = parse_escape_seq in
117117- let substring = take_while (fun c -> c <> '\027') in
118118- let pair =
119119- attr >>= fun a ->
120120- substring >>= fun s -> return (a, s)
121121- in
122122- (* if we don't start on an escape we can match one here*)
123123- let prefix = option "" substring >>| fun s -> A.empty, s in
124124- let pairs =
125125- prefix >>= fun prefix ->
126126- many pair >>= fun pairs ->
127127- if prefix |> snd == "" then return pairs else prefix :: pairs |> return
128128- in
129129- parse_string ~consume:Prefix pairs input
130130-;;
131131-132132-(** Like fold left except we run the first element through init to get the state*)
133133-let fold_left_pre (f : 'acc -> 'a -> 'acc) (init : 'a -> 'acc) (input : 'a list) =
134134- match input with
135135- | [] ->
136136- invalid_arg "empty list"
137137- | x :: xs ->
138138- let state = init x in
139139- xs |> List.fold_left f state
140140-;;
126126+ let parse_ansi_escape_codes (input : string) =
127127+ let open Angstrom in
128128+ let attr = parse_escape_seq in
129129+ let substring = take_while (fun c -> c <> '\027') in
130130+ let pair =
131131+ attr >>= fun a ->
132132+ substring >>= fun s -> return (a, s)
133133+ in
134134+ (* if we don't start on an escape we can match one here*)
135135+ let prefix = option "" substring >>| fun s -> A.empty, s in
136136+ let pairs =
137137+ prefix >>= fun prefix ->
138138+ many pair >>= fun pairs ->
139139+ if prefix |> snd == "" then return pairs else prefix :: pairs |> return
140140+ in
141141+ parse_string ~consume:Prefix pairs input
142142+ ;;
143143+end
141144142142-let string_to_image ?(extra_attr = A.empty) str =
145145+(** Converts a string with ansi escape codes to a notty image.
146146+ It parses the escape codes and then creates notty images from that by applying the styles*)
147147+let ansi_string_to_image ?(extra_attr = A.empty) str =
143148 let str =
144149 (* replace any carrriage returns becasue notty doesn't know what to do with them*)
145150 Base.String.Search_pattern.replace_all
···150155 (Base.String.Search_pattern.create "\r")
151156 ~with_:"\n"
152157 in
153153- match parse_ansi_escape_codes str with
158158+ match Parser.parse_ansi_escape_codes str with
154159 | Error a ->
155160 Printf.printf "restut: %s" a;
156161 Error a
157162 | Ok coded_strs ->
158158- (* print_endline "parsed"; *)
159163 let locate_newlines codes =
160164 codes
161165 |> List.concat_map (fun (attr, str) ->
162162- (* print_attr attr; *)
163163- (* print_endline str; *)
164166 str
165167 |> String.split_on_char '\n'
166168 |> List.map (fun x -> `Image (I.string A.(attr ++ extra_attr) x))
167169 |> Base.List.intersperse ~sep:`Newline)
168170 in
169171 let newline_seperated = locate_newlines coded_strs in
170170- (* Printf.printf "len:%d" (List.length newline_seperated); *)
171172 let lines =
172173 let open I in
173173- (* newline_seperated
174174- |> List.iter (fun x -> match x with `Imarge i -> print_image i | _ -> ()); *)
175174 newline_seperated
176175 |> Base.List.fold ~init:([], I.empty) ~f:(fun (images, image) x ->
177176 match x with
···180179 | `Image nextImage ->
181180 images, image <|> nextImage)
182181 |> fst
183183- (* |> List.map (fun x ->
184184- x |> print_image;
185185- x) *)
186182 |> Base.List.reduce ~f:(fun bottom top -> top <-> bottom)
187183 |> Option.value ~default:I.empty
188184 in
189189- let image =
190190- lines
191191- (* |> fold_left_pre
192192- (fun image (attr, str) ->
193193- let parts = str |> String.split_on_char '\n' in
194194- let nextImage =
195195- parts
196196- |> fold_left_pre
197197- (fun image str -> I.( <-> ) image (I.string attr str))
198198- (I.string attr)
199199- in
200200- I.( <|> ) image nextImage)
201201- (fun (attr, str) -> I.string attr str) *)
202202- in
203203- Ok image
185185+ Ok lines
204186;;
205187206206-let escaped_string ?(attr = A.empty) str =
207207- let control_character_index str i =
208208- let len = String.length str in
209209- let i = ref i in
210210- while
211211- let i = !i in
212212- i < len && str.[i] >= ' '
213213- do
214214- incr i
215215- done;
216216- if !i = len then raise Not_found;
217217- !i
218218- in
219219- let rec split str i =
220220- match control_character_index str i with
221221- | j ->
222222- let img = I.string attr (String.sub str i (j - i)) in
223223- img :: split str (j + 1)
224224- | exception Not_found ->
225225- [ I.string attr (if i = 0 then str else String.sub str i (String.length str - i)) ]
226226- in
227227- I.vcat (split str 0)
228228-;;
229229-230230-(* let colored_string s =
231231- s |> parse_ansi_escape_codes
232232- |> List.map (fun (x, str) -> escaped_string ~attr:x str)
233233- |> I.vcat *)
234234-let colored_string ?extra_attr s = s |> string_to_image ?extra_attr |> Result.get_ok
188188+(** Same as ansi_string_to_image, but can throw if a parsing error occurs. I have not seen it fail, should be safe. *)
189189+let colored_string ?extra_attr s = s |> ansi_string_to_image ?extra_attr |> Result.get_ok
+2-1
jj_tui/lib/util.ml
···2828 state_to_verbose_result final_parser_state
2929;;
30303131+Base.List.intersperse
3132let ( <-$ ) f v = Lwd.map ~f (Lwd.get v)
3233let ( $-> ) v f = Lwd.map ~f (Lwd.get v)
3334let ( let$$ ) v f = Lwd.map ~f (Lwd.get v)
3435let ( |>$ ) v f = Lwd.map ~f v
3535-let ( |>$$ ) v2 v f = Lwd.map2 ~f v v2
3636let ( >> ) f g x = g (f x)
3737let ( << ) f g x = f (g x)
3838+let ( |>$$ ) v2 v f = Lwd.map2 ~f v v2