Skip to content

Commit

Permalink
support .res
Browse files Browse the repository at this point in the history
  • Loading branch information
aspeddro authored and zth committed Apr 27, 2023
1 parent efd93db commit 794ae97
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 84 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

- Greatly extend completion abilities for unsaved code. WARNING: Might be a bit unstable initially. Report any issues you see. https://github.com/rescript-lang/rescript-vscode/pull/712
- Provide hovers for more unsaved code via the new completion features. https://github.com/rescript-lang/rescript-vscode/pull/749
- Docstring template Code Action. https://github.com/rescript-lang/rescript-vscode/pull/764

## 1.14.0

Expand Down
251 changes: 174 additions & 77 deletions analysis/src/Xform.ml
Original file line number Diff line number Diff line change
Expand Up @@ -253,27 +253,6 @@ module AddTypeAnnotation = struct
end

module AddDocTemplate = struct
let mkIterator ~pos ~result =
let signature_item (iterator : Ast_iterator.iterator)
(item : Parsetree.signature_item) =
match item.psig_desc with
| Psig_value value_description as r
when Loc.hasPos ~pos value_description.pval_loc
&& ProcessAttributes.findDocAttribute
value_description.pval_attributes
= None ->
result := Some (r, item.psig_loc)
| Psig_type (_, hd :: _) as r
when Loc.hasPos ~pos hd.ptype_loc
&& ProcessAttributes.findDocAttribute hd.ptype_attributes = None ->
result := Some (r, item.psig_loc)
| Psig_module {pmd_name = {loc}} as r ->
if Loc.start loc = pos then result := Some (r, item.psig_loc)
else Ast_iterator.default_iterator.signature_item iterator item
| _ -> Ast_iterator.default_iterator.signature_item iterator item
in
{Ast_iterator.default_iterator with signature_item}

let createTemplate () =
let docContent = ["\n"; "\n"] in
let expression =
Expand All @@ -295,61 +274,169 @@ module AddDocTemplate = struct
in
(Location.mkloc "res.doc" attrLoc, Parsetree.PStr [structureItem])

let processSigValue (vl_desc : Parsetree.value_description) loc =
let attr = createTemplate () in
let newValueBinding =
{vl_desc with pval_attributes = attr :: vl_desc.pval_attributes}
in
let signature_item_desc = Parsetree.Psig_value newValueBinding in
Ast_helper.Sig.mk ~loc signature_item_desc
module Interface = struct
let mkIterator ~pos ~result =
let signature_item (iterator : Ast_iterator.iterator)
(item : Parsetree.signature_item) =
match item.psig_desc with
| Psig_value value_description as r
when Loc.hasPos ~pos value_description.pval_loc
&& ProcessAttributes.findDocAttribute
value_description.pval_attributes
= None ->
result := Some (r, item.psig_loc)
| Psig_type (_, hd :: _) as r
when Loc.hasPos ~pos hd.ptype_loc
&& ProcessAttributes.findDocAttribute hd.ptype_attributes = None
->
result := Some (r, item.psig_loc)
| Psig_module {pmd_name = {loc}} as r ->
if Loc.start loc = pos then result := Some (r, item.psig_loc)
else Ast_iterator.default_iterator.signature_item iterator item
| _ -> Ast_iterator.default_iterator.signature_item iterator item
in
{Ast_iterator.default_iterator with signature_item}

let processTypeDecl (typ : Parsetree.type_declaration) =
let attr = createTemplate () in
let newTypeDeclaration =
{typ with ptype_attributes = attr :: typ.ptype_attributes}
in
newTypeDeclaration
let processSigValue (valueDesc : Parsetree.value_description) loc =
let attr = createTemplate () in
let newValueBinding =
{valueDesc with pval_attributes = attr :: valueDesc.pval_attributes}
in
let signature_item_desc = Parsetree.Psig_value newValueBinding in
Ast_helper.Sig.mk ~loc signature_item_desc

let processModDecl (modDecl : Parsetree.module_declaration) loc =
let attr = createTemplate () in
let newModDecl =
{modDecl with pmd_attributes = attr :: modDecl.pmd_attributes}
in
Ast_helper.Sig.mk ~loc (Parsetree.Psig_module newModDecl)
let processTypeDecl (typ : Parsetree.type_declaration) =
let attr = createTemplate () in
let newTypeDeclaration =
{typ with ptype_attributes = attr :: typ.ptype_attributes}
in
newTypeDeclaration

let xform ~path ~pos ~codeActions ~signature ~printSignatureItem =
let result = ref None in
let iterator = mkIterator ~pos ~result in
iterator.signature iterator signature;
match !result with
| Some (signatureItem, loc) -> (
let newSignatureItem =
match signatureItem with
| Psig_value value_desc ->
Some (processSigValue value_desc value_desc.pval_loc) (* Some loc *)
| Psig_type (flag, hd :: tl) ->
let newFirstTypeDecl = processTypeDecl hd in
Some
(Ast_helper.Sig.mk ~loc
(Parsetree.Psig_type (flag, newFirstTypeDecl :: tl)))
| Psig_module modDecl -> Some (processModDecl modDecl loc)
| _ -> None
let processModDecl (modDecl : Parsetree.module_declaration) loc =
let attr = createTemplate () in
let newModDecl =
{modDecl with pmd_attributes = attr :: modDecl.pmd_attributes}
in
Ast_helper.Sig.mk ~loc (Parsetree.Psig_module newModDecl)

match newSignatureItem with
| Some sig_item ->
let range = rangeOfLoc sig_item.psig_loc in
let newText = printSignatureItem ~range sig_item in
let codeAction =
CodeActions.make ~title:"Add Documentation template"
~kind:RefactorRewrite ~uri:path ~newText ~range
let xform ~path ~pos ~codeActions ~signature ~printSignatureItem =
let result = ref None in
let iterator = mkIterator ~pos ~result in
iterator.signature iterator signature;
match !result with
| Some (signatureItem, loc) -> (
let newSignatureItem =
match signatureItem with
| Psig_value value_desc ->
Some (processSigValue value_desc value_desc.pval_loc) (* Some loc *)
| Psig_type (flag, hd :: tl) ->
let newFirstTypeDecl = processTypeDecl hd in
Some
(Ast_helper.Sig.mk ~loc
(Parsetree.Psig_type (flag, newFirstTypeDecl :: tl)))
| Psig_module modDecl -> Some (processModDecl modDecl loc)
| _ -> None
in
codeActions := codeAction :: !codeActions
| None -> ())
| None -> ()

match newSignatureItem with
| Some signatureItem ->
let range = rangeOfLoc signatureItem.psig_loc in
let newText = printSignatureItem ~range signatureItem in
let codeAction =
CodeActions.make ~title:"Add Documentation template"
~kind:RefactorRewrite ~uri:path ~newText ~range
in
codeActions := codeAction :: !codeActions
| None -> ())
| None -> ()
end

module Implementation = struct
let mkIterator ~pos ~result =
let structure_item (iterator : Ast_iterator.iterator)
(si : Parsetree.structure_item) =
match si.pstr_desc with
| Pstr_value (_, {pvb_pat = {ppat_loc}; pvb_attributes} :: _) as r
when Loc.hasPos ~pos ppat_loc
&& ProcessAttributes.findDocAttribute pvb_attributes = None ->
result := Some (r, si.pstr_loc)
| Pstr_primitive value_description as r
when Loc.hasPos ~pos value_description.pval_loc
&& ProcessAttributes.findDocAttribute
value_description.pval_attributes
= None ->
result := Some (r, si.pstr_loc)
| Pstr_module {pmb_name = {loc}} as r ->
if Loc.start loc = pos then result := Some (r, si.pstr_loc)
else Ast_iterator.default_iterator.structure_item iterator si
| Pstr_type (_, hd :: _) as r
when Loc.hasPos ~pos hd.ptype_loc
&& ProcessAttributes.findDocAttribute hd.ptype_attributes = None
->
result := Some (r, si.pstr_loc)
| _ -> Ast_iterator.default_iterator.structure_item iterator si
in
{Ast_iterator.default_iterator with structure_item}

let processValueBinding (valueBinding : Parsetree.value_binding) =
let attr = createTemplate () in
let newValueBinding =
{valueBinding with pvb_attributes = attr :: valueBinding.pvb_attributes}
in
newValueBinding

let processPrimitive (valueDesc : Parsetree.value_description) loc =
let attr = createTemplate () in
let newValueDesc =
{valueDesc with pval_attributes = attr :: valueDesc.pval_attributes}
in
Ast_helper.Str.primitive ~loc newValueDesc

let processModuleBinding (modBind : Parsetree.module_binding) loc =
let attr = createTemplate () in
let newModBinding =
{modBind with pmb_attributes = attr :: modBind.pmb_attributes}
in
Ast_helper.Str.module_ ~loc newModBinding

let xform ~pos ~codeActions ~path ~printStructureItem ~structure =
let result = ref None in
let iterator = mkIterator ~pos ~result in
iterator.structure iterator structure;
match !result with
| None -> ()
| Some (structureItem, loc) -> (
let newStructureItem =
match structureItem with
| Pstr_value (flag, hd :: tl) ->
let newValueBinding = processValueBinding hd in
Some
(Ast_helper.Str.mk ~loc
(Parsetree.Pstr_value (flag, newValueBinding :: tl)))
| Pstr_primitive valueDesc -> Some (processPrimitive valueDesc loc)
| Pstr_module modBind -> Some (processModuleBinding modBind loc)
| Pstr_type (flag, hd :: tl) ->
let newFirstTypeDecl = Interface.processTypeDecl hd in
Some
(Ast_helper.Str.mk ~loc
(Parsetree.Pstr_type (flag, newFirstTypeDecl :: tl)))
| _ -> None
in

match newStructureItem with
| Some structureItem ->
let range = rangeOfLoc structureItem.pstr_loc in
let newText = printStructureItem ~range structureItem in
let codeAction =
CodeActions.make ~title:"Add Documentation template"
~kind:RefactorRewrite ~uri:path ~newText ~range
in
codeActions := codeAction :: !codeActions
| None -> ())
end
end

let parse ~filename =
let parseImplementation ~filename =
let {Res_driver.parsetree = structure; comments} =
Res_driver.parsingEngine.parseImplementation ~forPrinter:false ~filename
in
Expand Down Expand Up @@ -399,19 +486,29 @@ let parseInterface ~filename =
(structure, printSignatureItem)

let extractCodeActions ~path ~pos ~currentFile ~debug =
match Cmt.loadFullCmtFromPath ~path with
| Some full when Files.classifySourceFile currentFile = Res ->
let codeActions = ref [] in
match Files.classifySourceFile currentFile with
| Res ->
let structure, printExpr, printStructureItem =
parse ~filename:currentFile
parseImplementation ~filename:currentFile
in
let codeActions = ref [] in
AddTypeAnnotation.xform ~path ~pos ~full ~structure ~codeActions ~debug;
IfThenElse.xform ~pos ~codeActions ~printExpr ~path structure;
AddBracesToFn.xform ~pos ~codeActions ~path ~printStructureItem structure;
AddDocTemplate.Implementation.xform ~pos ~codeActions ~path
~printStructureItem ~structure;

(* This Code Action needs type info *)
let () =
match Cmt.loadFullCmtFromPath ~path with
| Some full ->
AddTypeAnnotation.xform ~path ~pos ~full ~structure ~codeActions ~debug
| None -> ()
in

!codeActions
| _ when Files.classifySourceFile currentFile = Resi ->
| Resi ->
let signature, printSignatureItem = parseInterface ~filename:currentFile in
let codeActions = ref [] in
AddDocTemplate.xform ~pos ~codeActions ~path ~signature ~printSignatureItem;
AddDocTemplate.Interface.xform ~pos ~codeActions ~path ~signature
~printSignatureItem;
!codeActions
| _ -> []
| Other -> []
20 changes: 20 additions & 0 deletions analysis/tests/not_compiled/DocTemplate.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type a = {a: int}
// ^xfm

type rec t = A | B
// ^xfm
and e = C
@unboxed type name = Name(string)
// ^xfm
let a = 1
// ^xfm
let inc = x => x + 1
// ^xfm
module T = {
// ^xfm
let b = 1
// ^xfm
}
@module("path")
external dirname: string => string = "dirname"
//^xfm
85 changes: 85 additions & 0 deletions analysis/tests/not_compiled/expected/DocTemplate.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
Xform not_compiled/DocTemplate.res 3:3
can't find module DocTemplate
Hit: Add Documentation template
{"start": {"line": 3, "character": 0}, "end": {"line": 5, "character": 9}}
newText:
<--here
/**

*/
type rec t = A | B
// ^xfm
and e = C

Xform not_compiled/DocTemplate.res 6:15
can't find module DocTemplate
Hit: Add Documentation template
{"start": {"line": 6, "character": 0}, "end": {"line": 6, "character": 33}}
newText:
<--here
/**

*/
@unboxed
type name = Name(string)

Xform not_compiled/DocTemplate.res 8:4
can't find module DocTemplate
Hit: Add Documentation template
{"start": {"line": 8, "character": 0}, "end": {"line": 8, "character": 9}}
newText:
<--here
/**

*/
let a = 1

Xform not_compiled/DocTemplate.res 10:4
can't find module DocTemplate
Hit: Add Documentation template
{"start": {"line": 10, "character": 0}, "end": {"line": 10, "character": 20}}
newText:
<--here
/**

*/
let inc = x => x + 1

Xform not_compiled/DocTemplate.res 12:7
can't find module DocTemplate
Hit: Add Documentation template
{"start": {"line": 12, "character": 0}, "end": {"line": 16, "character": 1}}
newText:
<--here
/**

*/
module T = {
// ^xfm
let b = 1
// ^xfm
}

Xform not_compiled/DocTemplate.res 14:6
can't find module DocTemplate
Hit: Add Documentation template
{"start": {"line": 14, "character": 2}, "end": {"line": 14, "character": 11}}
newText:
<--here
/**

*/
let b = 1

Xform not_compiled/DocTemplate.res 18:2
can't find module DocTemplate
Hit: Add Documentation template
{"start": {"line": 17, "character": 0}, "end": {"line": 18, "character": 46}}
newText:
<--here
/**

*/
@module("path")
external dirname: string => string = "dirname"

Loading

0 comments on commit 794ae97

Please sign in to comment.