Skip to content

Commit 75c70ff

Browse files
committed
add experimental command to format code blocks embedded in docstrings
1 parent 6e6fe0a commit 75c70ff

15 files changed

+564
-21
lines changed

compiler/ml/location.ml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,24 +231,29 @@ let error_of_exn exn =
231231

232232
(* taken from https://github.com/rescript-lang/ocaml/blob/d4144647d1bf9bc7dc3aadc24c25a7efa3a67915/parsing/location.ml#L380 *)
233233
(* This is the error report entry point. We'll replace the default reporter with this one. *)
234-
let rec default_error_reporter ?(src = None) ppf {loc; msg; sub} =
234+
let rec default_error_reporter ?(custom_intro = None) ?(src = None) ppf
235+
{loc; msg; sub} =
235236
setup_colors ();
236237
(* open a vertical box. Everything in our message is indented 2 spaces *)
237238
(* If src is given, it will display a syntax error after parsing. *)
238239
let intro =
239-
match src with
240-
| Some _ -> "Syntax error!"
241-
| None -> "We've found a bug for you!"
240+
match (custom_intro, src) with
241+
| Some intro, _ -> intro
242+
| None, Some _ -> "Syntax error!"
243+
| None, None -> "We've found a bug for you!"
242244
in
243245
Format.fprintf ppf "@[<v>@, %a@, %s@,@]"
244246
(print ~src ~message_kind:`error intro)
245247
loc msg;
246-
List.iter (Format.fprintf ppf "@,@[%a@]" (default_error_reporter ~src)) sub
248+
List.iter
249+
(Format.fprintf ppf "@,@[%a@]" (default_error_reporter ~custom_intro ~src))
250+
sub
247251
(* no need to flush here; location's report_exception (which uses this ultimately) flushes *)
248252

249253
let error_reporter = ref default_error_reporter
250254

251-
let report_error ?(src = None) ppf err = !error_reporter ~src ppf err
255+
let report_error ?(custom_intro = None) ?(src = None) ppf err =
256+
!error_reporter ~custom_intro ~src ppf err
252257

253258
let error_of_printer loc print x = errorf ~loc "%a@?" print x
254259

@@ -276,7 +281,8 @@ let rec report_exception_rec n ppf exn =
276281
match error_of_exn exn with
277282
| None -> reraise exn
278283
| Some `Already_displayed -> ()
279-
| Some (`Ok err) -> fprintf ppf "@[%a@]@." (report_error ~src:None) err
284+
| Some (`Ok err) ->
285+
fprintf ppf "@[%a@]@." (report_error ~custom_intro:None ~src:None) err
280286
with exn when n > 0 -> report_exception_rec (n - 1) ppf exn
281287

282288
let report_exception ppf exn = report_exception_rec 5 ppf exn

compiler/ml/location.mli

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,28 @@ val register_error_of_exn : (exn -> error option) -> unit
103103
a location, a message, and optionally sub-messages (each of them
104104
being located as well). *)
105105

106-
val report_error : ?src:string option -> formatter -> error -> unit
107-
108-
val error_reporter : (?src:string option -> formatter -> error -> unit) ref
106+
val report_error :
107+
?custom_intro:string option ->
108+
?src:string option ->
109+
formatter ->
110+
error ->
111+
unit
112+
113+
val error_reporter :
114+
(?custom_intro:string option ->
115+
?src:string option ->
116+
formatter ->
117+
error ->
118+
unit)
119+
ref
109120
(** Hook for intercepting error reports. *)
110121

111-
val default_error_reporter : ?src:string option -> formatter -> error -> unit
122+
val default_error_reporter :
123+
?custom_intro:string option ->
124+
?src:string option ->
125+
formatter ->
126+
error ->
127+
unit
112128
(** Original error reporter for use in hooks. *)
113129

114130
val report_exception : formatter -> exn -> unit

compiler/syntax/src/res_diagnostics.ml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,13 @@ let explain t =
131131

132132
let make ~start_pos ~end_pos category = {start_pos; end_pos; category}
133133

134-
let print_report diagnostics src =
134+
let print_report ?(custom_intro = None) ?(formatter = Format.err_formatter)
135+
diagnostics src =
135136
let rec print diagnostics src =
136137
match diagnostics with
137138
| [] -> ()
138139
| d :: rest ->
139-
Location.report_error ~src:(Some src) Format.err_formatter
140+
Location.report_error ~custom_intro ~src:(Some src) formatter
140141
Location.
141142
{
142143
loc =
@@ -147,12 +148,12 @@ let print_report diagnostics src =
147148
};
148149
(match rest with
149150
| [] -> ()
150-
| _ -> Format.fprintf Format.err_formatter "@.");
151+
| _ -> Format.fprintf formatter "@.");
151152
print rest src
152153
in
153-
Format.fprintf Format.err_formatter "@[<v>";
154+
Format.fprintf formatter "@[<v>";
154155
print (List.rev diagnostics) src;
155-
Format.fprintf Format.err_formatter "@]@."
156+
Format.fprintf formatter "@]@."
156157

157158
let unexpected token context = Unexpected {token; context}
158159

compiler/syntax/src/res_diagnostics.mli

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@ val message : string -> category
2222

2323
val make : start_pos:Lexing.position -> end_pos:Lexing.position -> category -> t
2424

25-
val print_report : t list -> string -> unit
25+
val print_report :
26+
?custom_intro:string option ->
27+
?formatter:Format.formatter ->
28+
t list ->
29+
string ->
30+
unit
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
This is the first docstring with unformatted ReScript code.
3+
4+
```rescript
5+
let badly_formatted=(x,y)=>{
6+
let result=x+y
7+
if result>0{Console.log("positive")}else{Console.log("negative")}
8+
result
9+
}
10+
```
11+
12+
And another code block in the same docstring:
13+
14+
```rescript
15+
type user={name:string,age:int,active:bool}
16+
let createUser=(name,age)=>{name:name,age:age,active:true}
17+
```
18+
*/
19+
let testFunction1 = () => "test1"
20+
21+
module Nested = {
22+
/**
23+
This is a second docstring with different formatting issues.
24+
25+
But if I add another line here it should be fine.
26+
27+
```rescript
28+
module UserService={
29+
let validate=user => user.age>=18 && user.name !== ""
30+
let getName = user=>user.name
31+
}
32+
```
33+
*/
34+
let testFunction2 = () => "test2"
35+
}
36+
37+
/**
38+
Third docstring with array and option types.
39+
40+
```rescript
41+
let processUsers=(users:array<user>)=>{
42+
users->Array.map(user=>{...user,active:false})->Array.filter(u=>u.age>21)
43+
}
44+
45+
type status=|Loading|Success(string)|Error(option<string>)
46+
```
47+
*/
48+
let testFunction3 = () => "test3"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
This is the first docstring with unformatted ReScript code.
3+
4+
```rescript
5+
let badly_formatted=(x,y)=>{
6+
let result=x+y
7+
if result>0{Console.log("positive")}else{Console.log("negative")}
8+
result
9+
}
10+
```
11+
12+
And another code block in the same docstring:
13+
14+
```rescript
15+
type user={name:string,age:int,active:bool}
16+
let createUser=(name,age)=>{name:name,age:age,active:true}
17+
```
18+
*/
19+
let testFunction1: unit => string
20+
21+
module Nested: {
22+
/**
23+
This is a second docstring with different formatting issues.
24+
25+
But if I add another line here it should be fine.
26+
27+
```rescript
28+
module UserService={
29+
let validate=user => user.age>=18 && user.name !== ""
30+
let getName = user=>user.name
31+
}
32+
```
33+
*/
34+
let testFunction2: unit => string
35+
}
36+
37+
/**
38+
Third docstring with array and option types.
39+
40+
```rescript
41+
let processUsers=(users:array<user>)=>{
42+
users->Array.map(user=>{...user,active:false})->Array.filter(u=>u.age>21)
43+
}
44+
45+
type status=|Loading|Success(string)|Error(option<string>)
46+
```
47+
*/
48+
let testFunction3: unit => string
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
Testing JSX and more complex formatting scenarios.
3+
4+
```rescript
5+
let component=()=>{
6+
<div className="container">
7+
<h1>{"Title"->React.string}</h1>
8+
<button onClick={_=>Console.log("clicked")}>{"Click me"->React.string}</button>
9+
</div>
10+
}
11+
```
12+
13+
Testing pattern matching and switch expressions:
14+
15+
```rescript
16+
let handleResult=(result:result<string,string>)=>{
17+
switch result {
18+
| Ok(value)=>Console.log(`Success: ${value}`)
19+
| Error(error)=>Console.error(`Error: ${error}`)
20+
}
21+
}
22+
```
23+
*/
24+
let testJsx = () => "jsx test"
25+
26+
/**
27+
Testing function composition and piping.
28+
29+
```rescript
30+
let processData=(data:array<int>)=>{
31+
data->Array.filter(x=>x>0)->Array.map(x=>x*2)->Array.reduce(0,(acc,x)=>acc+x)
32+
}
33+
34+
let asyncExample=async()=>{
35+
let data=await fetchData()
36+
let processed=await processData(data)
37+
Console.log(processed)
38+
}
39+
```
40+
*/
41+
let testPipes = () => "pipes test"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
This docstring has an error.
3+
4+
```rescript
5+
let name=
6+
let x=12
7+
```
8+
*/
9+
let testJsx = () => "jsx test"
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
This is the first docstring with unformatted ReScript code.
3+
4+
```rescript
5+
let badly_formatted = (x, y) => {
6+
let result = x + y
7+
if result > 0 {
8+
Console.log("positive")
9+
} else {
10+
Console.log("negative")
11+
}
12+
result
13+
}
14+
```
15+
16+
And another code block in the same docstring:
17+
18+
```rescript
19+
type user = {name: string, age: int, active: bool}
20+
let createUser = (name, age) => {name, age, active: true}
21+
```
22+
*/
23+
let testFunction1 = () => "test1"
24+
25+
module Nested = {
26+
/**
27+
This is a second docstring with different formatting issues.
28+
29+
But if I add another line here it should be fine.
30+
31+
```rescript
32+
module UserService = {
33+
let validate = user => user.age >= 18 && user.name !== ""
34+
let getName = user => user.name
35+
}
36+
```
37+
*/
38+
let testFunction2 = () => "test2"
39+
}
40+
41+
/**
42+
Third docstring with array and option types.
43+
44+
```rescript
45+
let processUsers = (users: array<user>) => {
46+
users
47+
->Array.map(user => {...user, active: false})
48+
->Array.filter(u => u.age > 21)
49+
}
50+
51+
type status = Loading | Success(string) | Error(option<string>)
52+
```
53+
*/
54+
let testFunction3 = () => "test3"
55+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
This is the first docstring with unformatted ReScript code.
3+
4+
```rescript
5+
let badly_formatted = (x, y) => {
6+
let result = x + y
7+
if result > 0 {
8+
Console.log("positive")
9+
} else {
10+
Console.log("negative")
11+
}
12+
result
13+
}
14+
```
15+
16+
And another code block in the same docstring:
17+
18+
```rescript
19+
type user = {name: string, age: int, active: bool}
20+
let createUser = (name, age) => {name, age, active: true}
21+
```
22+
*/
23+
let testFunction1: unit => string
24+
25+
module Nested: {
26+
/**
27+
This is a second docstring with different formatting issues.
28+
29+
But if I add another line here it should be fine.
30+
31+
```rescript
32+
module UserService = {
33+
let validate = user => user.age >= 18 && user.name !== ""
34+
let getName = user => user.name
35+
}
36+
```
37+
*/
38+
let testFunction2: unit => string
39+
}
40+
41+
/**
42+
Third docstring with array and option types.
43+
44+
```rescript
45+
let processUsers = (users: array<user>) => {
46+
users
47+
->Array.map(user => {...user, active: false})
48+
->Array.filter(u => u.age > 21)
49+
}
50+
51+
type status = Loading | Success(string) | Error(option<string>)
52+
```
53+
*/
54+
let testFunction3: unit => string
55+

0 commit comments

Comments
 (0)