Skip to content

Commit 88bff95

Browse files
committed
fix: make directives usable in the ocaml mode
Prior this PR, the following code fails: ``` {@ocaml ocaml[ #require "astring";; let x = Astring.strf;; ]} ``` because MDX incorrectly infers that the code block is a toplevel interaction (from the fact that the block starts with `#`), resulting in an error: incomplete toplevel entry: unexpected character '#'. Did you forget a space after the '#' at the start of the line It is possible to workaround this issue as suggested in #421 by adding a comment as a first line. ``` {@ocaml ocaml[ (* This works! *) #require "astring";; let x = Astring.strf;; ]} ``` but ideally the workaround should not be needed. One may wonder why the inference is needed, since the above code block is already specified to be in the `ocaml` mode. The answer appears to be that we are expected to use the inference heuristics for a light sanity check, as the existing tests ("invalid ocaml" and "invalid toplevel" in `test_block.ml`) require: "let x = 2;;" in the toplevel mode should error with invalid toplevel syntax in toplevel blocks "# let x = 2;;" in the ocaml mode should error with toplevel syntax is not allowed in OCaml blocks As a result, this PR keeps the light sanity check intact, but adjusts the inference heuristics to be more conservative. A block is now considered a toplevel interaction when it starts with `#` followed by a space. This fixes the issue, making it possible to use directives. As a bonus, directives will now also work even when the mode is not specified at all. But one disadvantage is that this kind of code will no longer be considered invalid. ``` ... ... ```
1 parent 3226c6e commit 88bff95

File tree

3 files changed

+18
-2
lines changed

3 files changed

+18
-2
lines changed

CHANGES.md

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

55
- Handle the error-blocks syntax (#439, @jonludlam, @gpetiot)
6+
- Fix directives not allowed in the `ocaml` mode.
67

78
### 2.3.1
89

lib/block.ml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ let guess_ocaml_kind contents =
300300
| h :: t ->
301301
let h = String.trim h in
302302
if h = "" then aux t
303-
else if String.length h > 1 && h.[0] = '#' then `Toplevel
303+
else if String.length h > 2 && h.[0] = '#' && h.[1] = ' ' then `Toplevel
304304
else `Code
305305
in
306306
aux contents
@@ -383,7 +383,10 @@ let mk_ocaml ~loc ~config ~header ~contents ~errors =
383383
let kind = "OCaml" in
384384
match config with
385385
| { file_inc = None; part = None; env; non_det; _ } -> (
386-
(* TODO: why does this call guess_ocaml_kind when infer_block already did? *)
386+
(* mk_ocaml can be invoked because an explicit "$MDX ocaml" is given.
387+
In such case, we still want to make it an error if
388+
a toplevel interaction is given (see the test "invalid ocaml"),
389+
so we use guess_ocaml_kind here to do that. *)
387390
match guess_ocaml_kind contents with
388391
| `Code -> Ok (OCaml { env = Ocaml_env.mk env; non_det; errors; header })
389392
| `Toplevel ->

test/bin/mdx-test/expect/simple-mld/test-case.mld

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,15 @@ Line 1, characters 15-18:
6767
Error: This expression has type string but an expression was expected of type
6868
int
6969
]err}]}
70+
71+
72+
{@ocaml ocaml[
73+
#require "astring";;
74+
let x = Astring.strf;;
75+
]}
76+
77+
78+
{[
79+
# x;;
80+
- : ('a, Format.formatter, unit, string) format4 -> 'a = <fun>
81+
]}

0 commit comments

Comments
 (0)