Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for tables #893

Merged
merged 16 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ UNRELEASED

Additions
- Source code rendering (@Julow, @panglesd, #909)
- Handle tables markup (@panglesd, @gpetiot, #893)

Bugfixes
- Fix `--hidden` not always taken into account (@panglesd, #940)
Expand Down
3 changes: 2 additions & 1 deletion doc/ocamldoc_differences.mld
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ can be seen rendered by [odoc] {{!Odoc_examples.Markup.Foo}here}.
The following describes the changes between what [odoc] understands and what’s in the OCaml manual.

- Heading levels are more restrictive. In the manual, it suggests any whole number is acceptable. In [odoc],
we follow the HTML spec in allowing headings from 1-6, and we also allow heading level [0] for the title
similarly to the HTML spec, we allow headings from 1-5, and heading level [0] for the title
of [.mld] files. [odoc] emits a warning for heading levels outside this range and caps them.

{3 Omissions}
Expand All @@ -29,6 +29,7 @@ The following describes the changes between what [odoc] understands and what’s
An other difference is that documentation starting with a heading or something that is not a paragraph won't have a synopsis ({{:https://github.com/ocaml/odoc/pull/643}github issue}).

{3 Improvements}
- [odoc] supports writing mathematics and tables with a specific syntax.
- [odoc] has a better mechanism for disambiguating references in comments. See 'reference syntax' later in this document.
- Built-in support for standalone [.mld] files. These are documents using the OCamldoc markup, but they’re rendered as distinct pages.
- Structured output: [odoc] can produce output in a structured directory tree rather a set of files.
Expand Down
68 changes: 68 additions & 0 deletions doc/odoc_for_authors.mld
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,74 @@ in block form this becomes:
See the {{:https://katex.org/docs/supported.html}KaTeX documentation} for the
HTML mode LaTeX support status.

{2 Tables}

Odoc 2.3 introduced new markup for tables. This markup comes in two flavors: the light syntax, and the heavy syntax.

The heavy syntax uses several markup: [{table ...}] to define a table, [{tr ...}] to define a row, and [{th ...}] and [{td ...}] to respectively define a header cell and a data cell.
Direct children of tables have to be rows, and direct children of rows have to be cells. Similarly, rows have to be direct children of tables, and cells direct children of row. Cells can contain any markup.

For instance, the following table:

{[
{table
{tr
{th Header 1}
{th Header 2}
{th Header 3}
}
{tr
{td Cell 1}
{td Cell with {e emphasized content}}
{td {v a block v} }
}
}
]}

would render as

{table
{tr
{th Header 1}
{th Header 2}
{th Header 3}
}
{tr
{td Cell 1}
{td Cell with {e emphasized content}}
{td {v a block v} }
}
}


The light syntax has the advantages of being simple to read, even as plain text. It is very similar to the {{:https://github.github.com/gfm/#tables-extension-}GFM Markdown syntax}, with the exception that it has to be enclosed in [{t ...}], and that the inline markup is the ocamldoc one. It supports alignment for columns using the [:] notation, from the GFM syntax: [---] is the default alignment, [:--] left-aligned, [--:] right-aligned and [:---:] is centered.

The following table, in light syntax:

{[
{t
| Header 1 | Header 2 | Header 3 | Header 4|
| :------: | --------:|:---------|---------|
| centered | right | left | default |
omitted | bar at | start and| finish
| {e emph} | and | unaligned | bars |
}
]}

would render as

{t
| Header 1 | Header 2 | Header 3 | Header 4|
| :------: | --------:|:---------|---------|
| centered | right | left | default |
omitted | bar at | start and| finish
| {e emph} | and | unaligned | bars |
}

The light syntax has the advantages of being arguably more readable for small tables, when viewing the source file directly. However, its content is restricted (for instance, no new line is allowed).
The heavy syntax is easier to write, can be more readable for big tables, and supports having any kind of content inside. It does not support alignment (yet).


{2 Stop Comments}

The special comment:
Expand Down
4 changes: 4 additions & 0 deletions odoc.opam
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Odoc is a documentation generator for OCaml. It reads doc comments,
delimited with `(** ... *)`, and outputs HTML.
"""

pin-depends: [
["odoc-parser.dev" "git+https://github.com/ocaml-doc/odoc-parser.git#f98cfe3"]
]

depends: [
"odoc-parser" {>= "2.0.0"}
"astring"
Expand Down
33 changes: 33 additions & 0 deletions src/document/comment.ml
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,39 @@ let rec nestable_block_element : Comment.nestable_block_element -> Block.one =
in
let items = List.map f items in
block @@ Block.List (kind, items)
| `Table { data; align } ->
let data =
List.map
(List.map (fun (cell, cell_type) ->
(nestable_block_element_list cell, cell_type)))
data
in
let generate_align data =
let max (a : int) b = if a < b then b else a in
(* Length of the longest line of the table *)
let max_length =
List.fold_left (fun m l -> max m (List.length l)) 0 data
in
let rec list_init i =
if i <= 0 then [] else Table.Default :: list_init (i - 1)
in
list_init max_length
in
let align =
match align with
| None -> generate_align data
| Some align ->
List.map
(function
| None -> Table.Default
| Some `Right -> Right
| Some `Left -> Left
| Some `Center -> Center)
align
(* We should also check wellness of number of table cells vs alignment,
and raise warnings *)
in
block @@ Table { data; align }

and paragraph : Comment.paragraph -> Block.one = function
| [ { value = `Raw_markup (target, s); _ } ] ->
Expand Down
2 changes: 2 additions & 0 deletions src/document/doctree.ml
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ end = struct
| Inline x -> inline x
| Paragraph x -> inline x
| List (_, x) -> List.exists block x
| Table { data; align = _ } ->
List.exists (List.exists (fun (cell, _) -> block cell)) data
| Description x -> description x
| Math _ -> true
| Source _ | Verbatim _ | Raw_markup _ -> false
Expand Down
11 changes: 11 additions & 0 deletions src/document/types.ml
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,22 @@ and Block : sig
| Math of Math.t
| Verbatim of string
| Raw_markup of Raw_markup.t
| Table of t Table.t

and list_type = Ordered | Unordered
end =
Block

and Table : sig
type alignment = Left | Center | Right | Default

type 'a t = {
data : ('a * [ `Header | `Data ]) list list;
align : alignment list;
}
end =
Table

and DocumentedSrc : sig
type 'a documented = {
attrs : Class.t;
Expand Down
35 changes: 35 additions & 0 deletions src/html/generator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ let heading ~config ~resolve (h : Heading.t) =
in
mk ~a (anchor @ content @ source_link)

let text_align = function
| Table.Left -> [ Html.a_style "text-align:left" ]
| Center -> [ Html.a_style "text-align:center" ]
| Right -> [ Html.a_style "text-align:right" ]
| Default -> []

let cell_kind = function `Header -> Html.th | `Data -> Html.td

let rec block ~config ~resolve (l : Block.t) : flow Html.elt list =
let as_flow x = (x : phrasing Html.elt list :> flow Html.elt list) in
let one (t : Block.one) =
Expand All @@ -192,6 +200,10 @@ let rec block ~config ~resolve (l : Block.t) : flow Html.elt list =
| List (typ, l) ->
let mk = match typ with Ordered -> Html.ol | Unordered -> Html.ul in
mk_block mk (List.map (fun x -> Html.li (block ~config ~resolve x)) l)
| Table t ->
mk_block ~extra_class:[ "odoc-table" ]
(fun ?a x -> Html.table ?a x)
(mk_rows ~config ~resolve t)
| Description l ->
let item i =
let a = class_ i.Description.attr in
Expand All @@ -213,6 +225,29 @@ let rec block ~config ~resolve (l : Block.t) : flow Html.elt list =
in
Utils.list_concat_map l ~f:one

and mk_rows ~config ~resolve { align; data } =
let mk_row row =
let mk_cell ~align (x, h) =
let a = text_align align in
cell_kind ~a h (block ~config ~resolve x)
in
let alignment align =
match align with align :: q -> (align, q) | [] -> (Table.Default, [])
(* Second case is for recovering from a too short alignment list. A
warning should have been raised when loading the doc-comment. *)
in
let acc, _align =
List.fold_left
(fun (acc, aligns) (x, h) ->
let align, aligns = alignment aligns in
let cell = mk_cell ~align (x, h) in
(cell :: acc, aligns))
([], align) row
in
Html.tr (List.rev acc)
in
List.map mk_row data

(* This coercion is actually sound, but is not currently accepted by Tyxml.
See https://github.com/ocsigen/tyxml/pull/265 for details
Can be replaced by a simple type coercion once this is fixed
Expand Down
16 changes: 16 additions & 0 deletions src/html_support_files/odoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,22 @@ td.def-doc *:first-child {
padding-left: 12px;
}

/* Tables */

.odoc-table {
margin: 1em;
}

.odoc-table td, .odoc-table th {
padding-left: 0.5em;
padding-right: 0.5em;
border: 1px solid black;
}

.odoc-table th {
font-weight: bold;
}

/* Mobile adjustements. */

@media only screen and (max-width: 110ex) {
Expand Down
Loading