Skip to content

Commit ed78f3a

Browse files
authored
odoc: Align light syntax tables (#2633)
Light tables are formatted in two steps: all the cells are formatted into strings first, then the table is assembled by adding the necessary padding in each cells. As a bonus, the alignment specified in the table is respected.
1 parent 214c363 commit ed78f3a

File tree

11 files changed

+151
-118
lines changed

11 files changed

+151
-118
lines changed

Diff for: CHANGES.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ profile. This started with version 0.26.0.
5656
- Added back the flag `--disable-outside-detected-project` (#2439, @gpetiot)
5757
It was removed in version 0.22.
5858

59-
- Support newer Odoc syntax (#2631, #2632, @Julow)
59+
- Support newer Odoc syntax (#2631, #2632, #2633, @Julow)
6060

6161
### Changed
6262

Diff for: lib/Fmt.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ let sequence l =
137137

138138
let char c = with_pp (fun fs -> Format_.pp_print_char fs c)
139139

140-
let utf8_length s =
140+
let str_length s =
141141
Uuseg_string.fold_utf_8 `Grapheme_cluster (fun n _ -> n + 1) 0 s
142142

143143
let str_as n s =
@@ -147,7 +147,7 @@ let str_as n s =
147147
Format_.pp_print_as fs n s ;
148148
Box_debug.end_str ~stack fs )
149149

150-
let str s = if String.is_empty s then noop else str_as (utf8_length s) s
150+
let str s = if String.is_empty s then noop else str_as (str_length s) s
151151

152152
let sp = function
153153
| Blank -> char ' '

Diff for: lib/Fmt.mli

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ val eval : Format_.formatter -> t -> unit
4646
val protect : t -> on_error:(exn -> unit) -> t
4747
(** Catch exceptions raised while formatting. *)
4848

49+
val str_length : string -> int
50+
(** Length that will be accounted when the given string is outputted. *)
51+
4952
(** Break hints and format strings --------------------------------------*)
5053

5154
val break : int -> int -> t

Diff for: lib/Fmt_odoc.ml

+66-23
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ module Light_table = struct
215215
let header, data = extract [] grid in
216216
Some (header, alignments, data)
217217
with Table_not_safe -> None )
218+
219+
let alignment_chars = function
220+
| Some `Left -> (":", "-")
221+
| Some `Center -> (":", ":")
222+
| Some `Right -> ("-", ":")
223+
| None -> ("-", "-")
218224
end
219225

220226
let non_wrap_space sp =
@@ -356,32 +362,69 @@ and fmt_table_heavy c (((grid, alignments), _) : table) =
356362
fmt_block_markup "table" (list grid force_break fmt_row)
357363

358364
and fmt_table_light c (header, alignments, data) =
359-
let fmt_align = function
360-
| Some `Left -> str ":--"
361-
| Some `Center -> str ":-:"
362-
| Some `Right -> str "--:"
363-
| None -> str "---"
365+
(* Format every cells into strings to then compute the width of columns. *)
366+
let format_rows_to_strings =
367+
let format_cell_to_string elems =
368+
Format_.asprintf " %a " Fmt.eval
369+
@@ fmt_inline_elements c ~wrap:false elems
370+
in
371+
List.map ~f:(List.map ~f:format_cell_to_string)
372+
in
373+
let header = format_rows_to_strings header
374+
and data = format_rows_to_strings data in
375+
let column_width =
376+
let column_count =
377+
let f acc row = max acc (List.length row) in
378+
let compute init rows = List.fold_left rows ~init ~f in
379+
let aligns_count =
380+
Option.value_map alignments ~default:0 ~f:List.length
381+
in
382+
compute (compute aligns_count header) data
383+
in
384+
let column_min_width = if Option.is_some alignments then 3 else 1 in
385+
let widths = Array.init column_count ~f:(fun _ -> column_min_width) in
386+
let compute_column_widths row =
387+
List.iteri row ~f:(fun i cell ->
388+
widths.(i) <- max widths.(i) (Fmt.str_length cell) )
389+
in
390+
List.iter ~f:compute_column_widths header ;
391+
List.iter ~f:compute_column_widths data ;
392+
Array.get widths
364393
in
365-
let has_header = not (List.is_empty header)
366-
and has_data = not (List.is_empty data) in
367-
let fmt_alignment_row =
368-
opt alignments (fun aligns ->
369-
str "|"
370-
$ list aligns (str "|") fmt_align
371-
$ str "|"
372-
$ fmt_if has_data force_break )
394+
let align_row, align_of_column =
395+
let align_column i align =
396+
let l, r = Light_table.alignment_chars align in
397+
l ^ String.make (column_width i - 2) '-' ^ r
398+
in
399+
match alignments with
400+
| Some aligns ->
401+
let aligns_ar = Array.of_list aligns in
402+
let aligns_get i =
403+
if i >= Array.length aligns_ar then `Left
404+
else Option.value ~default:`Left aligns_ar.(i)
405+
in
406+
([List.mapi ~f:align_column aligns], aligns_get)
407+
| None -> ([], fun _ -> `Left)
408+
in
409+
let padding n = str (String.make n ' ') in
410+
let fmt_cell i s =
411+
let pad = column_width i - Fmt.str_length s in
412+
let l, r =
413+
if pad <= 0 then (noop, noop)
414+
else
415+
match align_of_column i with
416+
| `Left -> (noop, padding pad)
417+
| `Center -> (padding (pad / 2), padding ((pad + 1) / 2))
418+
| `Right -> (padding pad, noop)
419+
in
420+
l $ str s $ r
373421
in
374-
(* Don't allow inline elements to wrap, meaning the line won't break if the
375-
row breaks the margin. *)
376-
let fmt_cell elems = fmt_inline_elements c ~wrap:false elems in
377-
let fmt_row row = str "| " $ list row (str " | ") fmt_cell $ str " |" in
378-
let fmt_rows rows = list rows force_break fmt_row in
379-
let fmt_grid =
380-
fmt_rows header
381-
$ fmt_if has_header force_break
382-
$ fmt_alignment_row $ fmt_rows data
422+
let fmt_row row =
423+
let row = List.mapi row ~f:fmt_cell in
424+
str "|" $ list row (str "|") Fn.id $ str "|"
383425
in
384-
fmt_block_markup ~force_break:true "t" (vbox 0 fmt_grid)
426+
fmt_block_markup ~force_break:true "t"
427+
(vbox 0 (list (header @ align_row @ data) force_break fmt_row))
385428

386429
and fmt_table c table =
387430
match Light_table.of_table table with

Diff for: test/passing/refs.default/odoc.mli.err

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Warning: odoc.mli:130 exceeds the margin
1+
Warning: odoc.mli:125 exceeds the margin
2+
Warning: odoc.mli:126 exceeds the margin

Diff for: test/passing/refs.default/odoc.mli.ref

+21-25
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
}
3939

4040
{t
41-
| a | *b* |
42-
| *c | d* |
41+
| a | *b* |
42+
| *c | d* |
4343
}
4444

4545
{t
@@ -71,9 +71,9 @@
7171
}
7272

7373
{t
74-
| a | b | c | d |
75-
|---|:--|--:|:-:|
76-
| a | b | c | d |
74+
| a | b | c | d |
75+
|----|:---|---:|:--:|
76+
| .. | .. | .. | .. |
7777
}
7878

7979
{t
@@ -83,23 +83,25 @@
8383
}
8484

8585
{t
86-
| {i a} {:google.com} \t | | {m b} {e c} {% xyz %} | {b d} [foo] |
87-
|---|---|---|---|
86+
| {i a} {:google.com} \t | | {m b} {e c} {% xyz %} | {b d} [foo] |
87+
|------------------------|---|-----------------------|-------------|
8888
}
8989

9090
{t
91-
| a | b | c | d |
92-
|---|--:|:--|:-:|
91+
| ... | ... | ... | ... | .... |
92+
|-----|----:|:----|:---:|:----:|
93+
| . | . | . | . | . |
94+
| .. | .. | .. | .. | .. |
9395
}
9496

9597
{t
96-
| | a | b |
97-
|:--|--:|
98-
| c | d |
99-
| cc | dd |
98+
| | a | b |
99+
|:---|----:|
100+
| c | d |
101+
| cc | dd |
100102
| -: | :-: |
101-
| e | f |
102-
| g | h | |
103+
| e | f |
104+
| g | h | |
103105
}
104106

105107
{t
@@ -115,19 +117,13 @@
115117
}
116118

117119
{t
118-
| Header and other word |
119-
|---|
120-
| cell and other words |
121-
}
122-
123-
{t
124-
| Header other word |
125-
|---|
126-
| Header other word |
120+
| Header and other word | | 🐫 |
121+
|-----------------------|---|---|
122+
| cell and other words | é | |
127123
}
128124

129125
{t
130-
| foo | bar |
126+
| foo | bar |
131127
| {i foooooooooooooooooooooooooooo} foooooooooooooooooooooooo fooooooooooooooooooooooo | bar |
132128
} *)
133129

Diff for: test/passing/refs.janestreet/odoc.mli.err

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
Warning: odoc.mli:130 exceeds the margin
2-
Warning: odoc.mli:306 exceeds the margin
3-
Warning: odoc.mli:308 exceeds the margin
1+
Warning: odoc.mli:125 exceeds the margin
2+
Warning: odoc.mli:126 exceeds the margin
3+
Warning: odoc.mli:302 exceeds the margin
4+
Warning: odoc.mli:304 exceeds the margin

Diff for: test/passing/refs.janestreet/odoc.mli.ref

+21-25
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
}
3939

4040
{t
41-
| a | *b* |
42-
| *c | d* |
41+
| a | *b* |
42+
| *c | d* |
4343
}
4444

4545
{t
@@ -71,9 +71,9 @@
7171
}
7272

7373
{t
74-
| a | b | c | d |
75-
|---|:--|--:|:-:|
76-
| a | b | c | d |
74+
| a | b | c | d |
75+
|----|:---|---:|:--:|
76+
| .. | .. | .. | .. |
7777
}
7878

7979
{t
@@ -83,23 +83,25 @@
8383
}
8484

8585
{t
86-
| {i a} {:google.com} \t | | {m b} {e c} {% xyz %} | {b d} [foo] |
87-
|---|---|---|---|
86+
| {i a} {:google.com} \t | | {m b} {e c} {% xyz %} | {b d} [foo] |
87+
|------------------------|---|-----------------------|-------------|
8888
}
8989

9090
{t
91-
| a | b | c | d |
92-
|---|--:|:--|:-:|
91+
| ... | ... | ... | ... | .... |
92+
|-----|----:|:----|:---:|:----:|
93+
| . | . | . | . | . |
94+
| .. | .. | .. | .. | .. |
9395
}
9496

9597
{t
96-
| | a | b |
97-
|:--|--:|
98-
| c | d |
99-
| cc | dd |
98+
| | a | b |
99+
|:---|----:|
100+
| c | d |
101+
| cc | dd |
100102
| -: | :-: |
101-
| e | f |
102-
| g | h | |
103+
| e | f |
104+
| g | h | |
103105
}
104106

105107
{t
@@ -115,19 +117,13 @@
115117
}
116118

117119
{t
118-
| Header and other word |
119-
|---|
120-
| cell and other words |
121-
}
122-
123-
{t
124-
| Header other word |
125-
|---|
126-
| Header other word |
120+
| Header and other word | | 🐫 |
121+
|-----------------------|---|---|
122+
| cell and other words | é | |
127123
}
128124

129125
{t
130-
| foo | bar |
126+
| foo | bar |
131127
| {i foooooooooooooooooooooooooooo} foooooooooooooooooooooooo fooooooooooooooooooooooo | bar |
132128
} *)
133129

Diff for: test/passing/refs.ocamlformat/odoc.mli.err

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Warning: odoc.mli:130 exceeds the margin
1+
Warning: odoc.mli:125 exceeds the margin
2+
Warning: odoc.mli:126 exceeds the margin

0 commit comments

Comments
 (0)