Printing formatted lists

I’m trying to print a vbox in an hvbox, and I want it so that if the inner vbox is more than 1 line long, the outer hvbox should print newlines instead of spaces.

Here’s the code I have so far

type bind_t =
  string * int

let pp_bind_t ppf (s, n) =
  Format.fprintf ppf "@[<hv 2>%s =@ %d@]" s n

let pp_bindings ppf l =
  Format.pp_print_list pp_bind_t ppf l

let l1 = [ ("x", 1) ]
let l2 = [ ("very_long_variable_name", 1) ]
let l3 = [ ("x", 1); ("y", 2); ("z", 3) ]
let l4 = [ ("x", 1); ("y", 2) ]

let pp_let_bindings ppf l =
  Format.fprintf ppf "@[<hv>let@;<1 2>@[<v>%a@]@ in@]" pp_bindings l

let () =
  begin
    Format.pp_set_geometry Format.std_formatter ~max_indent:10 ~margin:20;
    pp_let_bindings Format.std_formatter l1;
    Format.pp_print_newline Format.std_formatter ();
    pp_let_bindings Format.std_formatter l2;
    Format.pp_print_newline Format.std_formatter ();
    pp_let_bindings Format.std_formatter l3;
    Format.pp_print_newline Format.std_formatter ();
    pp_let_bindings Format.std_formatter l4;
    Format.pp_print_newline Format.std_formatter ();
  end

Here’s the output:

let x = 1 in
let
  very_long_variable_name =
    1
in
let
  x = 1
  y = 2
  z = 3
in
let x = 1
    y = 2 in

All but the very last output are printed correctly. Is there a good way fix it so that
let x = 1; y = 2 in is printed in the same way as let x = 1; y = 2; z = 3 in, with the let and in having separate lines to themselves?

The issue is that for v-boxes that fit on the line, like your last case, the v-box does not emit breaks from the point of view of the outer boxes, but merely internally interprets spaces as line breaks. Thus from the point view of the outer hv box, there is no line breaks in

    x = 1
    y = 2

The solution is to add unavoidable line break separating each bindings:

(* same as before *)
let pp_bind_t ppf (s, n) = Format.fprintf ppf "@[<hv 2>%s =@ %d@]" s n

(* we replace the breaks between each binding *)
let pp_bindings ppf l =
  let always_break ppf () =
    Format.pp_print_break ppf (Format.pp_get_margin ppf () + 1) 0 in
  Format.pp_print_list ~pp_sep:always_break pp_bind_t ppf l

That worked! Thanks.