Skip to content

Commit 4c71dab

Browse files
authored
Complete lowercase JSX labels from the domProps type (#883)
* complete lowercase JSX labels from the domProps type * changelog
1 parent cdd3983 commit 4c71dab

File tree

6 files changed

+89
-27
lines changed

6 files changed

+89
-27
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- Better error recovery when analysis fails. https://github.com/rescript-lang/rescript-vscode/pull/880
1616
- Expand type aliases in hovers. https://github.com/rescript-lang/rescript-vscode/pull/881
1717
- Include fields when completing a braced expr that's an ID, where it the path likely starts with a module. https://github.com/rescript-lang/rescript-vscode/pull/882
18+
- Complete domProps for lowercase JSX components from `ReactDOM.domProps` if possible. https://github.com/rescript-lang/rescript-vscode/pull/883
1819

1920
## 1.32.0
2021

analysis/src/CompletionBackEnd.ml

+65-27
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,24 @@ let getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact ~scope
548548
findAllCompletions ~env ~prefix ~exact ~namesUsed ~completionContext
549549
| None -> [])
550550

551+
let rec digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos ~env
552+
~scope path =
553+
match
554+
path
555+
|> getCompletionsForPath ~debug ~completionContext:Type ~exact:true ~package
556+
~opens ~full ~pos ~env ~scope
557+
with
558+
| {kind = Type {kind = Abstract (Some (p, _))}} :: _ ->
559+
(* This case happens when what we're looking for is a type alias.
560+
This is the case in newer rescript-react versions where
561+
ReactDOM.domProps is an alias for JsxEvent.t. *)
562+
let pathRev = p |> Utils.expandPath in
563+
pathRev |> List.rev
564+
|> digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos ~env
565+
~scope
566+
| {kind = Type {kind = Record fields}} :: _ -> Some fields
567+
| _ -> None
568+
551569
let mkItem ~name ~kind ~detail ~deprecated ~docstring =
552570
let docContent =
553571
(match deprecated with
@@ -571,7 +589,7 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring =
571589
detail;
572590
documentation =
573591
(if docContent = "" then None
574-
else Some {kind = "markdown"; value = docContent});
592+
else Some {kind = "markdown"; value = docContent});
575593
sortText = None;
576594
insertText = None;
577595
insertTextFormat = None;
@@ -1043,25 +1061,16 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10431061
in
10441062
let targetLabel =
10451063
if lowercaseComponent then
1046-
let rec digToTypeForCompletion path =
1047-
match
1048-
path
1049-
|> getCompletionsForPath ~debug ~completionContext:Type ~exact:true
1050-
~package ~opens ~full ~pos ~env ~scope
1051-
with
1052-
| {kind = Type {kind = Abstract (Some (p, _))}} :: _ ->
1053-
(* This case happens when what we're looking for is a type alias.
1054-
This is the case in newer rescript-react versions where
1055-
ReactDOM.domProps is an alias for JsxEvent.t. *)
1056-
let pathRev = p |> Utils.expandPath in
1057-
pathRev |> List.rev |> digToTypeForCompletion
1058-
| {kind = Type {kind = Record fields}} :: _ -> (
1059-
match fields |> List.find_opt (fun f -> f.fname.txt = propName) with
1060-
| None -> None
1061-
| Some f -> Some (f.fname.txt, f.typ, env))
1062-
| _ -> None
1063-
in
1064-
["ReactDOM"; "domProps"] |> digToTypeForCompletion
1064+
match
1065+
["ReactDOM"; "domProps"]
1066+
|> digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos
1067+
~env ~scope
1068+
with
1069+
| None -> None
1070+
| Some fields -> (
1071+
match fields |> List.find_opt (fun f -> f.fname.txt = propName) with
1072+
| None -> None
1073+
| Some f -> Some (f.fname.txt, f.typ, env))
10651074
else
10661075
CompletionJsx.getJsxLabels ~componentPath:pathToComponent
10671076
~findTypeOfValue ~package
@@ -1541,20 +1550,49 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
15411550
contextPath
15421551
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
15431552
~exact:forHover ~scope
1544-
| Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id ->
1553+
| Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> (
15451554
(* Lowercase JSX tag means builtin *)
15461555
let mkLabel (name, typString) =
15471556
Completion.create name ~kind:(Label typString) ~env
15481557
in
15491558
let keyLabels =
15501559
if Utils.startsWith "key" prefix then [mkLabel ("key", "string")] else []
15511560
in
1552-
(CompletionJsx.domLabels
1553-
|> List.filter (fun (name, _t) ->
1554-
Utils.startsWith name prefix
1555-
&& (forHover || not (List.mem name identsSeen)))
1556-
|> List.map mkLabel)
1557-
@ keyLabels
1561+
(* We always try to look up completion from the actual domProps type first.
1562+
This works in JSXv4. For JSXv3, we have a backup hardcoded list of dom
1563+
labels we can use for completion. *)
1564+
let fromDomProps =
1565+
match
1566+
["ReactDOM"; "domProps"]
1567+
|> digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos ~env
1568+
~scope
1569+
with
1570+
| None -> None
1571+
| Some fields ->
1572+
Some
1573+
(fields
1574+
|> List.filter_map (fun (f : field) ->
1575+
if
1576+
Utils.startsWith f.fname.txt prefix
1577+
&& (forHover || not (List.mem f.fname.txt identsSeen))
1578+
then
1579+
Some
1580+
( f.fname.txt,
1581+
Shared.typeToString (Utils.unwrapIfOption f.typ) )
1582+
else None)
1583+
|> List.map mkLabel)
1584+
in
1585+
match fromDomProps with
1586+
| Some domProps -> domProps
1587+
| None ->
1588+
if debug then
1589+
Printf.printf "Could not find ReactDOM.domProps to complete from.\n";
1590+
(CompletionJsx.domLabels
1591+
|> List.filter (fun (name, _t) ->
1592+
Utils.startsWith name prefix
1593+
&& (forHover || not (List.mem name identsSeen)))
1594+
|> List.map mkLabel)
1595+
@ keyLabels)
15581596
| Cjsx (componentPath, prefix, identsSeen) ->
15591597
let labels =
15601598
CompletionJsx.getJsxLabels ~componentPath ~findTypeOfValue ~package

analysis/tests/src/CompletionJsx.res

+3
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@ module CompWithoutJsxPpx = {
4848

4949
// <SomeComponent someProp=>
5050
// ^com
51+
52+
// <h1 hidd
53+
// ^com

analysis/tests/src/expected/Completion.res.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1684,6 +1684,8 @@ Completable: Cjsx([div], name, [name])
16841684
Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder
16851685
Package opens Pervasives.JsxModules.place holder
16861686
Resolved opens 3 pervasives Completion.res Completion.res
1687+
Path ReactDOM.domProps
1688+
Path Pervasives.JsxDOM.domProps
16871689
{"contents": {"kind": "markdown", "value": "```rescript\nstring\n```"}}
16881690

16891691
Hover src/Completion.res 349:17

analysis/tests/src/expected/CompletionJsx.res.txt

+16
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,19 @@ Path SomeComponent.make
492492
"insertTextFormat": 2
493493
}]
494494

495+
Complete src/CompletionJsx.res 51:11
496+
posCursor:[51:11] posNoWhite:[51:10] Found expr:[51:4->51:11]
497+
JSX <h1:[51:4->51:6] hidd[51:7->51:11]=...[51:7->51:11]> _children:None
498+
Completable: Cjsx([h1], hidd, [hidd])
499+
Package opens Pervasives.JsxModules.place holder
500+
Resolved opens 1 pervasives
501+
Path ReactDOM.domProps
502+
Path Pervasives.JsxDOM.domProps
503+
[{
504+
"label": "hidden",
505+
"kind": 4,
506+
"tags": [],
507+
"detail": "bool",
508+
"documentation": null
509+
}]
510+

analysis/tests/src/expected/Div.res.txt

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ JSX <div:[3:4->3:7] dangerous[3:8->3:17]=...[3:8->3:17]> _children:None
88
Completable: Cjsx([div], dangerous, [dangerous])
99
Package opens Pervasives.JsxModules.place holder
1010
Resolved opens 1 pervasives
11+
Path ReactDOM.domProps
12+
Path Pervasives.JsxDOM.domProps
1113
[{
1214
"label": "dangerouslySetInnerHTML",
1315
"kind": 4,

0 commit comments

Comments
 (0)