Skip to content

Commit 8e02014

Browse files
committed
Introduce new syntax for explicit block type
This introduce a new, more explicit labels `block-type=...` which is meant to replace the four separate labels `cram`, `ocaml`, `toplevel` and `include` in the future. It is added for a transition period at the end of which the four labels of the apocalypse should be removed in favor of this new one. Signed-off-by: Nathan Rebours <[email protected]>
1 parent d23b06b commit 8e02014

File tree

13 files changed

+163
-34
lines changed

13 files changed

+163
-34
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#### Added
44

55
- Report all parsing errors in Markdown files (#389, @NathanReb)
6+
- Add alternative syntax for explicitly setting the block-type.
7+
The new label `block-type=...` can be set to `ocaml`, `toplevel`, `cram` or
8+
`include`. (#<PR_NUMBER>, @NathanReb)
69

710
#### Changed
811

lib/block.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ let infer_block ~loc ~config ~header ~contents ~errors =
414414

415415
let mk ~loc ~section ~labels ~legacy_labels ~header ~contents ~errors =
416416
let block_kind =
417-
get_label (function Block_kind x -> Some x | _ -> None) labels
417+
get_label (function Block_type x -> Some x | _ -> None) labels
418418
in
419419
let config = get_block_config labels in
420420
(match block_kind with

lib/label.ml

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,13 @@ type non_det = Nd_output | Nd_command
7171

7272
let default_non_det = Nd_output
7373

74-
type block_kind = OCaml | Cram | Toplevel | Include
74+
type block_type = OCaml | Cram | Toplevel | Include
75+
76+
let pp_block_type ppf = function
77+
| OCaml -> Fmt.string ppf "ocaml"
78+
| Cram -> Fmt.string ppf "cram"
79+
| Toplevel -> Fmt.string ppf "toplevel"
80+
| Include -> Fmt.string ppf "include"
7581

7682
type t =
7783
| Dir of string
@@ -84,13 +90,7 @@ type t =
8490
| Version of Relation.t * Ocaml_version.t
8591
| Set of string * string
8692
| Unset of string
87-
| Block_kind of block_kind
88-
89-
let pp_block_kind ppf = function
90-
| OCaml -> Fmt.string ppf "ocaml"
91-
| Cram -> Fmt.string ppf "cram"
92-
| Toplevel -> Fmt.string ppf "toplevel"
93-
| Include -> Fmt.string ppf "include"
93+
| Block_type of block_type
9494

9595
let pp ppf = function
9696
| Dir d -> Fmt.pf ppf "dir=%s" d
@@ -106,7 +106,7 @@ let pp ppf = function
106106
Fmt.pf ppf "version%a%a" Relation.pp op Ocaml_version.pp v
107107
| Set (v, x) -> Fmt.pf ppf "set-%s=%s" v x
108108
| Unset x -> Fmt.pf ppf "unset-%s" x
109-
| Block_kind bk -> pp_block_kind ppf bk
109+
| Block_type bt -> Fmt.pf ppf "block-type=%a" pp_block_type bt
110110

111111
let is_prefix ~prefix s =
112112
let len_prefix = String.length prefix in
@@ -140,41 +140,65 @@ let requires_value ~label ~value f =
140140

141141
let requires_eq_value ~label ~value f =
142142
requires_value ~label ~value (fun op value ->
143-
match op with Relation.Eq -> Ok (f value) | _ -> non_eq_op ~label)
143+
match op with Relation.Eq -> f value | _ -> non_eq_op ~label)
144+
145+
let version_of_string s =
146+
match Ocaml_version.of_string s with
147+
| Ok v -> Ok v
148+
| Error (`Msg e) -> Util.Result.errorf "Invalid version: %s." e
149+
150+
let parse_non_det_value ~label s =
151+
match s with
152+
| "output" -> Ok Nd_output
153+
| "command" -> Ok Nd_command
154+
| s ->
155+
let allowed_values = [ "<none>"; {|"command"|}; {|"output"|} ] in
156+
invalid_value ~label ~allowed_values s
157+
158+
let parse_block_type_value ~label s =
159+
match s with
160+
| "ocaml" -> Ok OCaml
161+
| "cram" -> Ok Cram
162+
| "toplevel" -> Ok Toplevel
163+
| "include" -> Ok Include
164+
| s ->
165+
let allowed_values =
166+
[ {|"ocaml"|}; {|"cram"|}; {|"toplevel"|}; {|"include"|} ]
167+
in
168+
invalid_value ~label ~allowed_values s
144169

145170
let interpret label value =
171+
let open Util.Result.Infix in
146172
match label with
147173
| "skip" -> doesnt_accept_value ~label ~value Skip
148-
| "ocaml" -> doesnt_accept_value ~label ~value (Block_kind OCaml)
149-
| "cram" -> doesnt_accept_value ~label ~value (Block_kind Cram)
150-
| "toplevel" -> doesnt_accept_value ~label ~value (Block_kind Toplevel)
151-
| "include" -> doesnt_accept_value ~label ~value (Block_kind Include)
174+
| "ocaml" -> doesnt_accept_value ~label ~value (Block_type OCaml)
175+
| "cram" -> doesnt_accept_value ~label ~value (Block_type Cram)
176+
| "toplevel" -> doesnt_accept_value ~label ~value (Block_type Toplevel)
177+
| "include" -> doesnt_accept_value ~label ~value (Block_type Include)
152178
| v when is_prefix ~prefix:"unset-" v ->
153179
doesnt_accept_value ~label ~value
154180
(Unset (split_prefix ~prefix:"unset-" v))
155181
| "version" ->
156182
requires_value ~label ~value (fun op v ->
157-
match Ocaml_version.of_string v with
158-
| Ok v -> Ok (Version (op, v))
159-
| Error (`Msg e) ->
160-
Util.Result.errorf "Invalid `version` label value: %s." e)
183+
version_of_string v >>= fun v -> Ok (Version (op, v)))
161184
| "non-deterministic" -> (
162185
match value with
163186
| None -> Ok (Non_det None)
164-
| Some (Relation.Eq, "output") -> Ok (Non_det (Some Nd_output))
165-
| Some (Relation.Eq, "command") -> Ok (Non_det (Some Nd_command))
166-
| Some (Relation.Eq, v) ->
167-
let allowed_values = [ "<none>"; {|"command"|}; {|"output"|} ] in
168-
invalid_value ~label ~allowed_values v
187+
| Some (Relation.Eq, s) ->
188+
parse_non_det_value ~label s >>= fun nd -> Ok (Non_det (Some nd))
169189
| Some _ -> non_eq_op ~label)
170-
| "dir" -> requires_eq_value ~label ~value (fun x -> Dir x)
171-
| "source-tree" -> requires_eq_value ~label ~value (fun x -> Source_tree x)
172-
| "file" -> requires_eq_value ~label ~value (fun x -> File x)
173-
| "part" -> requires_eq_value ~label ~value (fun x -> Part x)
174-
| "env" -> requires_eq_value ~label ~value (fun x -> Env x)
190+
| "dir" -> requires_eq_value ~label ~value (fun x -> Ok (Dir x))
191+
| "source-tree" ->
192+
requires_eq_value ~label ~value (fun x -> Ok (Source_tree x))
193+
| "file" -> requires_eq_value ~label ~value (fun x -> Ok (File x))
194+
| "part" -> requires_eq_value ~label ~value (fun x -> Ok (Part x))
195+
| "env" -> requires_eq_value ~label ~value (fun x -> Ok (Env x))
196+
| "block-type" ->
197+
requires_eq_value ~label ~value (fun x ->
198+
parse_block_type_value ~label x >>= fun bt -> Ok (Block_type bt))
175199
| l when is_prefix ~prefix:"set-" l ->
176200
requires_eq_value ~label ~value (fun x ->
177-
Set (split_prefix ~prefix:"set-" l, x))
201+
Ok (Set (split_prefix ~prefix:"set-" l, x)))
178202
| l -> Error (`Msg (Format.sprintf "`%s` is not a valid label." l))
179203

180204
let of_string s =

lib/label.mli

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type non_det = Nd_output | Nd_command
2929

3030
val default_non_det : non_det
3131

32-
type block_kind = OCaml | Cram | Toplevel | Include
32+
type block_type = OCaml | Cram | Toplevel | Include
3333

3434
type t =
3535
| Dir of string
@@ -42,7 +42,7 @@ type t =
4242
| Version of Relation.t * Ocaml_version.t
4343
| Set of string * string
4444
| Unset of string
45-
| Block_kind of block_kind
45+
| Block_type of block_type
4646

4747
val pp : Format.formatter -> t -> unit
4848

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
It is possible to explicitly state the type of a block using the
2+
`block-type` label, working around language header and content based
3+
inference which can sometime lead to troublesome error messages.
4+
5+
The following blocks use a volontarily misleading language header that would
6+
normally lead to errors if we let MDX infer the type of block based on them.
7+
8+
```sh block-type=toplevel
9+
# 1 + 1;;
10+
```
11+
12+
```sh block-type=ocaml
13+
let x = 2
14+
```
15+
16+
```ocaml block-type=cram
17+
$ echo "boom"
18+
```
19+
20+
The include block type is somewhat redundant with the `file=...` label as
21+
so it is not tested here.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
It is possible to explicitly state the type of a block using the
2+
`block-type` label, working around language header and content based
3+
inference which can sometime lead to troublesome error messages.
4+
5+
The following blocks use a volontarily misleading language header that would
6+
normally lead to errors if we let MDX infer the type of block based on them.
7+
8+
```ocaml block-type=toplevel
9+
# 1 + 1;;
10+
- : int = 2
11+
```
12+
13+
```ocaml block-type=ocaml
14+
let x = 2
15+
```
16+
17+
```sh block-type=cram
18+
$ echo "boom"
19+
boom
20+
```
21+
22+
The include block type is somewhat redundant with the `file=...` label as
23+
so it is not tested here.

test/bin/mdx-test/expect/dune.inc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111
(alias runtest)
1212
(action (diff bash-fence/test-case.md.expected bash-fence.actual)))
1313

14+
(rule
15+
(target block-type.actual)
16+
(deps (package mdx) (source_tree block-type))
17+
(action
18+
(with-stdout-to %{target}
19+
(chdir block-type
20+
(run ocaml-mdx test --output - test-case.md)))))
21+
22+
(rule
23+
(alias runtest)
24+
(action (diff block-type/test-case.md.expected block-type.actual)))
25+
1426
(rule
1527
(target casual-file-inc.actual)
1628
(deps (package mdx) (source_tree casual-file-inc))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
This tests that erros are properly reported when the `block-type` label
2+
is misused.
3+
4+
It requires a value
5+
6+
```ocaml block-type
7+
```
8+
9+
It only accept a fixed set of values
10+
11+
```ocaml block-type=invalid
12+
```
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[mdx] Fatal error: File "test-case.md", lines 6-7: invalid code block: Label `block-type` requires a value.
2+
[mdx] Fatal error: File "test-case.md", lines 11-12: invalid code block: "invalid" is not a valid value for label `block-type`. Valid values are "ocaml", "cram", "toplevel" and "include".

test/bin/mdx-test/failure/dune.inc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@
1212
(alias runtest)
1313
(action (diff block-locations/test-case.md.expected block-locations.actual)))
1414

15+
(rule
16+
(target block-type-value.actual)
17+
(deps (package mdx) (source_tree block-type-value))
18+
(action
19+
(with-accepted-exit-codes 1
20+
(with-outputs-to %{target}
21+
(chdir block-type-value
22+
(run %{bin:ocaml-mdx} test test-case.md))))))
23+
24+
(rule
25+
(alias runtest)
26+
(action (diff block-type-value/test-case.md.expected block-type-value.actual)))
27+
1528
(rule
1629
(target both-prelude.actual)
1730
(deps (package mdx) (source_tree both-prelude))
@@ -65,6 +78,19 @@
6578
(enabled_if (<> %{os_type} Win32))
6679
(action (diff in-toplevel/test-case.md.expected in-toplevel.actual)))
6780

81+
(rule
82+
(target include-without-file-label.actual)
83+
(deps (package mdx) (source_tree include-without-file-label))
84+
(action
85+
(with-accepted-exit-codes 1
86+
(with-outputs-to %{target}
87+
(chdir include-without-file-label
88+
(run %{bin:ocaml-mdx} test test-case.md))))))
89+
90+
(rule
91+
(alias runtest)
92+
(action (diff include-without-file-label/test-case.md.expected include-without-file-label.actual)))
93+
6894
(rule
6995
(target invalid-label.actual)
7096
(deps (package mdx) (source_tree invalid-label))

0 commit comments

Comments
 (0)