Skip to content

Commit aac4dc6

Browse files
authored
Consistent formatting for arrow class types (ocaml-ppx#2422)
* Test wrapping class arrow type Test both break-separators to before and after. * Separate `fmt_arrow_type` The plan is to re-use it to format class types arrows. Some cleanup. * Consistent formatting for arrow class types Use the same indentation and breaks for arrows in class types as for arrows in core types. The main challenge is that class types contain class signatures, which are docked after an arrow. The `fmt_class_type` is rewritten in the "pro" style, which also remove unnecessary space in object poly types
1 parent aa99c1d commit aac4dc6

File tree

11 files changed

+268
-85
lines changed

11 files changed

+268
-85
lines changed

CHANGES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Changelog
22

3-
Items marked with an asterisk (`*`) are changes that are likely to format
3+
Items marked with an asterisk (\*) are changes that are likely to format
44
existing code differently from the previous release when using the default
55
profile. This started with version 0.26.0.
66

@@ -17,6 +17,10 @@ Tags:
1717

1818
## unreleased
1919

20+
### Changed
21+
22+
- \* Consistent formatting of arrows in class types (#2422, @Julow)
23+
2024
### Fixed
2125

2226
- Fix dropped attributes on a begin-end in a match case (#2421, @Julow)

lib/Fmt_ast.ml

Lines changed: 126 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,28 @@ and fmt_arrow_param c ctx {pap_label= lI; pap_loc= locI; pap_type= tI} =
715715
in
716716
hvbox 0 (Cmts.fmt_before c locI $ arg)
717717

718+
(** Format [Ptyp_arrow]. [indent] can be used to override the indentation
719+
added for the break-separators option. [parent_has_parens] is used to
720+
align arrows to parentheses. *)
721+
and fmt_arrow_type c ~ctx ?indent ~parens ~parent_has_parens args fmt_ret_typ
722+
=
723+
let indent =
724+
match indent with
725+
| Some k -> k
726+
| None ->
727+
fmt_if_k
728+
Poly.(c.conf.fmt_opts.break_separators.v = `Before)
729+
(fmt_or_k c.conf.fmt_opts.ocp_indent_compat.v (fits_breaks "" "")
730+
(fits_breaks "" " ") )
731+
in
732+
indent
733+
$ wrap_if parens "(" ")"
734+
( list args
735+
(arrow_sep c ~parens:parent_has_parens)
736+
(fmt_arrow_param c ctx)
737+
$ fmt (arrow_sep c ~parens:parent_has_parens)
738+
$ fmt_ret_typ )
739+
718740
(* The context of [xtyp] refers to the RHS of the expression (namely
719741
Pexp_constraint) and does not give a relevant information as to whether
720742
[xtyp] should be parenthesized. [constraint_ctx] gives the higher context
@@ -756,36 +778,35 @@ and fmt_core_type c ?(box = true) ?pro ?(pro_space = true) ?constraint_ctx
756778
let ctx = Typ typ in
757779
let parenze_constraint_ctx =
758780
match constraint_ctx with
759-
| Some `Fun when not parens -> wrap "(" ")"
760-
| _ -> Fn.id
781+
| Some `Fun when not parens -> true
782+
| _ -> false
761783
in
762784
match ptyp_desc with
763785
| Ptyp_alias (typ, str) ->
764786
hvbox 0
765-
(parenze_constraint_ctx
787+
(wrap_if parenze_constraint_ctx "(" ")"
766788
( fmt_core_type c (sub_typ ~ctx typ)
767789
$ fmt "@ as@ "
768790
$ Cmts.fmt c str.loc @@ fmt_type_var str.txt ) )
769791
| Ptyp_any -> str "_"
770-
| Ptyp_arrow (ctl, ct2) ->
792+
| Ptyp_arrow (args, ret_typ) ->
771793
Cmts.relocate c.cmts ~src:ptyp_loc
772-
~before:(List.hd_exn ctl).pap_type.ptyp_loc ~after:ct2.ptyp_loc ;
773-
let ct2 = {pap_label= Nolabel; pap_loc= ct2.ptyp_loc; pap_type= ct2} in
774-
let xt1N = List.rev (ct2 :: List.rev ctl) in
794+
~before:(List.hd_exn args).pap_type.ptyp_loc ~after:ret_typ.ptyp_loc ;
775795
let indent =
776-
if Poly.(c.conf.fmt_opts.break_separators.v = `Before) then 2 else 0
796+
match pro with
797+
| Some pro when c.conf.fmt_opts.ocp_indent_compat.v ->
798+
let indent =
799+
if Poly.(c.conf.fmt_opts.break_separators.v = `Before) then 2
800+
else 0
801+
in
802+
Some
803+
(fits_breaks ""
804+
(String.make (Int.max 1 (indent - String.length pro)) ' ') )
805+
| _ -> None
777806
in
778-
( match pro with
779-
| Some pro when c.conf.fmt_opts.ocp_indent_compat.v ->
780-
fits_breaks ""
781-
(String.make (Int.max 1 (indent - String.length pro)) ' ')
782-
| _ ->
783-
fmt_if_k
784-
Poly.(c.conf.fmt_opts.break_separators.v = `Before)
785-
(fmt_or_k c.conf.fmt_opts.ocp_indent_compat.v (fits_breaks "" "")
786-
(fits_breaks "" " ") ) )
787-
$ parenze_constraint_ctx
788-
(list xt1N (arrow_sep c ~parens) (fmt_arrow_param c ctx))
807+
let fmt_ret_typ = fmt_core_type c (sub_typ ~ctx ret_typ) in
808+
fmt_arrow_type c ~ctx ?indent ~parens:parenze_constraint_ctx
809+
~parent_has_parens:parens args fmt_ret_typ
789810
| Ptyp_constr (lid, []) -> fmt_longident_loc c lid
790811
| Ptyp_constr (lid, [t1]) ->
791812
fmt_core_type c (sub_typ ~ctx t1) $ fmt "@ " $ fmt_longident_loc c lid
@@ -809,7 +830,7 @@ and fmt_core_type c ?(box = true) ?pro ?(pro_space = true) ?constraint_ctx
809830
$ fmt_core_type c ~box:true (sub_typ ~ctx t) )
810831
| Ptyp_tuple typs ->
811832
hvbox 0
812-
(parenze_constraint_ctx
833+
(wrap_if parenze_constraint_ctx "(" ")"
813834
(wrap_fits_breaks_if ~space:false c.conf parens "(" ")"
814835
(list typs "@ * " (sub_typ ~ctx >> fmt_core_type c)) ) )
815836
| Ptyp_var s -> fmt_type_var s
@@ -2705,7 +2726,7 @@ and fmt_class_structure c ~ctx ?ext self_ fields =
27052726
$ fmt_or (List.is_empty fields) "@ " "@;<1000 0>"
27062727
$ str "end"
27072728

2708-
and fmt_class_signature c ~ctx ~parens ?ext self_ fields =
2729+
and fmt_class_signature c ~ctx ~parens ~loc ?(pro = noop) ?ext self_ fields =
27092730
let update_config c i =
27102731
match i.pctf_desc with
27112732
| Pctf_attribute atr -> update_config c [atr]
@@ -2722,53 +2743,75 @@ and fmt_class_signature c ~ctx ~parens ?ext self_ fields =
27222743
fmt_class_type_field c (sub_ctf ~ctx i)
27232744
in
27242745
let ast x = Ctf x in
2725-
Params.parens_if parens c.conf
2726-
( str "object"
2727-
$ fmt_extension_suffix c ext
2728-
$ self_ $ fmt "@ "
2729-
$ hvbox 0
2730-
( fmt_if_k (List.is_empty fields)
2731-
(Cmts.fmt_within ~pro:noop c (Ast.location ctx))
2732-
$ fmt_item_list c ctx update_config ast fmt_item fields )
2746+
let cmts_within =
2747+
if List.is_empty fields then
2748+
(* Side effect order is important. *)
2749+
Cmts.fmt_within ~pro:noop c (Ast.location ctx)
2750+
else noop
2751+
in
2752+
hvbox 2
2753+
( hvbox 2
2754+
( pro
2755+
$ ( fmt_if parens "(" $ Cmts.fmt_before c loc $ str "object"
2756+
$ fmt_extension_suffix c ext
2757+
$ self_ ) )
2758+
$ fmt "@ " $ cmts_within
2759+
$ fmt_item_list c ctx update_config ast fmt_item fields
27332760
$ fmt_if (not (List.is_empty fields)) "@;<1000 -2>"
2734-
$ str "end" )
2761+
$ hvbox 0 (str "end" $ Cmts.fmt_after c loc $ fmt_if parens ")") )
27352762

2736-
and fmt_class_type c ({ast= typ; _} as xtyp) =
2763+
and fmt_class_type ?(pro = noop) c ({ast= typ; _} as xtyp) =
27372764
protect c (Cty typ)
27382765
@@
27392766
let {pcty_desc; pcty_loc; pcty_attributes} = typ in
27402767
update_config_maybe_disabled c pcty_loc pcty_attributes
27412768
@@ fun c ->
27422769
let doc, atrs = doc_atrs pcty_attributes in
2743-
Cmts.fmt c pcty_loc
2744-
@@
27452770
let parens = parenze_cty xtyp in
2746-
( Params.parens_if parens c.conf
2747-
@@
27482771
let ctx = Cty typ in
27492772
match pcty_desc with
27502773
| Pcty_constr (name, params) ->
27512774
let params = List.map params ~f:(fun x -> (x, [])) in
2752-
fmt_class_params c ctx params
2753-
$ fmt_longident_loc c name $ fmt_attributes c atrs
2775+
hvbox 2
2776+
( pro
2777+
$ hovbox 0
2778+
(wrap_if parens "(" ")"
2779+
( Cmts.fmt c pcty_loc @@ fmt_class_params c ctx params
2780+
$ fmt_longident_loc c name $ fmt_attributes c atrs ) ) )
27542781
| Pcty_signature {pcsig_self; pcsig_fields} ->
2755-
fmt_class_signature c ~ctx ~parens pcsig_self pcsig_fields
2782+
fmt_class_signature c ~ctx ~parens ~loc:pcty_loc ~pro pcsig_self
2783+
pcsig_fields
27562784
$ fmt_attributes c atrs
2757-
| Pcty_arrow (ctl, ct2) ->
2785+
| Pcty_arrow (args, ret_typ) ->
27582786
Cmts.relocate c.cmts ~src:pcty_loc
2759-
~before:(List.hd_exn ctl).pap_type.ptyp_loc ~after:ct2.pcty_loc ;
2760-
let xct2 = sub_cty ~ctx ct2 in
2761-
list ctl (arrow_sep c ~parens) (fmt_arrow_param c ctx)
2762-
$ fmt (arrow_sep c ~parens)
2763-
$ (Cmts.fmt_before c ct2.pcty_loc $ fmt_class_type c xct2)
2764-
$ fmt_attributes c atrs
2765-
| Pcty_extension ext -> fmt_extension c ctx ext $ fmt_attributes c atrs
2787+
~before:(List.hd_exn args).pap_type.ptyp_loc ~after:ret_typ.pcty_loc ;
2788+
let pro =
2789+
pro
2790+
$ ( fmt_if parens "("
2791+
$ Cmts.fmt_before c pcty_loc
2792+
$ fmt_arrow_type c ~ctx ~parens:false ~parent_has_parens:parens
2793+
args noop )
2794+
in
2795+
fmt_class_type c ~pro (sub_cty ~ctx ret_typ)
2796+
$ fmt_attributes c atrs $ Cmts.fmt_after c pcty_loc $ fmt_if parens ")"
2797+
| Pcty_extension ext ->
2798+
hvbox 2
2799+
( pro
2800+
$ Cmts.fmt c pcty_loc
2801+
@@ Params.parens_if parens c.conf
2802+
(fmt_extension c ctx ext $ fmt_attributes c atrs) )
27662803
| Pcty_open (popen, cl) ->
2767-
hvbox 0
2768-
( fmt_open_description c ~keyword:"let open" ~kw_attributes:atrs popen
2769-
$ fmt " in@;<1000 0>"
2770-
$ fmt_class_type c (sub_cty ~ctx cl) ) )
2771-
$ fmt_docstring c ~pro:(fmt "@ ") doc
2804+
let pro =
2805+
hvbox 2
2806+
( pro
2807+
$ Cmts.fmt c pcty_loc
2808+
@@ Params.parens_if parens c.conf
2809+
@@ ( fmt_open_description c ~keyword:"let open"
2810+
~kw_attributes:atrs popen
2811+
$ fmt " in@;<1000 0>" ) )
2812+
in
2813+
fmt_class_type c ~pro (sub_cty ~ctx cl)
2814+
$ fmt_docstring c ~pro:(fmt "@ ") doc
27722815

27732816
and fmt_class_expr c ({ast= exp; ctx= ctx0} as xexp) =
27742817
protect c (Cl exp)
@@ -2833,7 +2876,7 @@ and fmt_class_expr c ({ast= exp; ctx= ctx0} as xexp) =
28332876
hvbox 2
28342877
(wrap_fits_breaks ~space:false c.conf "(" ")"
28352878
( fmt_class_expr c (sub_cl ~ctx e)
2836-
$ fmt "@ : "
2879+
$ fmt " :@ "
28372880
$ fmt_class_type c (sub_cty ~ctx t) ) )
28382881
$ fmt_atrs
28392882
| Pcl_extension ext -> fmt_extension c ctx ext $ fmt_atrs
@@ -3616,16 +3659,18 @@ and fmt_class_types ?ext c ctx ~pre ~sep cls =
36163659
fmt_docstring_around_item ~force_before c cl.pci_attributes
36173660
in
36183661
let class_types =
3662+
let pro =
3663+
hovbox 2
3664+
( str (if first then pre else "and")
3665+
$ fmt_if_k first (fmt_extension_suffix c ext)
3666+
$ fmt_virtual_flag c cl.pci_virt
3667+
$ fmt "@ "
3668+
$ fmt_class_params c ctx cl.pci_params
3669+
$ fmt_str_loc c cl.pci_name $ fmt " " $ str sep )
3670+
$ fmt "@ "
3671+
in
36193672
hovbox 2
3620-
( hvbox 2
3621-
( str (if first then pre else "and")
3622-
$ fmt_if_k first (fmt_extension_suffix c ext)
3623-
$ fmt_virtual_flag c cl.pci_virt
3624-
$ fmt "@ "
3625-
$ fmt_class_params c ctx cl.pci_params
3626-
$ fmt_str_loc c cl.pci_name $ fmt "@ " $ str sep )
3627-
$ fmt "@;"
3628-
$ fmt_class_type c (sub_cty ~ctx cl.pci_expr)
3673+
( fmt_class_type c ~pro (sub_cty ~ctx cl.pci_expr)
36293674
$ fmt_item_attributes c ~pre:(Break (1, 0)) atrs )
36303675
in
36313676
fmt_if (not first) "\n@;<1000 0>"
@@ -3654,22 +3699,27 @@ and fmt_class_exprs ?ext c ctx cls =
36543699
fmt_docstring_around_item ~force_before c cl.pci_attributes
36553700
in
36563701
let class_exprs =
3702+
let pro =
3703+
box_fun_decl_args c 2
3704+
( hovbox 2
3705+
( str (if first then "class" else "and")
3706+
$ fmt_if_k first (fmt_extension_suffix c ext)
3707+
$ fmt_virtual_flag c cl.pci_virt
3708+
$ fmt "@ "
3709+
$ fmt_class_params c ctx cl.pci_params
3710+
$ fmt_str_loc c cl.pci_name )
3711+
$ fmt_if (not (List.is_empty xargs)) "@ "
3712+
$ wrap_fun_decl_args c (fmt_fun_args c xargs) )
3713+
in
3714+
let intro =
3715+
match ty with
3716+
| Some ty ->
3717+
let pro = pro $ fmt " :@ " in
3718+
fmt_class_type c ~pro (sub_cty ~ctx ty)
3719+
| None -> pro
3720+
in
36573721
hovbox 2
3658-
( hovbox 2
3659-
( box_fun_decl_args c 2
3660-
( hovbox 2
3661-
( str (if first then "class" else "and")
3662-
$ fmt_if_k first (fmt_extension_suffix c ext)
3663-
$ fmt_virtual_flag c cl.pci_virt
3664-
$ fmt "@ "
3665-
$ fmt_class_params c ctx cl.pci_params
3666-
$ fmt_str_loc c cl.pci_name )
3667-
$ fmt_if (not (List.is_empty xargs)) "@ "
3668-
$ wrap_fun_decl_args c (fmt_fun_args c xargs) )
3669-
$ opt ty (fun t ->
3670-
fmt " :@ " $ fmt_class_type c (sub_cty ~ctx t) )
3671-
$ fmt "@ =" )
3672-
$ fmt "@;" $ fmt_class_expr c e )
3722+
(hovbox 2 (intro $ fmt "@ =") $ fmt "@;" $ fmt_class_expr c e)
36733723
$ fmt_item_attributes c ~pre:(Break (1, 0)) atrs
36743724
in
36753725
fmt_if (not first) "\n@;<1000 0>"

test/passing/dune.inc

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,24 @@
911911
(package ocamlformat)
912912
(action (diff tests/class_expr.ml.err class_expr.ml.stderr)))
913913

914+
(rule
915+
(deps tests/.ocamlformat )
916+
(package ocamlformat)
917+
(action
918+
(with-stdout-to class_sig-after.mli.stdout
919+
(with-stderr-to class_sig-after.mli.stderr
920+
(run %{bin:ocamlformat} --margin-check --break-separators=after %{dep:tests/class_sig.mli})))))
921+
922+
(rule
923+
(alias runtest)
924+
(package ocamlformat)
925+
(action (diff tests/class_sig-after.mli.ref class_sig-after.mli.stdout)))
926+
927+
(rule
928+
(alias runtest)
929+
(package ocamlformat)
930+
(action (diff tests/class_sig-after.mli.err class_sig-after.mli.stderr)))
931+
914932
(rule
915933
(deps tests/.ocamlformat )
916934
(package ocamlformat)
@@ -922,7 +940,7 @@
922940
(rule
923941
(alias runtest)
924942
(package ocamlformat)
925-
(action (diff tests/class_sig.mli class_sig.mli.stdout)))
943+
(action (diff tests/class_sig.mli.ref class_sig.mli.stdout)))
926944

927945
(rule
928946
(alias runtest)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--break-separators=after
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
class c : 'a -> object
2+
val x : 'b
3+
end
4+
5+
(** Fitting *)
6+
7+
class c : object end
8+
9+
class c : int -> object end
10+
11+
class c : int -> object end[@attr]
12+
13+
class c : int -> object end [@@attr]
14+
15+
class c : int -> object end
16+
17+
class c (* a *) : (* b *) int (* c *) -> (* d *) object (* e *) end (* f *)
18+
19+
class c : object end
20+
21+
class c : object
22+
(** Standalone doc-string. *)
23+
end
24+
25+
class unix_mockup :
26+
foooo:string ->
27+
foooo:string ->
28+
foooo:string ->
29+
foooo:string ->
30+
foooo:string ->
31+
foooo:string ->
32+
bar
33+
34+
class unix_mockup :
35+
foooo:string ->
36+
foooo:string ->
37+
foooo:string ->
38+
foooo:string ->
39+
foooo:string ->
40+
foooo:string ->
41+
object
42+
method foo : string
43+
end

0 commit comments

Comments
 (0)