diff --git a/CHANGES.md b/CHANGES.md index a10ab881c..d0d536da1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,9 @@ #### Added - Report all parsing errors in Markdown files (#389, @NathanReb) +- Add alternative syntax for explicitly setting the block-type. + The new label `block-type=...` can be set to `ocaml`, `toplevel`, `cram` or + `include`. (#385, @NathanReb) #### Changed diff --git a/doc/dune b/doc/dune new file mode 100644 index 000000000..716ec4ccc --- /dev/null +++ b/doc/dune @@ -0,0 +1,2 @@ +(documentation + (package mdx)) diff --git a/doc/dune_stanza.mld b/doc/dune_stanza.mld new file mode 100644 index 000000000..7653c3965 --- /dev/null +++ b/doc/dune_stanza.mld @@ -0,0 +1,2 @@ +{0 Dune Stanza} + diff --git a/doc/index.mld b/doc/index.mld new file mode 100644 index 000000000..85ed0c91c --- /dev/null +++ b/doc/index.mld @@ -0,0 +1,111 @@ +{0 MDX Manual} + +Welcome to the MDX Manual! + +MDX is a tool to help developpers and authors keep their documentation +up-to-date. +It ensures that the code examples you write compile and behave the way you +expect them to by actually running them. + +It is for example used to verify all of +{{:https://dev.realworldocaml.org/}Realworlocaml}'s examples! + +MDX is released on opam. You can install it in your switch by running: +{@sh[ + opam install mdx +]} + +{1 Table of Content} +- {{!page-markdown_syntax}Markdown MDX Syntax} +- {{!page-mli_syntax}.mli MDX Syntax} +- {{!page-types_of_blocks}Types of Blocks} +- {{!page-labels}Labels} +- {{!page-dune_stanza}Dune stanza} +- {{!page-preludes}Preludes} +- {{!page-using_libs}Using Libraries in your code examples} + +{1 Basic Usage} + +You can use MDX with your Markdown or [.mli] documentation, where it will ensure +the correctness of code respectively written in multi-line code blocks and +verbatim code blocks. + +To enable MDX, you need to add [(mdx)] stanzas to the right dune files. Before +that you will need to enable MDX for your project by adding the following to +your [dune-project]: + +{@dune[ + (using mdx 0.2) +]} + +You can then add the following in the relevant dune file: +{@dune[ + (mdx) +]} +That will enable MDX on all markdown files in the folder. +The MDX stanza can be further configured. If you want to know more about it, +visit the {{!page-mdx_dune_stanza}corresponding section of this manual} or the +one in +{{:https://dune.readthedocs.io/en/latest/dune-files.html#mdx-since-2-4}dune's manual}. + +MDX supports various type of code blocks but the most common are OCaml toplevel +blocks so we'll look at one of those for our example. In a markdown file, you +would write something along those lines: + +{@markdown[ + 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 [;;]. +Each of them is followed by the toplevel evaluation, or "output" which you +probably are already familiar with. + +Now you probably have noticed that [1 + 2] is not equal to [3] nor ["a" ^ "bc"] +to ["ab"]. Somebody must have updated the phrases but they then forgot to update +the evaluation. + +That's exactly what MDX is here for! + +If MDX were enabled for this file and they ran [dune runtest], here's what they +would have gotten: + +{@sh[ + $ 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: + +{@sh[ + dune promote +]} + +Now the documentation is up-to-date and running [dune runtest] again should be +successful! diff --git a/doc/labels.mld b/doc/labels.mld new file mode 100644 index 000000000..48af25f35 --- /dev/null +++ b/doc/labels.mld @@ -0,0 +1,272 @@ +{0 Labels} + +MDX blocks behaviour can be customised through the use of labels. + +This section documents in detail what each existing label does and how to use +it. + +{1:dir Dir} + +{2 Description} + +This label allows you to specify the working directory from which the block +should be evaluated. + +{2 Syntax} + +[dir=] + +The dir label expects a single string value which corresponds to the path +to the directory from which the block must be evaluated. It should be a path +relative to the directory containing the current [.mli]/[.mld]/[.md] file. + +[.mli] example: +{v +(** We will list the files in subdir: + {@sh dir=subdir[ + $ ls + something.ml something_else.ml + ]} *) +v} + +[.md] example: +{@markdown[ +We will list the files in subdir: + +```sh +$ ls +something.ml something_else.ml +``` +]} + +{2 Applies to} + +- {{!page-types_of_blocks.ocaml_toplevel} OCaml Toplevel Blocks} +- {{!page-types_of_blocks.ocaml} OCaml Blocks} +- {{!page-types_of_blocks.file_include} File Include Blocks} +- {{!page-types_of_blocks.shell} Shell Blocks} + +{2 Default} + +When absent, the block will be evaluated from the directory of +the file being tested by MDX. + +{1:source_tree Source Tree} + +{2 Description} + +{2 Syntax} + +{2 Applies to} + +{2 Default} + +{1:file File} + +{2 Description} + +This label allows you + +{2 Syntax} + +[file=] + +The environement label expects a single string value which corresponds to the +name of the environment to use. + +[.mli] example: +{v +(** Here is how to use this function: + {@ocaml env=foo[ + # f 0;; + - : int = 0 + ]} *) +v} + +[.md] example: +{@markdown[ +Here is how to use this function: + +```ocaml +# f 0;; +- : int = 0 +``` +]} + +{2 Applies to} + +- {{!page-types_of_blocks.ocaml_toplevel} OCaml Toplevel Blocks} +- {{!page-types_of_blocks.ocaml} OCaml Blocks} + +{2 Default} + +When absent, the block will be evaluated in the default environment, e.g. +the environment shared by all blocks without an [env] label. + +{1:skip Skip} + +{2 Description} + +This label allows you to explicitly ask MDX not to interpret a block, no +matter its content. + +{2 Syntax} + +[skip] + +The [skip] label takes no value. + +[.mli] example: +{v +(** MDX should not interpret the following block: + {@ocaml skip[ + # 1 + 1;; + - : int = 3 + ]} +v} + +[.md] example: +{@markdown[ +MDX should not interpret the following block: + +```ocaml +# 1 + 1;; +- : int = 3 +``` +]} + +{2 Applies to} + +- {{!page-types_of_blocks.ocaml_toplevel} OCaml Toplevel Blocks} +- {{!page-types_of_blocks.ocaml} OCaml Blocks} +- {{!page-types_of_blocks.file_include} File Include Blocks} +- {{!page-types_of_blocks.shell} Shell Blocks} + +{2 Default} + +By default MDX will interpret any block it knows how to deal with and skip +any other block. + +{1:non_det Non Deterministic} + +{2 Description} + +{2 Syntax} + +{2 Applies to} + +{2 Default} + +{1:version Version} + +{2 Description} + +{2 Syntax} + +{2 Applies to} + +{2 Default} + +{1:set Set} + +{2 Description} + +{2 Syntax} + +{2 Applies to} + +{2 Default} + +{1:unset Unset} + +{2 Description} + +{2 Syntax} + +{2 Applies to} + +{2 Default} + +{1:type Type} + +{2 Description} + +This label allows you to explicitly set the type of the block as described +in {{!page.types_of_blocks}this section}. +Explicitly setting the type of the block instead of relying on MDX to infer +it will provide better error messages and guidance in case of syntax errors or +labels misuse. + +{2 Syntax} + +[type=] + +The [type] label accepts one of the following values: +- [toplevel] for OCaml Toplevel Blocks +- [ocaml] for OCaml Blocks +- [cram] for Shell Blocks +- [include] for File Include Blocks + +[.mli] example: +{v +(** The following block is a toplevel block: + {@ocaml type=toplevel[ + # 1 + 1;; + - : int = 2 + ]} *) +v} + +[.md] example: +{@markdown[ +The following block is a toplevel block: + +```ocaml +# 1 + 1;; +- : int = 2 +``` +]} + +{2 Applies to} + +- {{!page-types_of_blocks.ocaml_toplevel} OCaml Toplevel Blocks} +- {{!page-types_of_blocks.ocaml} OCaml Blocks} +- {{!page-types_of_blocks.file_include} File Include Blocks} +- {{!page-types_of_blocks.shell} Shell Blocks} + +{2 Default} + +By default, MDX will infer the type of the block based on its language header, +its content and its labels. diff --git a/doc/markdown_syntax.mld b/doc/markdown_syntax.mld new file mode 100644 index 000000000..1333ed77b --- /dev/null +++ b/doc/markdown_syntax.mld @@ -0,0 +1 @@ +TODO diff --git a/doc/mli_syntax.mld b/doc/mli_syntax.mld new file mode 100644 index 000000000..1333ed77b --- /dev/null +++ b/doc/mli_syntax.mld @@ -0,0 +1 @@ +TODO diff --git a/doc/preludes.mld b/doc/preludes.mld new file mode 100644 index 000000000..37a16551e --- /dev/null +++ b/doc/preludes.mld @@ -0,0 +1,12 @@ +{0 Preludes} + +Preludes are fragments of code that can be evaluated ahead of running the +code in OCaml and OCaml toplevel MDX blocks. They can be used to globally +open modules, register toplevel printers, set some globals like +[Printexc.record_backtrace], you name it! + +They are especially useful if you want to hide or share this kind of setup +phase, e.g. if you are writing polished documentation you want to publish +or you are using MDX to add tests in your mli files directly. + +TODO diff --git a/doc/types_of_blocks.mld b/doc/types_of_blocks.mld new file mode 100644 index 000000000..5ea90efd1 --- /dev/null +++ b/doc/types_of_blocks.mld @@ -0,0 +1,38 @@ +{0 Types of Blocks} + +MDX searches for code blocks within your documentation to make sure they are up +to date. +It supports different type of block contents, each have their own behaviour. + +If a code block does not belong to one of the categories described below, MDX +will simply ignore it. + +The type of a block can either be set explicitly using the +{{!page-labels.type}[type]} label, otherwise MDX +will try to infer the type of the block based on its language tag, labels and +content. +Setting the block type explicitly allows MDX to better report syntax errors +or invalid labels. + +{1:ocaml_toplevel OCaml Toplevel Blocks} + +OCaml Toplevel Blocks are composed of a sequence of toplevel phrases, starting +with a [#] and a space and ending with [;;] followed by the output of the +toplevel evaluation of the phrase. + + +{1:ocaml OCaml Blocks} + +{1:file_include File Include Blocks} + +File Include Blocks are synchronized with the content of another file. Upon +running MDX, the content of the block will be refreshed to match the content +of the file specified in the block's {{!page-labels.file}[file]} label. + +It can be any kind of text file, [.ml], [dune], [.c], the content will simply be +copied as is. + +It is possible for OCaml files to only include specific parts of the file, using +the {{!page-labels.part}[part]} label and delimiter comments in the source file. + +{1:shell Shell Blocks} diff --git a/doc/using_libs.mld b/doc/using_libs.mld new file mode 100644 index 000000000..1333ed77b --- /dev/null +++ b/doc/using_libs.mld @@ -0,0 +1 @@ +TODO diff --git a/lib/block.ml b/lib/block.ml index 4a53ce329..9ebe0270f 100644 --- a/lib/block.ml +++ b/lib/block.ml @@ -414,7 +414,7 @@ let infer_block ~loc ~config ~header ~contents ~errors = let mk ~loc ~section ~labels ~legacy_labels ~header ~contents ~errors = let block_kind = - get_label (function Block_kind x -> Some x | _ -> None) labels + get_label (function Block_type x -> Some x | _ -> None) labels in let config = get_block_config labels in (match block_kind with diff --git a/lib/label.ml b/lib/label.ml index f9f93d73d..3c31ceded 100644 --- a/lib/label.ml +++ b/lib/label.ml @@ -71,7 +71,13 @@ type non_det = Nd_output | Nd_command let default_non_det = Nd_output -type block_kind = OCaml | Cram | Toplevel | Include +type block_type = OCaml | Cram | Toplevel | Include + +let pp_block_type ppf = function + | OCaml -> Fmt.string ppf "ocaml" + | Cram -> Fmt.string ppf "cram" + | Toplevel -> Fmt.string ppf "toplevel" + | Include -> Fmt.string ppf "include" type t = | Dir of string @@ -84,13 +90,7 @@ type t = | Version of Relation.t * Ocaml_version.t | Set of string * string | Unset of string - | Block_kind of block_kind - -let pp_block_kind ppf = function - | OCaml -> Fmt.string ppf "ocaml" - | Cram -> Fmt.string ppf "cram" - | Toplevel -> Fmt.string ppf "toplevel" - | Include -> Fmt.string ppf "include" + | Block_type of block_type let pp ppf = function | Dir d -> Fmt.pf ppf "dir=%s" d @@ -106,7 +106,7 @@ let pp ppf = function Fmt.pf ppf "version%a%a" Relation.pp op Ocaml_version.pp v | Set (v, x) -> Fmt.pf ppf "set-%s=%s" v x | Unset x -> Fmt.pf ppf "unset-%s" x - | Block_kind bk -> pp_block_kind ppf bk + | Block_type bt -> Fmt.pf ppf "type=%a" pp_block_type bt let is_prefix ~prefix s = let len_prefix = String.length prefix in @@ -140,41 +140,65 @@ let requires_value ~label ~value f = let requires_eq_value ~label ~value f = requires_value ~label ~value (fun op value -> - match op with Relation.Eq -> Ok (f value) | _ -> non_eq_op ~label) + match op with Relation.Eq -> f value | _ -> non_eq_op ~label) + +let version_of_string s = + match Ocaml_version.of_string s with + | Ok v -> Ok v + | Error (`Msg e) -> Util.Result.errorf "Invalid version: %s." e + +let parse_non_det_value ~label s = + match s with + | "output" -> Ok Nd_output + | "command" -> Ok Nd_command + | s -> + let allowed_values = [ ""; {|"command"|}; {|"output"|} ] in + invalid_value ~label ~allowed_values s + +let parse_block_type_value ~label s = + match s with + | "ocaml" -> Ok OCaml + | "cram" -> Ok Cram + | "toplevel" -> Ok Toplevel + | "include" -> Ok Include + | s -> + let allowed_values = + [ {|"ocaml"|}; {|"cram"|}; {|"toplevel"|}; {|"include"|} ] + in + invalid_value ~label ~allowed_values s let interpret label value = + let open Util.Result.Infix in match label with | "skip" -> doesnt_accept_value ~label ~value Skip - | "ocaml" -> doesnt_accept_value ~label ~value (Block_kind OCaml) - | "cram" -> doesnt_accept_value ~label ~value (Block_kind Cram) - | "toplevel" -> doesnt_accept_value ~label ~value (Block_kind Toplevel) - | "include" -> doesnt_accept_value ~label ~value (Block_kind Include) + | "ocaml" -> doesnt_accept_value ~label ~value (Block_type OCaml) + | "cram" -> doesnt_accept_value ~label ~value (Block_type Cram) + | "toplevel" -> doesnt_accept_value ~label ~value (Block_type Toplevel) + | "include" -> doesnt_accept_value ~label ~value (Block_type Include) | v when is_prefix ~prefix:"unset-" v -> doesnt_accept_value ~label ~value (Unset (split_prefix ~prefix:"unset-" v)) | "version" -> requires_value ~label ~value (fun op v -> - match Ocaml_version.of_string v with - | Ok v -> Ok (Version (op, v)) - | Error (`Msg e) -> - Util.Result.errorf "Invalid `version` label value: %s." e) + version_of_string v >>= fun v -> Ok (Version (op, v))) | "non-deterministic" -> ( match value with | None -> Ok (Non_det None) - | Some (Relation.Eq, "output") -> Ok (Non_det (Some Nd_output)) - | Some (Relation.Eq, "command") -> Ok (Non_det (Some Nd_command)) - | Some (Relation.Eq, v) -> - let allowed_values = [ ""; {|"command"|}; {|"output"|} ] in - invalid_value ~label ~allowed_values v + | Some (Relation.Eq, s) -> + parse_non_det_value ~label s >>= fun nd -> Ok (Non_det (Some nd)) | Some _ -> non_eq_op ~label) - | "dir" -> requires_eq_value ~label ~value (fun x -> Dir x) - | "source-tree" -> requires_eq_value ~label ~value (fun x -> Source_tree x) - | "file" -> requires_eq_value ~label ~value (fun x -> File x) - | "part" -> requires_eq_value ~label ~value (fun x -> Part x) - | "env" -> requires_eq_value ~label ~value (fun x -> Env x) + | "dir" -> requires_eq_value ~label ~value (fun x -> Ok (Dir x)) + | "source-tree" -> + requires_eq_value ~label ~value (fun x -> Ok (Source_tree x)) + | "file" -> requires_eq_value ~label ~value (fun x -> Ok (File x)) + | "part" -> requires_eq_value ~label ~value (fun x -> Ok (Part x)) + | "env" -> requires_eq_value ~label ~value (fun x -> Ok (Env x)) + | "type" -> + requires_eq_value ~label ~value (fun x -> + parse_block_type_value ~label x >>= fun bt -> Ok (Block_type bt)) | l when is_prefix ~prefix:"set-" l -> requires_eq_value ~label ~value (fun x -> - Set (split_prefix ~prefix:"set-" l, x)) + Ok (Set (split_prefix ~prefix:"set-" l, x))) | l -> Error (`Msg (Format.sprintf "`%s` is not a valid label." l)) let of_string s = diff --git a/lib/label.mli b/lib/label.mli index fdba0a603..5ae377bfc 100644 --- a/lib/label.mli +++ b/lib/label.mli @@ -29,7 +29,7 @@ type non_det = Nd_output | Nd_command val default_non_det : non_det -type block_kind = OCaml | Cram | Toplevel | Include +type block_type = OCaml | Cram | Toplevel | Include type t = | Dir of string @@ -42,7 +42,7 @@ type t = | Version of Relation.t * Ocaml_version.t | Set of string * string | Unset of string - | Block_kind of block_kind + | Block_type of block_type val pp : Format.formatter -> t -> unit diff --git a/lib/mdx.mli b/lib/mdx.mli index 38d432a00..41f3978c7 100644 --- a/lib/mdx.mli +++ b/lib/mdx.mli @@ -17,7 +17,7 @@ (** [Mdx] is a library to manipulate markdown code blocks. [mdx] allows to execute code blocks inside markdown files. The - supported code {{!Block}blocks} are either {{!Cram}cram-like} + supported code {{!module-Block}blocks} are either {{!module-Cram}cram-like} tests, raw OCaml fragments or {{!Toplevel}toplevel} phrases. Cram tests and toplevel phrases are sequences of commands and diff --git a/test/bin/mdx-test/expect/block-type/test-case.md b/test/bin/mdx-test/expect/block-type/test-case.md new file mode 100644 index 000000000..98b4bd329 --- /dev/null +++ b/test/bin/mdx-test/expect/block-type/test-case.md @@ -0,0 +1,24 @@ +It is possible to explicitly state the type of a block using the +`block-type` label, working around language header and content based +inference which can sometime lead to troublesome error messages. + +The following blocks use a volontarily misleading language header that would +normally lead to errors if we let MDX infer the type of block based on them. + + +```sh +# 1 + 1;; +``` + + +```sh +let x = 2 +``` + + +```ocaml +$ echo "boom" +``` + +The include block type is somewhat redundant with the `file=...` label as +so it is not tested here. diff --git a/test/bin/mdx-test/expect/block-type/test-case.md.expected b/test/bin/mdx-test/expect/block-type/test-case.md.expected new file mode 100644 index 000000000..d152dcb8e --- /dev/null +++ b/test/bin/mdx-test/expect/block-type/test-case.md.expected @@ -0,0 +1,26 @@ +It is possible to explicitly state the type of a block using the +`block-type` label, working around language header and content based +inference which can sometime lead to troublesome error messages. + +The following blocks use a volontarily misleading language header that would +normally lead to errors if we let MDX infer the type of block based on them. + + +```ocaml +# 1 + 1;; +- : int = 2 +``` + + +```ocaml +let x = 2 +``` + + +```sh +$ echo "boom" +boom +``` + +The include block type is somewhat redundant with the `file=...` label as +so it is not tested here. diff --git a/test/bin/mdx-test/expect/dune.inc b/test/bin/mdx-test/expect/dune.inc index 54b10eeba..c66e114c3 100644 --- a/test/bin/mdx-test/expect/dune.inc +++ b/test/bin/mdx-test/expect/dune.inc @@ -11,6 +11,18 @@ (alias runtest) (action (diff bash-fence/test-case.md.expected bash-fence.actual))) +(rule + (target block-type.actual) + (deps (package mdx) (source_tree block-type)) + (action + (with-stdout-to %{target} + (chdir block-type + (run ocaml-mdx test --output - test-case.md))))) + +(rule + (alias runtest) + (action (diff block-type/test-case.md.expected block-type.actual))) + (rule (target casual-file-inc.actual) (deps (package mdx) (source_tree casual-file-inc)) diff --git a/test/bin/mdx-test/failure/block-type-value/test-case.md b/test/bin/mdx-test/failure/block-type-value/test-case.md new file mode 100644 index 000000000..71e020bda --- /dev/null +++ b/test/bin/mdx-test/failure/block-type-value/test-case.md @@ -0,0 +1,14 @@ +This tests that erros are properly reported when the `block-type` label +is misused. + +It requires a value + + +```ocaml +``` + +It only accept a fixed set of values + + +```ocaml +``` diff --git a/test/bin/mdx-test/failure/block-type-value/test-case.md.expected b/test/bin/mdx-test/failure/block-type-value/test-case.md.expected new file mode 100644 index 000000000..8931f1eff --- /dev/null +++ b/test/bin/mdx-test/failure/block-type-value/test-case.md.expected @@ -0,0 +1,2 @@ +[mdx] Fatal error: File "test-case.md", lines 6-8: invalid code block: Label `type` requires a value. +[mdx] Fatal error: File "test-case.md", lines 12-14: invalid code block: "invalid" is not a valid value for label `type`. Valid values are "ocaml", "cram", "toplevel" and "include". diff --git a/test/bin/mdx-test/failure/dune.inc b/test/bin/mdx-test/failure/dune.inc index 9bf381ab6..87739745f 100644 --- a/test/bin/mdx-test/failure/dune.inc +++ b/test/bin/mdx-test/failure/dune.inc @@ -12,6 +12,19 @@ (alias runtest) (action (diff block-locations/test-case.md.expected block-locations.actual))) +(rule + (target block-type-value.actual) + (deps (package mdx) (source_tree block-type-value)) + (action + (with-accepted-exit-codes 1 + (with-outputs-to %{target} + (chdir block-type-value + (run %{bin:ocaml-mdx} test test-case.md)))))) + +(rule + (alias runtest) + (action (diff block-type-value/test-case.md.expected block-type-value.actual))) + (rule (target both-prelude.actual) (deps (package mdx) (source_tree both-prelude)) @@ -65,6 +78,19 @@ (enabled_if (<> %{os_type} Win32)) (action (diff in-toplevel/test-case.md.expected in-toplevel.actual))) +(rule + (target include-without-file-label.actual) + (deps (package mdx) (source_tree include-without-file-label)) + (action + (with-accepted-exit-codes 1 + (with-outputs-to %{target} + (chdir include-without-file-label + (run %{bin:ocaml-mdx} test test-case.md)))))) + +(rule + (alias runtest) + (action (diff include-without-file-label/test-case.md.expected include-without-file-label.actual))) + (rule (target invalid-label.actual) (deps (package mdx) (source_tree invalid-label)) diff --git a/test/bin/mdx-test/failure/include-without-file-label/test-case.md b/test/bin/mdx-test/failure/include-without-file-label/test-case.md new file mode 100644 index 000000000..45cd9e5b8 --- /dev/null +++ b/test/bin/mdx-test/failure/include-without-file-label/test-case.md @@ -0,0 +1,6 @@ +Explicitly setting the block-type to include has little benefits except +for warning you that the `file=...` label is mandatory. + + +```ocaml +``` diff --git a/test/bin/mdx-test/failure/include-without-file-label/test-case.md.expected b/test/bin/mdx-test/failure/include-without-file-label/test-case.md.expected new file mode 100644 index 000000000..2deddf767 --- /dev/null +++ b/test/bin/mdx-test/failure/include-without-file-label/test-case.md.expected @@ -0,0 +1 @@ +[mdx] Fatal error: File "test-case.md", lines 4-6: invalid code block: `file` label is required for include blocks. diff --git a/test/lib/test_block.ml b/test/lib/test_block.ml index 86312d883..f9d687bf0 100644 --- a/test/lib/test_block.ml +++ b/test/lib/test_block.ml @@ -38,10 +38,10 @@ let test_mk = (test_name, `Quick, test_fun) in [ - make_test ~name:"invalid ocaml" ~labels:[ Block_kind OCaml ] + make_test ~name:"invalid ocaml" ~labels:[ Block_type OCaml ] ~header:(Some OCaml) ~contents:[ "# let x = 2;;" ] ~expected:(Error (`Msg "toplevel syntax is not allowed in OCaml blocks.")); - make_test ~name:"invalid toplevel" ~labels:[ Block_kind Toplevel ] + make_test ~name:"invalid toplevel" ~labels:[ Block_type Toplevel ] ~header:(Some OCaml) ~contents:[ "let x = 2;;" ] ~expected:(Error (`Msg "invalid toplevel syntax in toplevel blocks.")); ]