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
+ }]
+