Skip to content

Commit efd93db

Browse files
aspeddrozth
authored andcommitted
codeaction: add docstring template
1 parent 7163f0c commit efd93db

File tree

4 files changed

+229
-1
lines changed

4 files changed

+229
-1
lines changed

Diff for: analysis/src/Xform.ml

+123
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,103 @@ module AddTypeAnnotation = struct
252252
| _ -> ()))
253253
end
254254

255+
module AddDocTemplate = struct
256+
let mkIterator ~pos ~result =
257+
let signature_item (iterator : Ast_iterator.iterator)
258+
(item : Parsetree.signature_item) =
259+
match item.psig_desc with
260+
| Psig_value value_description as r
261+
when Loc.hasPos ~pos value_description.pval_loc
262+
&& ProcessAttributes.findDocAttribute
263+
value_description.pval_attributes
264+
= None ->
265+
result := Some (r, item.psig_loc)
266+
| Psig_type (_, hd :: _) as r
267+
when Loc.hasPos ~pos hd.ptype_loc
268+
&& ProcessAttributes.findDocAttribute hd.ptype_attributes = None ->
269+
result := Some (r, item.psig_loc)
270+
| Psig_module {pmd_name = {loc}} as r ->
271+
if Loc.start loc = pos then result := Some (r, item.psig_loc)
272+
else Ast_iterator.default_iterator.signature_item iterator item
273+
| _ -> Ast_iterator.default_iterator.signature_item iterator item
274+
in
275+
{Ast_iterator.default_iterator with signature_item}
276+
277+
let createTemplate () =
278+
let docContent = ["\n"; "\n"] in
279+
let expression =
280+
Ast_helper.Exp.constant
281+
(Parsetree.Pconst_string (String.concat "" docContent, None))
282+
in
283+
let structureItemDesc = Parsetree.Pstr_eval (expression, []) in
284+
let structureItem = Ast_helper.Str.mk structureItemDesc in
285+
let attrLoc =
286+
{
287+
Location.none with
288+
loc_start = Lexing.dummy_pos;
289+
loc_end =
290+
{
291+
Lexing.dummy_pos with
292+
pos_lnum = Lexing.dummy_pos.pos_lnum (* force line break *);
293+
};
294+
}
295+
in
296+
(Location.mkloc "res.doc" attrLoc, Parsetree.PStr [structureItem])
297+
298+
let processSigValue (vl_desc : Parsetree.value_description) loc =
299+
let attr = createTemplate () in
300+
let newValueBinding =
301+
{vl_desc with pval_attributes = attr :: vl_desc.pval_attributes}
302+
in
303+
let signature_item_desc = Parsetree.Psig_value newValueBinding in
304+
Ast_helper.Sig.mk ~loc signature_item_desc
305+
306+
let processTypeDecl (typ : Parsetree.type_declaration) =
307+
let attr = createTemplate () in
308+
let newTypeDeclaration =
309+
{typ with ptype_attributes = attr :: typ.ptype_attributes}
310+
in
311+
newTypeDeclaration
312+
313+
let processModDecl (modDecl : Parsetree.module_declaration) loc =
314+
let attr = createTemplate () in
315+
let newModDecl =
316+
{modDecl with pmd_attributes = attr :: modDecl.pmd_attributes}
317+
in
318+
Ast_helper.Sig.mk ~loc (Parsetree.Psig_module newModDecl)
319+
320+
let xform ~path ~pos ~codeActions ~signature ~printSignatureItem =
321+
let result = ref None in
322+
let iterator = mkIterator ~pos ~result in
323+
iterator.signature iterator signature;
324+
match !result with
325+
| Some (signatureItem, loc) -> (
326+
let newSignatureItem =
327+
match signatureItem with
328+
| Psig_value value_desc ->
329+
Some (processSigValue value_desc value_desc.pval_loc) (* Some loc *)
330+
| Psig_type (flag, hd :: tl) ->
331+
let newFirstTypeDecl = processTypeDecl hd in
332+
Some
333+
(Ast_helper.Sig.mk ~loc
334+
(Parsetree.Psig_type (flag, newFirstTypeDecl :: tl)))
335+
| Psig_module modDecl -> Some (processModDecl modDecl loc)
336+
| _ -> None
337+
in
338+
339+
match newSignatureItem with
340+
| Some sig_item ->
341+
let range = rangeOfLoc sig_item.psig_loc in
342+
let newText = printSignatureItem ~range sig_item in
343+
let codeAction =
344+
CodeActions.make ~title:"Add Documentation template"
345+
~kind:RefactorRewrite ~uri:path ~newText ~range
346+
in
347+
codeActions := codeAction :: !codeActions
348+
| None -> ())
349+
| None -> ()
350+
end
351+
255352
let parse ~filename =
256353
let {Res_driver.parsetree = structure; comments} =
257354
Res_driver.parsingEngine.parseImplementation ~forPrinter:false ~filename
@@ -280,6 +377,27 @@ let parse ~filename =
280377
in
281378
(structure, printExpr, printStructureItem)
282379

380+
let parseInterface ~filename =
381+
let {Res_driver.parsetree = structure; comments} =
382+
Res_driver.parsingEngine.parseInterface ~forPrinter:false ~filename
383+
in
384+
let filterComments ~loc comments =
385+
(* Relevant comments in the range of the expression *)
386+
let filter comment =
387+
Loc.hasPos ~pos:(Loc.start (Res_comment.loc comment)) loc
388+
in
389+
comments |> List.filter filter
390+
in
391+
let printSignatureItem ~(range : Protocol.range)
392+
(item : Parsetree.signature_item) =
393+
let signature_item = [item] in
394+
signature_item
395+
|> Res_printer.printInterface ~width:!Res_cli.ResClflags.width
396+
~comments:(comments |> filterComments ~loc:item.psig_loc)
397+
|> Utils.indent range.start.character
398+
in
399+
(structure, printSignatureItem)
400+
283401
let extractCodeActions ~path ~pos ~currentFile ~debug =
284402
match Cmt.loadFullCmtFromPath ~path with
285403
| Some full when Files.classifySourceFile currentFile = Res ->
@@ -291,4 +409,9 @@ let extractCodeActions ~path ~pos ~currentFile ~debug =
291409
IfThenElse.xform ~pos ~codeActions ~printExpr ~path structure;
292410
AddBracesToFn.xform ~pos ~codeActions ~path ~printStructureItem structure;
293411
!codeActions
412+
| _ when Files.classifySourceFile currentFile = Resi ->
413+
let signature, printSignatureItem = parseInterface ~filename:currentFile in
414+
let codeActions = ref [] in
415+
AddDocTemplate.xform ~pos ~codeActions ~path ~signature ~printSignatureItem;
416+
!codeActions
294417
| _ -> []

Diff for: analysis/tests/not_compiled/DocTemplate.resi

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
type a = {a: int}
2+
// ^xfm
3+
4+
type rec t = A | B
5+
// ^xfm
6+
and e = C
7+
@unboxed type name = Name(string)
8+
// ^xfm
9+
let a: int
10+
// ^xfm
11+
let inc: int => int
12+
// ^xfm
13+
module T: {
14+
// ^xfm
15+
let b: int
16+
// ^xfm
17+
}
18+
@module("path")
19+
external dirname: string => string = "dirname"
20+
//^xfm
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
Xform not_compiled/DocTemplate.resi 3:3
2+
can't find module DocTemplate
3+
Hit: Add Documentation template
4+
{"start": {"line": 3, "character": 0}, "end": {"line": 5, "character": 9}}
5+
newText:
6+
<--here
7+
/**
8+
9+
*/
10+
type rec t = A | B
11+
// ^xfm
12+
and e = C
13+
14+
Xform not_compiled/DocTemplate.resi 6:15
15+
can't find module DocTemplate
16+
Hit: Add Documentation template
17+
{"start": {"line": 6, "character": 0}, "end": {"line": 6, "character": 33}}
18+
newText:
19+
<--here
20+
/**
21+
22+
*/
23+
@unboxed
24+
type name = Name(string)
25+
26+
Xform not_compiled/DocTemplate.resi 8:4
27+
can't find module DocTemplate
28+
Hit: Add Documentation template
29+
{"start": {"line": 8, "character": 0}, "end": {"line": 8, "character": 10}}
30+
newText:
31+
<--here
32+
/**
33+
34+
*/
35+
let a: int
36+
37+
Xform not_compiled/DocTemplate.resi 10:4
38+
can't find module DocTemplate
39+
Hit: Add Documentation template
40+
{"start": {"line": 10, "character": 0}, "end": {"line": 10, "character": 19}}
41+
newText:
42+
<--here
43+
/**
44+
45+
*/
46+
let inc: int => int
47+
48+
Xform not_compiled/DocTemplate.resi 12:7
49+
can't find module DocTemplate
50+
Hit: Add Documentation template
51+
{"start": {"line": 12, "character": 0}, "end": {"line": 16, "character": 1}}
52+
newText:
53+
<--here
54+
/**
55+
56+
*/
57+
module T: {
58+
// ^xfm
59+
let b: int
60+
// ^xfm
61+
}
62+
63+
Xform not_compiled/DocTemplate.resi 14:6
64+
can't find module DocTemplate
65+
Hit: Add Documentation template
66+
{"start": {"line": 14, "character": 2}, "end": {"line": 14, "character": 12}}
67+
newText:
68+
<--here
69+
/**
70+
71+
*/
72+
let b: int
73+
74+
Xform not_compiled/DocTemplate.resi 18:2
75+
can't find module DocTemplate
76+
Hit: Add Documentation template
77+
{"start": {"line": 17, "character": 0}, "end": {"line": 18, "character": 46}}
78+
newText:
79+
<--here
80+
/**
81+
82+
*/
83+
@module("path")
84+
external dirname: string => string = "dirname"
85+

Diff for: analysis/tests/test.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ for file in src/*.{res,resi}; do
77
fi
88
done
99

10-
for file in not_compiled/*.res; do
10+
for file in not_compiled/*.{res,resi}; do
1111
output="$(dirname $file)/expected/$(basename $file).txt"
1212
../rescript-editor-analysis.exe test $file &> $output
1313
# CI. We use LF, and the CI OCaml fork prints CRLF. Convert.

0 commit comments

Comments
 (0)