Format behaviour - hvbox behaviour with nested line breaks

Hi,

I’m trying to use the Format module to make a pretty printer and hvbox's aren’t behaving as I
thought they would.

let example () =
  print_string "(";
  open_hvbox 2;
    print_string "a";
    print_space ();
    print_string "(";
    open_vbox 2;
      print_string "b";
      print_space ();
      print_string "c";
    close_box ();
    print_string ")";
    print_space ();
    print_string "d";
  close_box ();
  print_string ")";
  print_newline ()
Format.printf "(@[<hv 2>a@ (@[<v 2>b@ c@])@ d@])@."

Both examples print:

(a (b
      c) d)

But I was hoping for:

(a
  (b
    c)
  d)

Is there any way to get this kind of behaviour? Specifically a box that like hvbox either has no
line breaks or all line breaks but if a sub-box has line breaks then the parent box also uses line
breaks?

I’ve realised what I should look into in the process of writing this post. I’ll try to update this later with whether I made any progress. Here’s what I wrote:

As a slightly more concrete motivating example imagine making an OCaml AST pretty-printter with the Format module to have three allowed formats for if statements:

if <A> then <B> else <C>
if <A> then
  <B>
else
  <C>
if
  <A>
then
  <B>
else
  <C>

(I have no idea if this is actually good style)

in priority order based on if things fit.

I hoped that the behaviour of:

(hvbox 0
  (hvbox 0
    "if"
    (break 1 2)
    <A>
  )
  (break 1 0)
  "then"
  (break 1 2)
  <B>
  (break 1 0)
  "else"
  (break 1 2)
  <C>
)

where this is a tree form isomoprhic to the imperative calls would work to implement this behaviour

Of the top of my head I think this is feasible to do without needing any backtracaking beyond going back in the stack and popping things once it is decided that multiple lines are needed. I’ll look into format’s source for why hvbox has the behaviour it does and if a new box can be added that works the way I’ve described.

The issue is the formatting of v-boxes: v-boxes behave as h-boxes where all break hints have been converted to @\n. Thus if the v-box fits on the line if it were printed as an h-box, the rest of the formatting engine is not aware of any breaks in the v-box. Typically, your example formatting revert to the one that you were hoping when the formatting margin is reduced:

Format.set_geometry 6 7;
Format.printf "(@[<hv 2>a@ (@[<h 2>b @\nc@])@ d@])@.";;

prints

(a
   (b 
      c)
   d)