diff --git a/README.md b/README.md index 890db02c5..66eedd4d3 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,135 @@ [![Build Status](https://img.shields.io/endpoint?url=https%3A%2F%2Fci.ocamllabs.io%2Fbadge%2Frealworldocaml%2Fmdx%2Fmain&logo=ocaml)](https://ci.ocamllabs.io/github/realworldocaml/mdx) -## mdx -- executable code blocks inside markdown files +## MDX -`mdx` allows to execute code blocks inside markdown files. -There are (currently) two sub-commands, corresponding -to two modes of operations: pre-processing (`ocaml-mdx pp`) -and tests (`ocaml-mdx test`). +MDX allows to execute code blocks inside markdown and mli documentation +to help keeping them up to date. -The pre-processor mode allows to mix documentation and code, -and to practice "literate programming" using markdown and OCaml. +Use the +[dune stanza](https://dune.readthedocs.io/en/latest/dune-files.html#mdx-since-2-4) +to enable it on your documentation. -The test mode allows to ensure that shell scripts and OCaml fragments -in the documentation always stays up-to-date. - -`mdx` is released as a single binary (called `ocaml-mdx`) and -can be installed using opam: +`mdx` is released on opam and can be installed by running: ```sh $ opam install mdx ``` -If you want to contribute or hack on the project, please see the +If you want to contribute to the project, please see the [CONTRIBUTING.md](CONTRIBUTING.md). +### Basic Usage + +You can use MDX with your Markdown or `.mli` documentation, which ensures +code in multi-line or verbatim code blocks is correct. + +To enable MDX on specific files you must first enable it for your project by +adding the following stanza to your `dune-project`: +``` +(using mdx 0.2) +``` + +Note that version `0.2` of the MDX stanza is only available in dune `3.0` or +higher. You can use the first, `0.1` version with dune `2.4` or higher. + +Then add the following in the relevant `dune` file: +``` +(mdx) +``` +That enables MDX on all markdown files in the folder. +The MDX stanza can be further configured. Please visit the relevant section of +[dune's manual](https://dune.readthedocs.io/en/latest/dune-files.html#mdx-since-2-4) +for more information. + +MDX supports various type of code blocks but the most common are OCaml toplevel +blocks. We illustrate one in our example below. In a Markdown file, you +would write something similar to this: + +````markdown +Let's look at how good OCaml is with integers and strings: +```ocaml +# 1 + 2;; +- : int = 2 +# "a" ^ "bc";; +- : string = "ab" +``` +```` +or in an `mli` file: +```ocaml +(** Let's look at how good OCaml is with integers and strings: + {@ocaml[ + # 1 + 2;; + - : int = 2 + # "a" ^ "bc";; + - : string = "ab" + ]} +*) +``` + +The content of the toplevel blocks looks just like an interactive toplevel +session. Phrases, i.e., the toplevel "input", start with a `#` and end with `;;`. +The toplevel evaluation, or "output" follows each phrase. + +Now you probably have noticed that `1 + 2` is not equal to `2` nor is `"a" ^ "bc"` +to `"ab"`. Somebody must have updated the phrases, but then forgot to update +the evaluation. + +That's exactly why MDX is here! + +If you enable MDX for this file and then ran `dune runtest`, this would be the +result: + +```` +$ dune runtest +File "README.md", line 1, characters 0-0: + git (internal) (exit 1) +(cd _build/default && /usr/bin/git --no-pager diff --no-index --color=always -u README.md .mdx/README.md.corrected) +diff --git a/README.md b/.mdx/README.md.corrected +index 181b86f..458ecec 100644 +--- a/README.md ++++ b/.mdx/README.md.corrected +@@ -1,13 +1,13 @@ +Let's look at how good OCaml is with integers and strings: +```ocaml +# 1 + 2;; +-- : int = 2 ++- : int = 3 +# "a" ^ "bc";; +-- : string = "ab" ++- : string = "abc" +``` +```` + +The test run just failed and dune is showing the diff between what we have +locally and what should be, according to MDX. +This uses dune's promotion workflow so at this point you can either investigate +it further if you're surprised by this diff or if you're happy with it, simply +accept it by running: + +``` +dune promote +``` + +Now the documentation is up-to-date and running `dune runtest` again should be +successful! + ### Supported Extensions #### Labels -The blocks in markdown files can be parameterized by `mdx`-specific labels, that +The blocks can be parameterized by `mdx`-specific labels, that will change the way `mdx` interprets the block. -The syntax is: ``, where `LABELS` is a list of valid labels -separated by a comma. This line has to immediately precede the block it is -attached to. +The markdown syntax is: ``, where `LABELS` is a list of +valid labels separated by a comma. This line has to immediately precede the +block it is attached to. ```ocaml ``` -This syntax is the recommended way to define labels since `mdx` 1.7.0, to use -the previous syntax please refer to the -[mdx 1.6.0 README](https://github.com/realworldocaml/mdx/blob/1.6.0/README.md). - -It is also possible to use labels in OCaml interface files (`mli`), the syntax -for this is is slightly different to match the conventions of OCaml -documentation comments: +The `.mli` syntax for this is is slightly different to match the conventions of +OCaml documentation comments: (** This is an documentation comment with an ocaml block {@ocaml LABELS [ @@ -93,7 +178,7 @@ with a padding of 3: 10 ``` -`ocaml-mdx` will also consider exit codes when the syntax `[]`is used: +MDX will also consider exit codes when the syntax `[]`is used: ```sh $ exit 1 @@ -105,7 +190,7 @@ of success). #### OCaml Code -`ocaml-mdx` interprets OCaml fragments. It understands _normal_ code fragments and +MDX interprets OCaml fragments. It understands _normal_ code fragments and _toplevel_ code fragments (starting with a `#` sign and optionally ending with `;;`). Arbitrary whitespace padding is supported, at long as it stays consistent within a code block. @@ -126,7 +211,8 @@ Here is an examples of toplevel OCaml code: ``` ### File sync -`mdx` is also capable of including content from files in fenced code blocks + +MDX is also capable of including content from files in fenced code blocks using the label `file`. OCaml files can be sliced using named blocks: ```ocaml @@ -149,38 +235,6 @@ Non-OCaml files can also be read and included in a block: ``` However, part splitting is only supported for OCaml files. -### Pre-processing - -`ocaml-mdx pp` allows to transform a markdown file into a valid -OCaml file, which can be passed to OCaml using the `-pp` -option. - -For instance, given the following `file.md` document: - - ```ocaml - # print_endline "42" - 42 - ``` - -Can be compiled and executed using: - -```sh -$ ocamlc -pp 'ocaml-mdx pp' -impl file.md -o file.exe -$ ./file.exe -42 -``` - -This can be automated using `dune`: - -``` -(rule - ((targets (file.ml)) - (deps (file.md)) - (action (with-stdout-to ${@} (run ocaml-mdx pp ${<}))))) - -(executable ((name file))) -``` - ### Tests #### Cram Tests @@ -209,51 +263,6 @@ To execute OCaml code and toplevel fragments, uses `ocaml-mdx test `. If the output is not consistent with what is expected `.corrected` is generated. -#### Integration with Dune - -To test that the code blocks of `file.md` stay consistent, one can use -dune's `mdx` stanza: - -``` -(mdx - (files file.md)) -``` - -This allows to test the consistency of a markdown file using the normal dev -workflow: - -``` -$ dune runtest -``` - -will display a diff of the output if something has changed. For instance: - -``` -$ dune runtest ------- file.md -++++++ file.md.corrected -File "file.md", line 23, characters 0-1: - | - |```sh --| $ for i in `seq 1 3`; do echo $i; done -+| $ for i in `seq 1 4`; do echo $i; done - | 1 - | 2 - | 3 -+| 4 - |``` -``` - -And the changes can then be accepted using: - -``` -$ dune promote -``` - -For further details about the mdx stanza you should read the -[according section](https://dune.readthedocs.io/en/latest/dune-files.html#mdx-since-2-4) -in the dune documentation. - #### Non-deterministic Tests **Non-deterministic Outputs** @@ -373,10 +382,3 @@ Those variables are then available in the subsequent blocks bar - : unit = () ``` - -### Sections - -It is possible to test or execute only a subset of the file using -sections using the `--section` option (short name is `-s`). For -instance `ocaml-mdx pp -s foo` will only consider the section matching the -perl regular expression `foo`.