Skip to content

Commit f494e9b

Browse files
authored
Multiple-lined documents are automatically aligned (#27)
1 parent 3ec70e9 commit f494e9b

8 files changed

Lines changed: 163 additions & 5 deletions

File tree

CHANGES.md

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

3+
## Unreleased
4+
5+
### Added
6+
7+
- Multiple-lined documents are automatically aligned (#27)
8+
39
## 0.8.7
410

511
### Changed

examples/echo_ansi.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ let () =
6666
; arg_name = { ansi_style_plain with color = Some `Blue }
6767
; arg_doc = { ansi_style_plain with color = Some `Cyan }
6868
; error = { ansi_style_plain with color = Some `Red }
69+
; margin = None
6970
}
7071
in
7172
match

src/ansi_style.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ let escape { bold; dim; underline; color } =
6868
;;
6969

7070
let pp_with_style t ppf ~f =
71-
if not (is_default t) then Format.pp_print_string ppf (escape t);
71+
if not (is_default t) then Format.pp_print_as ppf 0 (escape t);
7272
f ppf;
73-
if not (is_default t) then Format.pp_print_string ppf reset
73+
if not (is_default t) then Format.pp_print_as ppf 0 reset
7474
;;

src/help.ml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Style = struct
99
; arg_doc : Ansi_style.t
1010
; section_heading : Ansi_style.t
1111
; error : Ansi_style.t
12+
; margin : int option
1213
}
1314

1415
let plain =
@@ -18,6 +19,7 @@ module Style = struct
1819
; arg_doc = Ansi_style.default
1920
; section_heading = Ansi_style.default
2021
; error = Ansi_style.default
22+
; margin = None
2123
}
2224
;;
2325

@@ -49,6 +51,11 @@ let rec pp_print_spaces ppf = function
4951
pp_print_spaces ppf (n - 1)
5052
;;
5153

54+
let pp_print_string_as_seq ppf s =
55+
let s = String.split_on_char ~sep:' ' s in
56+
Format.pp_print_list ~pp_sep:Format.pp_print_space Format.pp_print_string ppf s
57+
;;
58+
5259
module Print = struct
5360
module Names = struct
5461
(* An entry possibly has several names in the case of command aliases or
@@ -128,6 +135,7 @@ module Print = struct
128135
~doc_left_padding
129136
t
130137
=
138+
Format.pp_open_box ppf (doc_left_padding + 4);
131139
pp_print_spaces ppf indent;
132140
let names_value_string =
133141
names_value_padded_to_string ~at_least_one_left_name ~right_names_left_padding t
@@ -139,7 +147,7 @@ module Print = struct
139147
let padding = doc_left_padding - String.length names_value_string in
140148
pp_print_spaces ppf padding;
141149
Ansi_style.pp_with_style style.arg_doc ppf ~f:(fun ppf ->
142-
Format.pp_print_string ppf doc));
150+
pp_print_string_as_seq ppf doc));
143151
Format.pp_print_newline ppf ()
144152
;;
145153
end
@@ -184,6 +192,7 @@ module Print = struct
184192
let doc_left_padding =
185193
max_name_length ~at_least_one_left_name ~right_names_left_padding t
186194
in
195+
Format.pp_open_box ppf (doc_left_padding + 4);
187196
List.iter t.entries ~f:(fun entry ->
188197
Entry.pp_padded
189198
style
@@ -299,6 +308,8 @@ let pp_usage (style : Style.t) ppf (spec : Command_doc_spec.t) =
299308
;;
300309

301310
let pp (style : Style.t) ppf (spec : Command_doc_spec.t) =
311+
let margin = Option.value ~default:1_000_000 style.margin in
312+
Format.pp_set_margin Format.std_formatter margin;
302313
Option.iter spec.doc ~f:(fun spec ->
303314
Ansi_style.pp_with_style style.program_doc ppf ~f:(fun ppf ->
304315
Format.pp_print_string ppf spec);

src/help.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Style : sig
88
; arg_doc : Ansi_style.t
99
; section_heading : Ansi_style.t
1010
; error : Ansi_style.t
11+
; margin : int option
1112
}
1213

1314
(** An opinionated default value with some colours and formatting *)

src/lib.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ module For_test = struct
858858
;;
859859

860860
let print_help_spec spec = Help.pp Help_style.plain Format.std_formatter spec
861+
let print_help_spec_with_style style spec = Help.pp style Format.std_formatter spec
861862

862863
let print_manpage spec prose =
863864
let manpage = { Manpage.spec; prose; version = None } in

src/lib.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module Help_style : sig
3636
; arg_doc : ansi_style
3737
; section_heading : ansi_style
3838
; error : ansi_style
39+
; margin : int option
3940
}
4041

4142
(** An opinionated default value with some colours and formatting *)
@@ -213,5 +214,6 @@ module For_test : sig
213214
-> ('a, Non_ret.t) result
214215

215216
val print_help_spec : Command_doc_spec.t -> unit
217+
val print_help_spec_with_style : Help_style.t -> Command_doc_spec.t -> unit
216218
val print_manpage : Command_doc_spec.t -> Manpage.prose -> unit
217219
end

tests/usage_tests/help_messages.ml

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
open Climate
22
open Command
33

4-
let run command args =
4+
let run ?(style = Help_style.plain) command args =
55
match For_test.eval_result ~program_name:"foo.exe" command args with
66
| Ok () -> ()
77
| Error (For_test.Non_ret.Help { command_doc_spec; _ }) ->
8-
For_test.print_help_spec command_doc_spec
8+
For_test.print_help_spec_with_style style command_doc_spec
99
| Error (For_test.Non_ret.Manpage { prose; command_doc_spec }) ->
1010
For_test.print_manpage command_doc_spec prose
1111
| _ -> failwith "unexpected parser output"
@@ -470,3 +470,139 @@ let%expect_test "positional arguments" =
470470
-h, --help Show this help message.
471471
|}]
472472
;;
473+
474+
let lorem_ipsum =
475+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \
476+
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \
477+
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure \
478+
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla"
479+
;;
480+
481+
let lorem_reversed =
482+
lorem_ipsum |> String.to_seq |> List.of_seq |> List.rev |> List.to_seq |> String.of_seq
483+
;;
484+
485+
let%expect_test "a long arg document" =
486+
run
487+
(singleton
488+
@@
489+
let open Arg_parser in
490+
let+ _ = flag [ "lorem" ] ~doc:(String.sub lorem_ipsum 0 100)
491+
and+ _ = flag [ "ipsum" ] ~doc:(String.sub lorem_reversed 0 100) in
492+
())
493+
[ "--help" ];
494+
[%expect
495+
{|
496+
Usage: foo.exe [OPTION]…
497+
498+
Options:
499+
--lorem Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore
500+
--ipsum allun taiguf ue erolod mullic esse tilev etatpulov ni tiredneherper ni rolod eruri etua siuD .tauqes
501+
-h, --help Show this help message.
502+
|}]
503+
;;
504+
505+
let%expect_test "a long arg document with margin" =
506+
let cmd =
507+
singleton
508+
@@
509+
let open Arg_parser in
510+
let+ _ = flag [ "lorem" ] ~doc:lorem_ipsum
511+
and+ _ = flag [ "ipsum" ] ~doc:lorem_reversed in
512+
()
513+
in
514+
run ~style:{ Help_style.plain with margin = Some 80 } cmd [ "--help" ];
515+
[%expect
516+
{|
517+
Usage: foo.exe [OPTION]…
518+
519+
Options:
520+
--lorem Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
521+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
522+
enim ad minim veniam, quis nostrud exercitation ullamco laboris
523+
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
524+
in reprehenderit in voluptate velit esse cillum dolore eu fugiat
525+
nulla
526+
--ipsum allun taiguf ue erolod mullic esse tilev etatpulov ni
527+
tiredneherper ni rolod eruri etua siuD .tauqesnoc odommoc ae xe
528+
piuqila tu isin sirobal ocmallu noitaticrexe durtson siuq
529+
,mainev minim da mine tU .auqila angam erolod te erobal tu
530+
tnudidicni ropmet domsuie od des ,tile gnicsipida rutetcesnoc
531+
,tema tis rolod muspi meroL
532+
-h, --help Show this help message.
533+
|}];
534+
(* adding ansi escape sequences does not harm the shape *)
535+
run
536+
~style:
537+
{ Help_style.plain with
538+
margin = Some 80
539+
; arg_doc = { color = Some `Yellow; bold = true; dim = true; underline = true }
540+
}
541+
cmd
542+
[ "--help" ];
543+
[%expect
544+
{|
545+
Usage: foo.exe [OPTION]…
546+
547+
Options:
548+
--lorem Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
549+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
550+
enim ad minim veniam, quis nostrud exercitation ullamco laboris
551+
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
552+
in reprehenderit in voluptate velit esse cillum dolore eu fugiat
553+
nulla
554+
--ipsum allun taiguf ue erolod mullic esse tilev etatpulov ni
555+
tiredneherper ni rolod eruri etua siuD .tauqesnoc odommoc ae xe
556+
piuqila tu isin sirobal ocmallu noitaticrexe durtson siuq
557+
,mainev minim da mine tU .auqila angam erolod te erobal tu
558+
tnudidicni ropmet domsuie od des ,tile gnicsipida rutetcesnoc
559+
,tema tis rolod muspi meroL
560+
-h, --help Show this help message.
561+
|}]
562+
;;
563+
564+
let%expect_test "a long command document" =
565+
let lorem = unit_command ~doc:(String.sub lorem_ipsum 0 100) () in
566+
let ipsum = unit_command ~doc:(String.sub lorem_reversed 0 100) () in
567+
let cmd = group [ subcommand "lorem" lorem; subcommand "ipsum" ipsum ] in
568+
run cmd [ "--help" ];
569+
[%expect
570+
{|
571+
Usage: foo.exe [COMMAND]
572+
foo.exe [OPTION]…
573+
574+
Options:
575+
-h, --help Show this help message.
576+
577+
Commands:
578+
lorem Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore
579+
ipsum allun taiguf ue erolod mullic esse tilev etatpulov ni tiredneherper ni rolod eruri etua siuD .tauqes
580+
|}]
581+
;;
582+
583+
let%expect_test "a long command document with margin" =
584+
let lorem = unit_command ~doc:lorem_ipsum () in
585+
let ipsum = unit_command ~doc:lorem_reversed () in
586+
let cmd = group [ subcommand "lorem" lorem; subcommand "ipsum" ipsum ] in
587+
run ~style:{ Help_style.plain with margin = Some 80 } cmd [ "--help" ];
588+
[%expect
589+
{|
590+
Usage: foo.exe [COMMAND]
591+
foo.exe [OPTION]…
592+
593+
Options:
594+
-h, --help Show this help message.
595+
596+
Commands:
597+
lorem Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
598+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
599+
minim veniam, quis nostrud exercitation ullamco laboris nisi ut
600+
aliquip ex ea commodo consequat. Duis aute irure dolor in
601+
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
602+
ipsum allun taiguf ue erolod mullic esse tilev etatpulov ni tiredneherper ni
603+
rolod eruri etua siuD .tauqesnoc odommoc ae xe piuqila tu isin sirobal
604+
ocmallu noitaticrexe durtson siuq ,mainev minim da mine tU .auqila
605+
angam erolod te erobal tu tnudidicni ropmet domsuie od des ,tile
606+
gnicsipida rutetcesnoc ,tema tis rolod muspi meroL
607+
|}]
608+
;;

0 commit comments

Comments
 (0)