Cli_entries: export formatter setup functions

This commit is contained in:
Benjamin Canou 2018-02-12 15:57:49 +01:00
parent a830c29185
commit 2c2f0a1818
2 changed files with 249 additions and 196 deletions

View File

@ -731,201 +731,217 @@ let group_commands commands =
ref l ])) ref l ]))
let print_group print_command ppf ({ title ; _ }, commands) = let print_group print_command ppf ({ title ; _ }, commands) =
Format.fprintf ppf "@{<title>%s@}@,@{<section>%a@}" Format.fprintf ppf "@{<title>%s@}@,@{<list>%a@}"
title title
(Format.pp_print_list print_command) commands (Format.pp_print_list print_command) commands
let setup_ppf ppf format verbosity = type formatter_state =
Format.formatter_out_functions *
Format.formatter_tag_functions *
bool
let setup_formatter ppf ~format ~verbosity =
let skip = ref false in let skip = ref false in
let orig_out_functions = let orig_out_functions, _, _ as orig_state =
Format.pp_get_formatter_out_functions ppf () in Format.pp_get_formatter_out_functions ppf (),
Format.pp_set_formatter_out_functions ppf Format.pp_get_formatter_tag_functions ppf (),
{ out_string = Format.pp_get_print_tags ppf () in
(fun s b a -> begin
if s = "\000\000\000" then skip := true Format.pp_set_formatter_out_functions ppf
else if s = "\255\255\255" then skip := false { out_string =
else if not !skip then orig_out_functions.out_string s b a) ; (fun s b a ->
out_spaces = (fun n -> if not !skip then orig_out_functions.out_spaces n) ; if s = "\000\000\000" then skip := true
out_newline = (fun () -> if not !skip then orig_out_functions.out_newline ()) ; else if s = "\255\255\255" then skip := false
out_flush = (fun () -> if not !skip then orig_out_functions.out_flush ()) } ; else if not !skip then orig_out_functions.out_string s b a) ;
let levels = ref [] in out_spaces = (fun n -> if not !skip then orig_out_functions.out_spaces n) ;
let setup_level level = out_newline = (fun () -> if not !skip then orig_out_functions.out_newline ()) ;
match verbosity, level with out_flush = (fun () -> if not !skip then orig_out_functions.out_flush ()) } ;
| (`Full, (`Terse | `Short | `Args | `Full)) let levels = ref [] in
| (`Args, (`Terse | `Short | `Args)) let setup_level level =
| (`Short, (`Terse | `Short)) match verbosity, level with
| `Terse, `Terse -> Format.fprintf ppf "@<0>%s" "\255\255\255" | (`Full, (`Terse | `Short | `Args | `Full))
| _ -> Format.fprintf ppf "@<0>%s" "\000\000\000" in | (`Args, (`Terse | `Short | `Args))
let push_level level = | (`Short, (`Terse | `Short))
levels := level :: !levels ; | `Terse, `Terse -> Format.fprintf ppf "@<0>%s" "\255\255\255"
setup_level level in | _ -> Format.fprintf ppf "@<0>%s" "\000\000\000" in
let pop_level _ = let push_level level =
match !levels with levels := level :: !levels ;
| _ :: level :: rest -> levels := level :: rest ; setup_level level setup_level level in
| [ _ ] | [] -> Pervasives.failwith "Cli_entries: unclosed verbosity tag" in let pop_level _ =
push_level `Terse ; match !levels with
match format with | _ :: level :: rest -> levels := level :: rest ; setup_level level
| `Ansi -> | [ _ ] | [] -> Pervasives.failwith "Cli_entries: unclosed verbosity tag" in
let color_num = function push_level `Terse ;
| `Black -> None match format with
| `Red -> Some 1 | `Ansi ->
| `Green -> Some 2 let color_num = function
| `Yellow -> Some 3 | `Black -> None
| `Blue -> Some 4 | `Red -> Some 1
| `Magenta -> Some 5 | `Green -> Some 2
| `Cyan -> Some 6 | `Yellow -> Some 3
| `White -> Some 7 in | `Blue -> Some 4
let ansi_format ppf (fg, bg, b, u) = | `Magenta -> Some 5
Format.fprintf ppf "@<0>%s" "\027[0m" ; | `Cyan -> Some 6
match | `White -> Some 7 in
(match color_num fg with Some n -> [ string_of_int (30 + n) ] | None -> []) @ let ansi_format ppf (fg, bg, b, u) =
(match color_num bg with Some n -> [ string_of_int (40 + n) ] | None -> []) @ Format.fprintf ppf "@<0>%s" "\027[0m" ;
(if b then [ "1" ] else []) @ match
(if u then [ "4" ] else []) (match color_num fg with Some n -> [ string_of_int (30 + n) ] | None -> []) @
with (match color_num bg with Some n -> [ string_of_int (40 + n) ] | None -> []) @
| [] -> () (if b then [ "1" ] else []) @
| l -> Format.fprintf ppf "@<0>%s" ("\027[" ^ String.concat ";" l ^ "m") in (if u then [ "4" ] else [])
Format.pp_set_formatter_tag_functions ppf with
{ mark_open_tag = (fun _ -> "") ; | [] -> ()
mark_close_tag = (fun _ -> "") ; | l -> Format.fprintf ppf "@<0>%s" ("\027[" ^ String.concat ";" l ^ "m") in
print_open_tag = begin function Format.pp_set_formatter_tag_functions ppf
| "title" -> Format.fprintf ppf "@<0>%a" ansi_format (`White, `Black, true, true) { mark_open_tag = (fun _ -> "") ;
| "commandline" -> Format.fprintf ppf "@[<hov 4>" mark_close_tag = (fun _ -> "") ;
| "commanddoc" -> Format.fprintf ppf " @[<v 0>" print_open_tag = begin function
| "opt" -> Format.fprintf ppf "@<0>%a" ansi_format (`Green, `Black, false, false) | "title" -> Format.fprintf ppf "@<0>%a" ansi_format (`White, `Black, true, true)
| "arg" -> Format.fprintf ppf "@<0>%a<" ansi_format (`Yellow, `Black, false, false) | "commandline" -> Format.fprintf ppf "@[<hov 4>"
| "kwd" -> Format.fprintf ppf "@<0>%a" ansi_format (`White, `Black, false, true) | "commanddoc" -> Format.fprintf ppf " @[<v 0>"
| "hilight" -> Format.fprintf ppf "@<0>%a" ansi_format (`Black, `Yellow, false, true) | "opt" -> Format.fprintf ppf "@<0>%a" ansi_format (`Green, `Black, false, false)
| "section" -> Format.fprintf ppf " @[<v 0>" | "arg" -> Format.fprintf ppf "@<0>%a<" ansi_format (`Yellow, `Black, false, false)
| "command" -> Format.fprintf ppf "@[<v 0>" | "kwd" -> Format.fprintf ppf "@<0>%a" ansi_format (`White, `Black, false, true)
| "full" -> push_level `Full | "hilight" -> Format.fprintf ppf "@<0>%a" ansi_format (`Black, `Yellow, false, true)
| "args" -> push_level `Args | "list" -> Format.fprintf ppf " @[<v 0>"
| "short" -> push_level `Short | "command" -> Format.fprintf ppf "@[<v 0>"
| "terse" -> push_level `Terse | "full" -> push_level `Full
| "document" -> Format.fprintf ppf "@[<v 0>" | "args" -> push_level `Args
| _ -> Pervasives.failwith "Cli_entries: invalid semantic tag" | "short" -> push_level `Short
end ; | "terse" -> push_level `Terse
print_close_tag = begin function | "document" -> Format.fprintf ppf "@[<v 0>"
| "title" -> Format.fprintf ppf ":@<0>%s" "\027[0m" | _ -> Pervasives.failwith "Cli_entries: invalid semantic tag"
| "commandline" -> Format.fprintf ppf "@]" end ;
| "commanddoc" -> Format.fprintf ppf "@]" print_close_tag = begin function
| "opt" -> Format.fprintf ppf "@<0>%s" "\027[0m" | "title" -> Format.fprintf ppf ":@<0>%s" "\027[0m"
| "arg" -> Format.fprintf ppf ">@<0>%s" "\027[0m" | "commandline" -> Format.fprintf ppf "@]"
| "kwd" -> Format.fprintf ppf "@<0>%s" "\027[0m" | "commanddoc" -> Format.fprintf ppf "@]"
| "hilight" -> Format.fprintf ppf "@<0>%s" "\027[0m" | "opt" -> Format.fprintf ppf "@<0>%s" "\027[0m"
| "command" | "section" -> Format.fprintf ppf "@]" | "arg" -> Format.fprintf ppf ">@<0>%s" "\027[0m"
| "full" -> pop_level `Full | "kwd" -> Format.fprintf ppf "@<0>%s" "\027[0m"
| "args" -> pop_level `Args | "hilight" -> Format.fprintf ppf "@<0>%s" "\027[0m"
| "short" -> pop_level `Short | "command" | "list" -> Format.fprintf ppf "@]"
| "terse" -> pop_level `Terse | "full" -> pop_level `Full
| "document" -> Format.fprintf ppf "@]" | "args" -> pop_level `Args
| _ -> Pervasives.failwith "Cli_entries: invalid semantic tag" | "short" -> pop_level `Short
end } ; | "terse" -> pop_level `Terse
Format.pp_set_print_tags ppf true | "document" -> Format.fprintf ppf "@]"
| `Plain -> | _ -> Pervasives.failwith "Cli_entries: invalid semantic tag"
Format.pp_set_formatter_tag_functions ppf end } ;
{ mark_open_tag = (fun _ -> "") ; Format.pp_set_print_tags ppf true
mark_close_tag = (fun _ -> "") ; | `Plain ->
print_open_tag = begin function Format.pp_set_formatter_tag_functions ppf
| "title" -> () { mark_open_tag = (fun _ -> "") ;
| "commandline" -> Format.fprintf ppf "@[<hov 4>" mark_close_tag = (fun _ -> "") ;
| "commanddoc" -> Format.fprintf ppf " @[<v 0>" print_open_tag = begin function
| "opt" -> () | "title" -> ()
| "arg" -> Format.fprintf ppf "<" | "commandline" -> Format.fprintf ppf "@[<hov 4>"
| "kwd" -> () | "commanddoc" -> Format.fprintf ppf " @[<v 0>"
| "hilight" -> () | "opt" -> ()
| "section" -> Format.fprintf ppf " @[<v 0>" | "arg" -> Format.fprintf ppf "<"
| "command" -> Format.fprintf ppf "@[<v 0>" | "kwd" -> ()
| "full" -> push_level `Full | "hilight" -> ()
| "args" -> push_level `Args | "list" -> Format.fprintf ppf " @[<v 0>"
| "short" -> push_level `Short | "command" -> Format.fprintf ppf "@[<v 0>"
| "terse" -> push_level `Terse | "full" -> push_level `Full
| "document" -> Format.fprintf ppf "@[<v 0>" | "args" -> push_level `Args
| _ -> Pervasives.failwith "Cli_entries: invalid semantic tag" | "short" -> push_level `Short
end ; | "terse" -> push_level `Terse
print_close_tag = begin function | "document" -> Format.fprintf ppf "@[<v 0>"
| "title" -> Format.fprintf ppf ":" | _ -> Pervasives.failwith "Cli_entries: invalid semantic tag"
| "commandline" -> Format.fprintf ppf "@]" end ;
| "commanddoc" -> Format.fprintf ppf "@]" print_close_tag = begin function
| "opt" -> () | "title" -> Format.fprintf ppf ":"
| "arg" -> Format.fprintf ppf ">" | "commandline" -> Format.fprintf ppf "@]"
| "kwd" -> () | "commanddoc" -> Format.fprintf ppf "@]"
| "hilight" -> () | "opt" -> ()
| "command" | "section" -> Format.fprintf ppf "@]" | "arg" -> Format.fprintf ppf ">"
| "full" -> pop_level `Full | "kwd" -> ()
| "args" -> pop_level `Args | "hilight" -> ()
| "short" -> pop_level `Short | "command" | "list" -> Format.fprintf ppf "@]"
| "terse" -> pop_level `Terse | "full" -> pop_level `Full
| "document" -> Format.fprintf ppf "@]" | "args" -> pop_level `Args
| _ -> Pervasives.failwith "Cli_entries: invalid semantic tag" | "short" -> pop_level `Short
end } ; | "terse" -> pop_level `Terse
Format.pp_set_print_tags ppf true | "document" -> Format.fprintf ppf "@]"
| `Html -> | _ -> Pervasives.failwith "Cli_entries: invalid semantic tag"
Format.pp_set_formatter_tag_functions ppf end } ;
{ mark_open_tag = (fun _ -> "") ; Format.pp_set_print_tags ppf true
mark_close_tag = (fun _ -> "") ; | `Html ->
print_open_tag = begin function Format.pp_set_formatter_tag_functions ppf
| "title" -> Format.fprintf ppf "\003h3\004" { mark_open_tag = (fun _ -> "") ;
| "commandline" -> Format.fprintf ppf "\003div class='cmdline'\004@[<h>" mark_close_tag = (fun _ -> "") ;
| "commanddoc" -> Format.fprintf ppf "\003div class='cmddoc'\004" print_open_tag = begin function
| "opt" -> Format.fprintf ppf "\003span class='opt'\004" | "title" -> Format.fprintf ppf "\003h3\004"
| "arg" -> Format.fprintf ppf "\003span class='arg'\004" | "commandline" -> Format.fprintf ppf "\003div class='cmdline'\004@[<h>"
| "kwd" -> Format.fprintf ppf "\003span class='kwd'\004" | "commanddoc" -> Format.fprintf ppf "\003div class='cmddoc'\004"
| "hilight" -> () | "opt" -> Format.fprintf ppf "\003span class='opt'\004"
| "section" -> Format.fprintf ppf "\003ul\004@\n" | "arg" -> Format.fprintf ppf "\003span class='arg'\004"
| "command" -> Format.fprintf ppf "\003li\004@\n" | "kwd" -> Format.fprintf ppf "\003span class='kwd'\004"
| "full" -> push_level `Full | "hilight" -> ()
| "args" -> push_level `Args | "list" -> Format.fprintf ppf "\003ul\004@\n"
| "short" -> push_level `Short | "command" -> Format.fprintf ppf "\003li\004@\n"
| "terse" -> push_level `Terse | "full" -> push_level `Full
| "document" -> | "args" -> push_level `Args
Format.fprintf ppf | "short" -> push_level `Short
"@[<v 0>\003style\004\ | "terse" -> push_level `Terse
.cmdline { font-family: monospace }\ | "document" ->
.cmddoc { white-space: pre-wrap ; font-family: monospace; line-height: 170%%; margin: 0 0 20px 0 }\ Format.fprintf ppf
.cmdline { background: #343131; padding: 2px 8px; border-radius:10px; color: white; margin: 5px; }\ "@[<v 0>\003style\004\
.cmdline+.cmddoc { margin: -5px 5px 0 20px; padding: 5px }\ .cmdline { font-family: monospace }\
.opt,.arg { background: #343131; font-weight: bold; padding: 2px 4px; border-radius:5px; }\ .cmddoc { white-space: pre-wrap ; font-family: monospace; line-height: 170%%; margin: 0 0 20px 0 }\
.kwd { font-weight: bold; } .opt { color:#CF0; background: #460; } .arg { color: #CEF; background: #369; }\ .cmdline { background: #343131; padding: 2px 8px; border-radius:10px; color: white; margin: 5px; }\
\003/style\004@\n" ; .cmdline+.cmddoc { margin: -5px 5px 0 20px; padding: 5px }\
| _ -> Pervasives.failwith "Cli_entries: invalid semantic tag" .opt,.arg { background: #343131; font-weight: bold; padding: 2px 4px; border-radius:5px; }\
end ; .kwd { font-weight: bold; } .opt { color:#CF0; background: #460; } .arg { color: #CEF; background: #369; }\
print_close_tag = begin function \003/style\004@\n" ;
| "title" -> Format.fprintf ppf "\003/h3\004@\n" | _ -> Pervasives.failwith "Cli_entries: invalid semantic tag"
| "commandline" -> Format.fprintf ppf "@]\003/div\004@\n" end ;
| "commanddoc" -> Format.fprintf ppf "\003/div\004@\n" print_close_tag = begin function
| "opt" -> Format.fprintf ppf "\003/span\004" | "title" -> Format.fprintf ppf "\003/h3\004@\n"
| "arg" -> Format.fprintf ppf "\003/span\004" | "commandline" -> Format.fprintf ppf "@]\003/div\004@\n"
| "kwd" -> Format.fprintf ppf "\003/span\004" | "commanddoc" -> Format.fprintf ppf "\003/div\004@\n"
| "hilight" -> () | "opt" -> Format.fprintf ppf "\003/span\004"
| "section" -> Format.fprintf ppf "\003/ul\004@\n" | "arg" -> Format.fprintf ppf "\003/span\004"
| "command" -> Format.fprintf ppf "\003/li\004@\n" | "kwd" -> Format.fprintf ppf "\003/span\004"
| "full" -> pop_level `Full | "hilight" -> ()
| "args" -> pop_level `Args | "list" -> Format.fprintf ppf "\003/ul\004@\n"
| "short" -> pop_level `Short | "command" -> Format.fprintf ppf "\003/li\004@\n"
| "terse" -> pop_level `Terse | "full" -> pop_level `Full
| "document" -> Format.fprintf ppf "@]" | "args" -> pop_level `Args
| _ -> Pervasives.failwith "Cli_entries: invalid semantic tag" | "short" -> pop_level `Short
end } ; | "terse" -> pop_level `Terse
let orig_out_functions = | "document" -> Format.fprintf ppf "@]"
Format.pp_get_formatter_out_functions ppf () in | _ -> Pervasives.failwith "Cli_entries: invalid semantic tag"
Format.pp_set_formatter_out_functions ppf end } ;
{ orig_out_functions with let orig_out_functions =
out_string = (fun s i j -> Format.pp_get_formatter_out_functions ppf () in
let buf = Buffer.create (j - i) in Format.pp_set_formatter_out_functions ppf
for n = i to j - 1 do match String.get s n with { orig_out_functions with
| '\003' -> Buffer.add_char buf '<' out_string = (fun s i j ->
| '\004' -> Buffer.add_char buf '>' let buf = Buffer.create (j - i) in
| '>' -> Buffer.add_string buf "&gt;" for n = i to j - 1 do match String.get s n with
| '<' -> Buffer.add_string buf "&lt;" | '\003' -> Buffer.add_char buf '<'
| c -> Buffer.add_char buf c | '\004' -> Buffer.add_char buf '>'
done ; | '>' -> Buffer.add_string buf "&gt;"
let s' = Buffer.contents buf in | '<' -> Buffer.add_string buf "&lt;"
orig_out_functions.out_string s' 0 (String.length s'))} ; | c -> Buffer.add_char buf c
Format.pp_set_print_tags ppf true done ;
let s' = Buffer.contents buf in
orig_out_functions.out_string s' 0 (String.length s'))} ;
Format.pp_set_print_tags ppf true
end ;
orig_state
let restore_formatter ppf (out_functions, tag_functions, tags) =
Format.pp_set_formatter_out_functions ppf out_functions ;
Format.pp_set_formatter_tag_functions ppf tag_functions ;
Format.pp_set_print_tags ppf tags
let usage ppf ?global_options ?(highlights=[]) commands format verbosity = let usage ppf ?global_options ?(highlights=[]) commands format verbosity =
setup_ppf ppf format verbosity ; ignore (setup_formatter ppf ~format ~verbosity) ;
let usage ppf (by_group, options) = let usage ppf (by_group, options) =
let exe = Filename.basename Sys.executable_name in let exe = Filename.basename Sys.executable_name in
let print_groups = let print_groups =
@ -940,7 +956,7 @@ let usage ppf ?global_options ?(highlights=[]) commands format verbosity =
| Some (Argument { spec ; _ })-> | Some (Argument { spec ; _ })->
Format.fprintf ppf Format.fprintf ppf
"@[<v>@{<title>Usage@}@,\ "@[<v>@{<title>Usage@}@,\
@{<section>\ @{<list>\
@{<command>@{<commandline>\ @{<command>@{<commandline>\
%s [@{<opt>global options@}] command @{<opt>[command options]@}@}@}@,\ %s [@{<opt>global options@}] command @{<opt>[command options]@}@}@}@,\
@{<command>@{<commandline>\ @{<command>@{<commandline>\
@ -949,7 +965,7 @@ let usage ppf ?global_options ?(highlights=[]) commands format verbosity =
%s [@{<opt>global options@}] command @{<opt>-help@} (for command options)@}@}\ %s [@{<opt>global options@}] command @{<opt>-help@} (for command options)@}@}\
@}@,@,\ @}@,@,\
@{<title>To browse the documentation@}@,\ @{<title>To browse the documentation@}@,\
@{<section>\ @{<list>\
@{<command>@{<commandline>\ @{<command>@{<commandline>\
%s [@{<opt>global options@}] man (for a list of commands)@}@}@,\ %s [@{<opt>global options@}] man (for a list of commands)@}@}@,\
@{<command>@{<commandline>\ @{<command>@{<commandline>\
@ -968,7 +984,7 @@ let usage ppf ?global_options ?(highlights=[]) commands format verbosity =
Format.fprintf ppf "@}" Format.fprintf ppf "@}"
let command_usage ppf commands format verbosity = let command_usage ppf commands format verbosity =
setup_ppf ppf format verbosity ; ignore (setup_formatter ppf ~format ~verbosity) ;
let exe = Filename.basename Sys.executable_name in let exe = Filename.basename Sys.executable_name in
let prefix ppf () = let prefix ppf () =
Format.fprintf ppf "@{<kwd>%s@} @{<opt>[global options]@} " exe in Format.fprintf ppf "@{<kwd>%s@} @{<opt>[global options]@} " exe in
@ -1147,13 +1163,13 @@ let handle_cli_errors ~stdout ~stderr ~global_options = function
| Error [ e ] -> (* Should only be one error here *) | Error [ e ] -> (* Should only be one error here *)
begin match e with begin match e with
| Extra_arguments (_, cmd) -> | Extra_arguments (_, cmd) ->
setup_ppf stderr (if Unix.isatty Unix.stderr then `Ansi else `Plain) `Terse ; ignore (setup_formatter stderr ~format:(if Unix.isatty Unix.stderr then `Ansi else `Plain) ~verbosity:`Terse) ;
Format.fprintf stderr Format.fprintf stderr
"Extra arguments provided for command:@;<1 2>@[%a@]@." "Extra arguments provided for command:@;<1 2>@[%a@]@."
(print_command ?prefix:None ~highlights:[]) cmd; (print_command ?prefix:None ~highlights:[]) cmd;
return 1 return 1
| Not_enough_args (_, cmds) -> | Not_enough_args (_, cmds) ->
setup_ppf stderr (if Unix.isatty Unix.stderr then `Ansi else `Plain) `Terse ; ignore (setup_formatter stderr ~format:(if Unix.isatty Unix.stderr then `Ansi else `Plain) ~verbosity:`Terse) ;
Format.fprintf stderr Format.fprintf stderr
"@[<v 2>Unterminated command, here are possible completions:@,%a@]@." "@[<v 2>Unterminated command, here are possible completions:@,%a@]@."
(Format.pp_print_list (Format.pp_print_list

View File

@ -252,7 +252,7 @@ val dispatch:
(** Parse the global options, and return their value, with the rest of (** Parse the global options, and return their value, with the rest of
the command to be parsed. *) the command to be parsed. *)
val parse_global_options : val parse_initial_options :
('a, 'ctx) options -> ('a, 'ctx) options ->
'ctx -> 'ctx ->
string list -> string list ->
@ -262,4 +262,41 @@ val map_command: ('a -> 'b) -> ('b, 'c) command -> ('a, 'c) command
(** {2 Output formatting} *) (** {2 Output formatting} *)
val setup_ppf : Format.formatter -> [< `Plain ] -> [< `LOL ] -> unit (** Used to restore the formatter state after [setup_formatter]. *)
type formatter_state
(** Updates the formatter's functions to interprete some semantic tags
used in manual production. Returns the previous state of the
formatter to restore it afterwards if needed.
Toplevel structure tags:
* [<document>]: a toplevel group
* [<title>]: a section title (just below a [<document])
* [<list>]: a list section (just below a [<document])
Structure tags used internally for generating the manual:
* [<command>]: wraps the full documentation bloc for a command
* [<commandline>]: wraps the command line in a [<command>]
* [<commanddoc>]: wraps everything but the command line in a [<command>]
Cosmetic tags for hilighting text:
* [<opt>]: optional arguments * [<arg>]: positional arguments
* [<kwd>]: positional keywords * [<hilight>]: search results
Verbosity levels, in order, and how they are used in the manual:
* [<terse>]: always displayed (titles commands lines)
* [<args>]: displayed if [verbosity >= `Args] (lists of arguments)
* [<short>]: displayed if [verbosity >= `Short] (single line descriptions)
* [<full>]: only displayed if [verbosity = `Full] (long descriptions) *)
val setup_formatter :
Format.formatter ->
format: [< `Ansi | `Html | `Plain ] ->
verbosity: [> `Terse | `Short | `Args | `Full ] ->
formatter_state
(** Restore the formatter state after [setup_formatter]. *)
val restore_formatter : Format.formatter -> formatter_state -> unit