easy-format icon indicating copy to clipboard operation
easy-format copied to clipboard

Classic if/then/else formatting.

Open jordwalke opened this issue 8 years ago • 9 comments

This is one pattern that I've had a hard time figuring out how to accomplish with the formatting primitives in Eeasy_format:

if (x) {itemHere(a, b, c)} else {itemThere(x, y, z)}

Where if for any reason the "if braces" had broken, then the "else braces" should also break.

if (x) {
  itemHere (
    a,
    b,
    c
  )
} else {
   itemThere(x, y, z)
}

jordwalke avatar May 03 '16 01:05 jordwalke

I think you can accomplish this with a list of 2 elements, using else as separator, like the comma in a tuple. For a tuple we'd get the following structures:

(aaaaaa,
 bbbbbb)

(aaaaaa, bbbbbb)

The opening and closing parens become `""`, the comma becomes `"else"` (or `" else"`).

That's what I would try because lists (boxes) that are either horizontal or vertical (but not mixed) are built in.

mjambon avatar May 03 '16 02:05 mjambon

I was having similar problems implementing if/else if/else breaks since if you use Labels to implement it will break in the wrong order, as per below:

if (x) { itemHere(a); } else if (x) { itemThere(x, y, z); }

/* 1 */
if (x) { itemHere(a); } else if (x) {
  itemThere(x, y, z);
}

/* 2 */
if (x) { itemHere(a); } else if (
  x
) {
  itemThere(x, y, z);
}

/* 3 */
if (x) {
  itemHere(a);
} else if (
  x
) {
  itemThere(x, y, z);
}

By changing it to a list and manually adding splitting { and } across list items i think i have found a nice (but hacky solution), it at least covers all the JS if/else printing cases well. Here is a simplified version:

let string s => Atom s atom;
let inline_list indent::indent=false items =>
  List
    ("", "", "", {
      ...list,
      indent_body: indent ? 2 : 0,
      space_after_opening: false,
      space_after_separator: false,
      space_before_closing: false,
      wrap_body: `Force_breaks_rec,
      align_closing: false
    })
    items;
inline_list [
  Label
    (string "if (a) {", {...label, label_break: `Always_rec})
    (inline_list indent::true [string "line one;", string "line two;"]),
  Label
    (string "} else if (a) {", {...label, label_break: `Always_rec})
    (inline_list indent::true [string "line one;", string "line two;"]),
  string "}"
]

@jordwalke it won't quite solve the problem since half the breaks still happen one level down, but it's closer :/

pieterv avatar Dec 28 '16 23:12 pieterv

Can you show the case where "half the breaks still happen one level down"?

jordwalke avatar Dec 28 '16 23:12 jordwalke

The list inserts the newline for the trailing "}" whereas the statement list is separated onto a newline via the Label within the list, like so:

[
  "if () { statement",
          ^ breaks here via the label
  "}"
  ^ breaks here via list
]

Thinking about this more we could potentially remove the Label between the if and statements and just put it on a newline:

let string s => Atom s atom;
let inline_list indent::indent=false items =>
  List
    ("", "", "", {
      ...list,
      indent_body: indent ? 2 : 0,
      space_after_opening: false,
      space_after_separator: false,
      space_before_closing: false,
      wrap_body: `Force_breaks_rec,
      align_closing: false
    })
    items;
inline_list [
  string "if (a) {",
  inline_list indent::true [string "line one;", string "line two;"],
  string "} else if (a) {",
  inline_list indent::true [string "line one;", string "line two;"]),
  string "}"
]

I haven't tried it but in theory it could work :/

pieterv avatar Dec 28 '16 23:12 pieterv

The only problem with the last suggestion (I think) is that you indent even if the if/else doesn't break. But then again, if we force if/else to break as we often do, maybe that will work well!

jordwalke avatar Dec 28 '16 23:12 jordwalke

I guess it depends on how bad you want to allow people to configure the formatter to output:

let x = if (something) {thenThis} else {thenThat};

Since in ES6, ifs aren't expressions, it's probably much less important to support the non-breaking if/else scenario.

jordwalke avatar Dec 28 '16 23:12 jordwalke

The only problem with the last suggestion (I think) is that you indent even if the if/else doesn't break.

Not sure if this would work but may be able to use Format.pp_print_if_newline to inject the indentation if the list breaks. Im looking into doing this for trailing commas, i'll let you know how it goes :P

pieterv avatar Dec 28 '16 23:12 pieterv

let x = if (something) {thenThis} else {thenThat};

Potentially should work if you output like this (apart from the inner indentation):

[
  "let x = if (something) {",
  "thenThis",
  "} else {",
   "thenThat",
  "};",
]

pieterv avatar Dec 29 '16 00:12 pieterv

Yes, that sounds wonderful. We were discussing the same proposal for trailing commas. It would perhaps make a nice PR to Easy_format as a new primitive. breakAwareAtom "ifNoNewline" "ifNewline".

It would also be cool if we could have something even more advanced than switching between two atoms (two complete Easy_format trees) but I don't yet see a user for it and I don't know if it's possible.

jordwalke avatar Dec 29 '16 00:12 jordwalke