diff --git a/CHANGELOG.md b/CHANGELOG.md index 376a81c30..d050fedec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ ## master +#### :rocket: New Feature + +- Add support for prop completion for JSX V4 https://github.com/rescript-lang/rescript-vscode/pull/579 + ## v1.6.0 #### :rocket: New Feature diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index cbebd2a00..51dab59be 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1427,16 +1427,39 @@ let processCompletable ~debug ~package ~scope ~env ~pos ~forHover | Cjsx (componentPath, prefix, identsSeen) -> let labels = match componentPath @ ["make"] |> findTypeOfValue with - | Some (typ, _env) -> - let rec getFields (texp : Types.type_expr) = + | Some (typ, make_env) -> + let rec getFieldsV3 (texp : Types.type_expr) = match texp.desc with | Tfield (name, _, t1, t2) -> - let fields = t2 |> getFields in + let fields = t2 |> getFieldsV3 in if name = "children" then fields else (name, t1) :: fields - | Tlink te | Tsubst te | Tpoly (te, []) -> te |> getFields + | Tlink te | Tsubst te | Tpoly (te, []) -> te |> getFieldsV3 | Tvar None -> [] | _ -> [] in + let getFieldsV4 ~path ~typeArgs = + match References.digConstructor ~env:make_env ~package path with + | Some + ( _env, + { + item = + { + decl = + { + type_kind = Type_record (labelDecls, _repr); + type_params = typeParams; + }; + }; + } ) -> + labelDecls + |> List.map (fun (ld : Types.label_declaration) -> + let name = Ident.name ld.ld_id in + let t = + ld.ld_type |> instantiateType ~typeParams ~typeArgs + in + (name, t)) + | _ -> [] + in let rec getLabels (t : Types.type_expr) = match t.desc with | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> getLabels t1 @@ -1449,9 +1472,14 @@ let processCompletable ~debug ~package ~scope ~env ~pos ~forHover }, _, _ ) -> - getFields tObj + (* JSX V3 *) + getFieldsV3 tObj + | Tarrow (Nolabel, {desc = Tconstr (path, typeArgs, _)}, _, _) + when Path.last path = "props" -> + (* JSX V4 *) + getFieldsV4 ~path ~typeArgs | Tconstr - ( path, + ( clPath, [ { desc = @@ -1461,8 +1489,14 @@ let processCompletable ~debug ~package ~scope ~env ~pos ~forHover _; ], _ ) - when Path.name path = "React.componentLike" -> - getFields tObj + when Path.name clPath = "React.componentLike" -> + (* JSX V3 external or interface *) + getFieldsV3 tObj + | Tconstr (clPath, [{desc = Tconstr (path, typeArgs, _)}; _], _) + when Path.name clPath = "React.componentLike" + && Path.last path = "props" -> + (* JSX V4 external or interface *) + getFieldsV4 ~path ~typeArgs | _ -> [] in typ |> getLabels @@ -1480,6 +1514,7 @@ let processCompletable ~debug ~package ~scope ~env ~pos ~forHover (labels |> List.filter (fun (name, _t) -> Utils.startsWith name prefix + && name <> "key" && (forHover || not (List.mem name identsSeen))) |> List.map mkLabel) @ keyLabels diff --git a/analysis/tests/src/JsxV4.res b/analysis/tests/src/JsxV4.res new file mode 100644 index 000000000..52e01c38b --- /dev/null +++ b/analysis/tests/src/JsxV4.res @@ -0,0 +1,12 @@ +@@jsxConfig({version: 4}) + +module M4 = { + @react.component + let make = (~first, ~fun="", ~second="") => React.string(first ++ fun ++ second) +} + +let _ = +// ^def + +// {x} {y} +heuristic for: [React.null, makeProps, make, createElementVariadic], give the loc of `make` +n1:() n2:makeProps n3:make n4:createElement +{"uri": "JsxV4.res", "range": {"start": {"line": 4, "character": 6}, "end": {"line": 4, "character": 10}}} + +Complete src/JsxV4.res 10:20 +posCursor:[10:20] posNoWhite:[10:19] Found expr:[10:4->10:20] +JSX 10:6] first[10:7->10:12]=...[10:13->10:18] f[10:19->10:20]=...[10:19->10:20]> _children:None +Completable: Cjsx([M4], f, [first, f]) +[{ + "label": "fun", + "kind": 4, + "tags": [], + "detail": "option", + "documentation": null + }] +