ocamlformat icon indicating copy to clipboard operation
ocamlformat copied to clipboard

Feature request: Don't wrap pipelines

Open Julow opened this issue 6 years ago • 4 comments

The option break-infix=fit-or-vertical allows to break before every infix operators if the whole expression does not fit on a single line:

-           get_succs parent |> Sequence.of_list
+           get_succs parent
+           |> Sequence.of_list
            |> Sequence.filter ~f:(fun n -> not (equal node n))
            |> Sequence.Generator.of_sequence)

This works well for pipelines, boolean operators and sometimes list cons/concat but not with most other expressions:

 let git_clone ~branch ~remote ~output_dir =
   run_and_log
     Cmd.(
-      v "git" % "clone" % "--depth" % "1" % "--branch" % branch % remote
+      v "git"
+      % "clone"
+      % "--depth"
+      % "1"
+      % "--branch"
+      % branch
+      % remote
       % p output_dir)

This is clearly not what we want when the operands are short or when the expression is dense (eg. arithmetic, cmdliner's & and $).

I'd like fit-or-vertical in the default profile but only for pipelines. Should we implement that ? How to decide what is a pipeline and what is not ?

There have been discussions before, in https://github.com/ocaml-ppx/ocamlformat/issues/69 then break-infix has been added in https://github.com/ocaml-ppx/ocamlformat/pull/150.

Julow avatar Oct 16 '19 12:10 Julow

I agree, if we find a good heuristics we could also removes this option in favor of an auto mode:

  • pipes -> fit or vertical
  • others -> wrap

gpetiot avatar Oct 16 '19 14:10 gpetiot

I wonder if using the precedence of the operator would be a reasonable criterion. For example, perhaps everything at the precedence of :: and lower could be treated as fit-or-vertical, and everything of higher precedence could be treated as wrap.

jberdine avatar Apr 19 '20 00:04 jberdine

For maximal headaches, the threshold precedence level could be an int configure option. :-P

jberdine avatar Apr 19 '20 00:04 jberdine

Commenting my opinion on this suggestion as it came up in https://github.com/ocaml-ppx/ocamlformat/issues/1851. We use break-infix = fit-or-vertical in the Irmin code, and I think it's a benefit even in the non-pipeline cases. Here's a branch that applies the diff to drop the option, and some excerpts that I think are made worse:

let all_6_2char_hex a b c d e f =
-  is_2char_hex a
-  && is_2char_hex b
-  && is_2char_hex c
-  && is_2char_hex d
-  && is_2char_hex e
-  && is_2char_hex f
+  is_2char_hex a && is_2char_hex b && is_2char_hex c && is_2char_hex d
+  && is_2char_hex e && is_2char_hex f
  let pf0 =
    let open Utils.Parallel_folders in
-   open_ construct
-   |+ misc_stats_folder header
+   open_ construct |+ misc_stats_folder header
    |+ elapsed_wall_over_blocks_folder header block_count
    |+ elapsed_cpu_over_blocks_folder header block_count
    |+ Span_folder.create header.initial_stats.timestamp_wall block_count
    |+ cpu_usage_folder header block_count
-   |+ pack_folder
-   |+ tree_folder
-   |+ index_folder
-   |+ gc_folder
-   |+ disk_folder
+   |+ pack_folder |+ tree_folder |+ index_folder |+ gc_folder |+ disk_folder
+   |+ Store_watched_nodes_folder.create block_count
    |> seal
      let cmd =
        root
-       ^ "_build"
-         / "default"
+       ^ "_build" / "default"
          / Fmt.str "%s serve %d %d &" Sys.argv.(0) i id.id
      in

Overall, it's not clear to me why we should split e.g. large lists / chains of :: over multiple lines, but not chains of @. For the non-builtin cases, I suspect that it's not easy to predict how a user intends their DSL to be interpreted, and so the notion of what is a "pipeline" is very fluid. To take two examples:

  • the operator ( >=> ) is sometimes used as a point-free equivalent of ( >>= ), but perhaps not commonly enough to make it worthwhile as an exception.
  • operators like ( <+> ) are sometimes seen as an in-DSL equivalent of ( + ), and so it'd be nice to format them similarly.

(I don't even think that @Julow's original example is much worse when split onto multiple lines. Again, if I kept these arguments in a list, they would be formatted in the same way.) Overall, I think the formatting experience would be better (and much less surprising), with the existing fit-or-vertical behaviour.

CC: @samoht

craigfe avatar Oct 08 '21 09:10 craigfe