Skip to content

Commit b3e681c

Browse files
panglesdjonludlam
authored andcommitted
odoc extract-code cleanup and test
1 parent de3dbff commit b3e681c

File tree

4 files changed

+171
-44
lines changed

4 files changed

+171
-44
lines changed

src/odoc/extract_code.ml

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,76 @@
11
open Odoc_utils
22
open Odoc_parser
33

4-
let rec nestable_block_element = function
5-
| {
6-
Loc.location = _;
7-
value = `Verbatim _ | `Modules _ | `Math_block _ | `Media _ | `Paragraph _;
8-
} ->
9-
()
10-
| {
11-
location = _;
12-
value = `Code_block { Ast.content = { value; location }; _ };
13-
} ->
14-
Format.printf "#%d \"%s\"\n" (location.start.line + 1) location.file;
15-
Format.printf "%s"
16-
(String.v ~len:(location.start.column + 1) (fun _ -> ' '));
17-
Format.printf "%s" value
18-
| { location = _; value = `List (_, _, l) } ->
19-
List.iter (List.iter nestable_block_element) l
20-
| { location = _; value = `Table ((table, _), _) } ->
4+
let tags_included_in_names names tags =
5+
let fields = String.fields ~empty:false tags in
6+
List.exists
7+
(fun tag ->
8+
match String.cut ~sep:"=" tag with
9+
| Some ("name", n) -> List.exists (String.equal n) names
10+
| _ -> false)
11+
fields
12+
13+
let needs_extraction names meta =
14+
let check_language l = String.equal "ocaml" l.Loc.value in
15+
let check_name tags =
16+
if List.is_empty names then true
17+
else
18+
match tags with
19+
| None -> false
20+
| Some tags -> tags_included_in_names names tags.Loc.value
21+
in
22+
match meta with
23+
| None -> false
24+
| Some meta -> check_language meta.Ast.language && check_name meta.tags
25+
26+
let print oc line_directives location value =
27+
if line_directives then (
28+
Printf.fprintf oc "#%d \"%s\"\n" (location.Loc.start.line + 1) location.file;
29+
Printf.fprintf oc "%s%s\n"
30+
(String.v ~len:(location.start.column + 1) (fun _ -> ' '))
31+
value)
32+
else Printf.fprintf oc "%s" value
33+
34+
let rec nestable_block_element line_directives oc names v =
35+
match v.Loc.value with
36+
| `Verbatim _ | `Modules _ | `Math_block _ | `Media _ | `Paragraph _ -> ()
37+
| `Code_block { Ast.content = { value; location }; meta; _ }
38+
when needs_extraction names meta ->
39+
print oc line_directives location value
40+
| `Code_block _ -> ()
41+
| `List (_, _, l) ->
42+
List.iter (List.iter (nestable_block_element line_directives oc names)) l
43+
| `Table ((table, _), _) ->
2144
List.iter
22-
(List.iter (fun (x, _) -> List.iter nestable_block_element x))
45+
(List.iter (fun (x, _) ->
46+
List.iter (nestable_block_element line_directives oc names) x))
2347
table
2448

25-
and block_element = function
26-
| {
27-
Loc.value =
28-
`Tag
29-
( `Deprecated l
30-
| `Param (_, l)
31-
| `Raise (_, l)
32-
| `Return l
33-
| `See (_, _, l)
34-
| `Before (_, l) );
35-
_;
36-
} ->
37-
List.iter nestable_block_element l
38-
| {
39-
Loc.value =
40-
`Tag
41-
( `Author _ | `Since _ | `Version _ | `Canonical _ | `Inline | `Open
42-
| `Children_order _ | `Toc_status _ | `Order_category _
43-
| `Short_title _ | `Closed | `Hidden );
44-
_;
45-
}
46-
| { Loc.value = `Heading _; _ } ->
49+
and block_element line_directives oc names v =
50+
match v.Loc.value with
51+
| `Tag
52+
( `Deprecated l
53+
| `Param (_, l)
54+
| `Raise (_, l)
55+
| `Return l
56+
| `See (_, _, l)
57+
| `Before (_, l) ) ->
58+
List.iter (nestable_block_element line_directives oc names) l
59+
| `Tag
60+
( `Author _ | `Since _ | `Version _ | `Canonical _ | `Inline | `Open
61+
| `Children_order _ | `Toc_status _ | `Order_category _ | `Short_title _
62+
| `Closed | `Hidden )
63+
| `Heading _ ->
4764
()
48-
| { Loc.value = #Ast.nestable_block_element; _ } as x ->
49-
nestable_block_element x
65+
| #Ast.nestable_block_element as value ->
66+
nestable_block_element line_directives oc names { v with value }
5067

51-
let extract ~dst:_ ~input ~names:_ ~line_directives:_ =
68+
let extract ~dst ~input ~names ~line_directives =
5269
let location =
5370
{ Lexing.pos_fname = input; pos_lnum = 0; pos_bol = 0; pos_cnum = 0 }
5471
in
5572
let c = Io_utils.read_lines input |> String.concat ~sep:"\n" in
5673
let parsed = parse_comment ~location ~text:c in
5774
let ast = ast parsed in
58-
List.iter block_element ast
75+
let go oc = List.iter (block_element line_directives oc names) ast in
76+
match dst with None -> go stdout | Some dst -> Io_utils.with_open_out dst go

test/extract_code/dune

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(cram
2+
(package odoc)
3+
(deps %{bin:odoc}))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{0 Test}
2+
3+
By default, language is assumed to be OCaml
4+
5+
{[
6+
(** By default, an odoc code block is assumed to contain OCaml code *)
7+
let () = ()
8+
]}
9+
10+
{@c name=c-quine[
11+
#include <stdio.h>
12+
int main(){
13+
char*a="#include <stdio.h>%cint main(){char*a=%c%s%c;printf(a,10,34,a,34);}";
14+
printf(a,10,34,a,34);
15+
}
16+
]}
17+
18+
{@ocaml name=error.ml name=printing[let x = 5]}
19+
20+
{@ocaml name=printing[
21+
let () = print_int x
22+
]}
23+
24+
25+
{@ocaml name=error.ml[
26+
let y = x + 6. (* This is a typing error *)
27+
]}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
Without --name argument, all OCaml code blocks are extracted
2+
3+
$ odoc extract-code main.mld
4+
let x = 5
5+
let () = print_int x
6+
7+
let y = x + 6. (* This is a typing error *)
8+
9+
10+
We can add (OCaml) line directives
11+
12+
$ odoc extract-code --line-directives main.mld
13+
#18 "main.mld"
14+
let x = 5
15+
#20 "main.mld"
16+
17+
let () = print_int x
18+
19+
#25 "main.mld"
20+
21+
let y = x + 6. (* This is a typing error *)
22+
23+
24+
We can restrict to a named code blocks
25+
26+
$ odoc extract-code --line-directives --name error.ml main.mld
27+
#18 "main.mld"
28+
let x = 5
29+
#25 "main.mld"
30+
31+
let y = x + 6. (* This is a typing error *)
32+
33+
34+
We can output to a file
35+
36+
$ odoc extract-code --line-directives --name error.ml -o error.ml main.mld
37+
$ cat error.ml
38+
#18 "main.mld"
39+
let x = 5
40+
#25 "main.mld"
41+
42+
let y = x + 6. (* This is a typing error *)
43+
44+
45+
Let's check line directive work:
46+
47+
$ ocaml error.ml
48+
File "main.mld", line 26, characters 15-17:
49+
Error: This expression has type "float" but an expression was expected of type
50+
"int"
51+
[2]
52+
53+
Here is the line 26, and the characters 15-17:
54+
55+
$ sed -n '26p' main.mld
56+
let y = x + 6. (* This is a typing error *)
57+
$ sed -n '26p' main.mld | cut -c15-17
58+
6.
59+
60+
We can get content from multiple names
61+
62+
$ odoc extract-code --line-directives --name error.ml --name printing main.mld
63+
#18 "main.mld"
64+
let x = 5
65+
#20 "main.mld"
66+
67+
let () = print_int x
68+
69+
#25 "main.mld"
70+
71+
let y = x + 6. (* This is a typing error *)
72+
73+
$ odoc extract-code --line-directives --name error.ml --name printing -o names.ml main.mld
74+
$ ocaml names.ml
75+
File "main.mld", line 26, characters 15-17:
76+
Error: This expression has type "float" but an expression was expected of type
77+
"int"
78+
5
79+
[2]

0 commit comments

Comments
 (0)