Documenting library usage examples with odoc

Understandably, odoc focuses on generating documentation for interfaces, but as far as I’m aware, there isn’t existing support lurking in the ecosystem for generating usage documentation that is actually tied implementations in some way. If the usage docs are generated from the implementation code, the docs wont get out of sync with the library, and cut out need for pasting test cases into .mld code blocks etc, which is easy enough to forget to do.

I haven’t spent enough time digesting the way odoc is organized to figure out how easy it would be extended to do this, but at the moment I’ve been trying out a hacky solution that works directly on executable .ml source as part of a dune build. For example:

ex.ml

(** {0 Title} *)

(** blah blah blah *)
let foo = ()

(** blah blah *)

let bar = ()
let baz = ()

ex.mld

{0 Title}

blah blah blah

{[
let foo = ()
]}

blah blah

{[
let bar = ()
let baz = ()
]}

Obviously, this isn’t exactly as robust as working from compilation artifacts, and I rely on other parts of the build compiling the sources in question to know that they work, but it does the trick, taking a doc commented executable implementation to a usage demonstration html with the help of odoc and dune. Other examples (some still WIP) are linked here.

Currently, dunes documentation stanza can only be pointed at .mld files in the same directory (feature request), so as a workaround I build the alias that generates the doc subdirectory before @doc (thanks to watch mode with dune build -w @examples @doc this isn’t too painful).

Anyway I thought I’d post this to get feedback and get a sense of whether people think this is a direction worth pursuing (with odoc or otherwise). I understand that not all libraries call out for these kinds of documented usage examples, but for those that do, it’d be pretty nice. Also, thanks to @dbuenzli for starting me down on this road of trying to write better docs, reading through Vg and seeing images along with rest made me realize I needed to think a bit bigger.

6 Likes

This is a big problem for me too. I keep on having problems with my Cmdliner examples being out of sync and it’s a real PITA to maintain.

There’s a simple and obvious solution to this problem which has been lingering for long here. But unfortunately people do not like simple and obvious solutions. You can read about the complex plans in this discussion which have, unsurprisingly, never materialized.

2 Likes

I’ve read through that PR before (it really has been there for quite some time), but hadn’t come across that other discussion, I’ll have to take a look.

Extracting blocks from comments would be nice, but I figure it would also need more on improvements the tooling side to feel good, as opposed to going the other way (from .ml to docs), where all of the support already exists. I suppose it depends on how long the example code is, and how much it calls to be broken up by additional explanatory text. Whichever context you find yourself in will bias the mode preferred mode. In any case, I’d love to be able to do both (doc first and source first).

I had a quick go on Friday at the proposed mdx solution - the PR is here: Support for .mld files by jonludlam · Pull Request #377 · realworldocaml/mdx · GitHub

This works with dune’s support for mdx as long as you add in an explicit files section into the dune stanza, e.g.:

(mdx (files test.mld))
2 Likes

Cool. Meanwhile I still think we should have simple and obvious extraction support in odoc itself. There’s no need to add more tooling complications besides odoc.

@rizo would you still be up to polish the PR to something mergeable ?

I wrote an internal exercise a few months ago, part of which addresses running examples through mdx and offers a (somewhat sad) solution about interacting with mdx (and formatting using OCamlformat).

It works relatively well to make the examples part of the test suite and it works pretty well in terms of Worfklow.

1 Like

@dbuenzli unfortunately I have been busy with some other work, but I do want to get back to completing that PR this year.

One (rather small) challenge I remember having had to do with the syntax for code block annotations. The proposed syntax was {block_id[...]} which syntactically conflicts with styling attributes like {b[...]}.

Update: Maybe we could require the annotation to be a relative path like {./example-1.ml[...]}; this would make it clear that it’s supposed to be extracted into a file and would avoid the ambiguity.

3 Likes

I really don’t mean to suggest this as the solution for everyone, but I just wrote a couple of lines of awk to extract top-level code blocks from a signature file to a simple test. I use the same technique to extract a test from the README.md of the library.

In the past ocamldoc don’t provide the feature to include images.
How is it today with ocamldoc and/or odoc?
What are the .mld files?
Is there any one that ever thought about ocamldoc as a static website generator?
At the end of this api doc I put a complete tutorial / howto, with examples of code and explanations. With ocamldoc we can put text, titles, links, highlight some words, organise the articles in the order we want (the order on the command line), there is an index of modules that we only have to change into index of articles (just use a simple sed regexp), and that’s it.
(And for more options and power we can probably couple it in some way with commonmark/cmark)

Ah, yes, adding images into doc would be nice. I’m not sure if it’s supported by ocamldoc, but I was able to hackily get an image into my docs for ego (index (ego.index)) by encoding the image as a base64 image data directly in the src attribute.

3 Likes

Images can be added through links as well, for example in the Scad_ml index ( src ) and example pages of the docs (so far). However, there is no support in odoc for assets folders yet (though there is a long standing issue), so copying them into the right place during the build has to be arranged ad hoc (as with this dune rule.

3 Likes