Skip to content

Commit 5d9c682

Browse files
authored
Add completion for React primitives (#7292)
* During call * Simplify example * Add completion for integers inside braces. * Add changelog entry * Remove completion of integer in braces * Fix typo
1 parent 9da9b19 commit 5d9c682

File tree

8 files changed

+247
-34
lines changed

8 files changed

+247
-34
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- Fix completion for application with tagged template. https://github.com/rescript-lang/rescript/pull/7278
2525
- Fix error message for arity in the presence of optional arguments. https://github.com/rescript-lang/rescript/pull/7284
2626
- Fix issue in functors with more than one argument (which are curried): emit nested function always. https://github.com/rescript-lang/rescript/pull/7273
27+
- Fix dot completion issue with React primitives. https://github.com/rescript-lang/rescript/pull/7292
2728

2829
#### :house: Internal
2930

analysis/src/CompletionBackEnd.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
979979
path @ [fieldName]
980980
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact
981981
~completionContext:Field ~env ~scope
982-
| CPField {contextPath = cp; fieldName; posOfDot; exprLoc} -> (
982+
| CPField {contextPath = cp; fieldName; posOfDot; exprLoc; inJsx} -> (
983983
if Debug.verbose () then print_endline "[dot_completion]--> Triggered";
984984
let completionsFromCtxPath =
985985
cp
@@ -1013,7 +1013,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10131013
CPApply (cp, [Asttypes.Noloc.Nolabel])
10141014
| _ -> cp);
10151015
id = fieldName;
1016-
inJsx = false;
1016+
inJsx;
10171017
lhsLoc = exprLoc;
10181018
}
10191019
in

analysis/src/CompletionFrontEnd.ml

+48-31
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
206206
})
207207
| _ -> loop args
208208

209-
let rec exprToContextPathInner (e : Parsetree.expression) =
209+
let rec exprToContextPathInner ~(inJsxContext : bool) (e : Parsetree.expression)
210+
=
210211
match e.pexp_desc with
211212
| Pexp_constant (Pconst_string _) -> Some Completable.CPString
212213
| Pexp_constant (Pconst_integer _) -> Some CPInt
@@ -217,13 +218,13 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
217218
(CPArray
218219
(match exprs with
219220
| [] -> None
220-
| exp :: _ -> exprToContextPath exp))
221+
| exp :: _ -> exprToContextPath ~inJsxContext exp))
221222
| Pexp_ident {txt = Lident "->"} -> None
222223
| Pexp_ident {txt; loc} ->
223224
Some
224225
(CPId {path = Utils.flattenLongIdent txt; completionContext = Value; loc})
225226
| Pexp_field (e1, {txt = Lident name}) -> (
226-
match exprToContextPath e1 with
227+
match exprToContextPath ~inJsxContext e1 with
227228
| Some contextPath ->
228229
Some
229230
(CPField
@@ -232,6 +233,7 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
232233
fieldName = name;
233234
posOfDot = None;
234235
exprLoc = e1.pexp_loc;
236+
inJsx = inJsxContext;
235237
})
236238
| _ -> None)
237239
| Pexp_field (e1, {loc; txt = Ldot (lid, name)}) ->
@@ -249,9 +251,10 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
249251
fieldName = name;
250252
posOfDot = None;
251253
exprLoc = e1.pexp_loc;
254+
inJsx = inJsxContext;
252255
})
253256
| Pexp_send (e1, {txt}) -> (
254-
match exprToContextPath e1 with
257+
match exprToContextPath ~inJsxContext e1 with
255258
| None -> None
256259
| Some contexPath -> Some (CPObj (contexPath, txt)))
257260
| Pexp_apply
@@ -266,7 +269,7 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
266269
[(_, lhs); (_, {pexp_desc = Pexp_apply {funct = d; args; partial}})];
267270
} ->
268271
(* Transform away pipe with apply call *)
269-
exprToContextPath
272+
exprToContextPath ~inJsxContext
270273
{
271274
pexp_desc =
272275
Pexp_apply {funct = d; args = (Nolabel, lhs) :: args; partial};
@@ -283,7 +286,7 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
283286
partial;
284287
} ->
285288
(* Transform away pipe with identifier *)
286-
exprToContextPath
289+
exprToContextPath ~inJsxContext
287290
{
288291
pexp_desc =
289292
Pexp_apply
@@ -296,29 +299,31 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
296299
pexp_attributes;
297300
}
298301
| Pexp_apply {funct = e1; args} -> (
299-
match exprToContextPath e1 with
302+
match exprToContextPath ~inJsxContext e1 with
300303
| None -> None
301304
| Some contexPath ->
302305
Some
303306
(CPApply (contexPath, args |> List.map fst |> List.map Asttypes.to_noloc))
304307
)
305308
| Pexp_tuple exprs ->
306-
let exprsAsContextPaths = exprs |> List.filter_map exprToContextPath in
309+
let exprsAsContextPaths =
310+
exprs |> List.filter_map (exprToContextPath ~inJsxContext)
311+
in
307312
if List.length exprs = List.length exprsAsContextPaths then
308313
Some (CTuple exprsAsContextPaths)
309314
else None
310315
| _ -> None
311316

312-
and exprToContextPath (e : Parsetree.expression) =
317+
and exprToContextPath ~(inJsxContext : bool) (e : Parsetree.expression) =
313318
match
314319
( Res_parsetree_viewer.has_await_attribute e.pexp_attributes,
315-
exprToContextPathInner e )
320+
exprToContextPathInner ~inJsxContext e )
316321
with
317322
| true, Some ctxPath -> Some (CPAwait ctxPath)
318323
| false, Some ctxPath -> Some ctxPath
319324
| _, None -> None
320325

321-
let completePipeChain (exp : Parsetree.expression) =
326+
let completePipeChain ~(inJsxContext : bool) (exp : Parsetree.expression) =
322327
(* Complete the end of pipe chains by reconstructing the pipe chain as a single pipe,
323328
so it can be completed.
324329
Example:
@@ -334,15 +339,17 @@ let completePipeChain (exp : Parsetree.expression) =
334339
funct = {pexp_desc = Pexp_ident {txt = Lident "->"}};
335340
args = [_; (_, {pexp_desc = Pexp_apply {funct = d}})];
336341
} ->
337-
exprToContextPath exp |> Option.map (fun ctxPath -> (ctxPath, d.pexp_loc))
342+
exprToContextPath ~inJsxContext exp
343+
|> Option.map (fun ctxPath -> (ctxPath, d.pexp_loc))
338344
(* When the left side of the pipe we're completing is an identifier application.
339345
Example: someArray->filterAllTheGoodStuff-> *)
340346
| Pexp_apply
341347
{
342348
funct = {pexp_desc = Pexp_ident {txt = Lident "->"}};
343349
args = [_; (_, {pexp_desc = Pexp_ident _; pexp_loc})];
344350
} ->
345-
exprToContextPath exp |> Option.map (fun ctxPath -> (ctxPath, pexp_loc))
351+
exprToContextPath ~inJsxContext exp
352+
|> Option.map (fun ctxPath -> (ctxPath, pexp_loc))
346353
| _ -> None
347354

348355
let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
@@ -429,6 +436,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
429436
(Completable.toString x);
430437
result := Some (x, !scope)
431438
in
439+
let inJsxContext = ref false in
432440
let setResult x = setResultOpt (Some x) in
433441
let scopeValueDescription (vd : Parsetree.value_description) =
434442
scope :=
@@ -563,9 +571,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
563571
(* Pipe chains get special treatment here, because when assigning values
564572
we want the return of the entire pipe chain as a function call, rather
565573
than as a pipe completion call. *)
566-
match completePipeChain vb.pvb_expr with
574+
match completePipeChain ~inJsxContext:!inJsxContext vb.pvb_expr with
567575
| Some (ctxPath, _) -> Some ctxPath
568-
| None -> exprToContextPath vb.pvb_expr
576+
| None -> exprToContextPath ~inJsxContext:!inJsxContext vb.pvb_expr
569577
in
570578
scopePattern ?contextPath vb.pvb_pat
571579
in
@@ -597,7 +605,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
597605
scope :=
598606
!scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc
599607
in
600-
let inJsxContext = ref false in
608+
601609
(* Identifies expressions where we can do typed pattern or expr completion. *)
602610
let typedCompletionExpr (exp : Parsetree.expression) =
603611
let debugTypedCompletionExpr = false in
@@ -614,7 +622,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
614622
print_endline "[typedCompletionExpr] No cases - has cursor";
615623
(* We can do exhaustive switch completion if this is an ident we can
616624
complete from. *)
617-
match exprToContextPath expr with
625+
match exprToContextPath ~inJsxContext:!inJsxContext expr with
618626
| None -> ()
619627
| Some contextPath ->
620628
setResult (CexhaustiveSwitch {contextPath; exprLoc = exp.pexp_loc}))
@@ -644,7 +652,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
644652
};
645653
] ) -> (
646654
(* A single case that's a pattern hole typically means `switch x { | }`. Complete as the pattern itself with nothing nested. *)
647-
match exprToContextPath exp with
655+
match exprToContextPath ~inJsxContext:!inJsxContext exp with
648656
| None -> ()
649657
| Some ctxPath ->
650658
setResult
@@ -661,7 +669,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
661669
print_endline "[typedCompletionExpr] Has cases";
662670
(* If there's more than one case, or the case isn't a pattern hole, figure out if we're completing another
663671
broken parser case (`switch x { | true => () | <com> }` for example). *)
664-
match exp |> exprToContextPath with
672+
match exp |> exprToContextPath ~inJsxContext:!inJsxContext with
665673
| None ->
666674
if Debug.verbose () && debugTypedCompletionExpr then
667675
print_endline "[typedCompletionExpr] Has cases - no ctx path"
@@ -802,7 +810,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
802810
( pvb_pat
803811
|> CompletionPatterns.traversePattern ~patternPath:[] ~locHasCursor
804812
~firstCharBeforeCursorNoWhite ~posBeforeCursor,
805-
exprToContextPath pvb_expr )
813+
exprToContextPath ~inJsxContext:!inJsxContext pvb_expr )
806814
with
807815
| Some (prefix, nested), Some ctxPath ->
808816
setResult
@@ -1059,14 +1067,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
10591067
in
10601068
(match findThisExprLoc with
10611069
| Some loc when expr.pexp_loc = loc -> (
1062-
match exprToContextPath expr with
1070+
match exprToContextPath ~inJsxContext:!inJsxContext expr with
10631071
| None -> ()
10641072
| Some ctxPath -> setResult (Cpath ctxPath))
10651073
| _ -> ());
10661074
let setPipeResult ~(lhs : Parsetree.expression) ~id =
1067-
match completePipeChain lhs with
1075+
match completePipeChain ~inJsxContext:!inJsxContext lhs with
10681076
| None -> (
1069-
match exprToContextPath lhs with
1077+
match exprToContextPath ~inJsxContext:!inJsxContext lhs with
10701078
| Some pipe ->
10711079
setResult
10721080
(Cpath
@@ -1101,7 +1109,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11011109
&& Option.is_none findThisExprLoc ->
11021110
if Debug.verbose () then
11031111
print_endline "[completionFrontend] Checking each case";
1104-
let ctxPath = exprToContextPath expr in
1112+
let ctxPath = exprToContextPath ~inJsxContext:!inJsxContext expr in
11051113
let oldCtxPath = !currentCtxPath in
11061114
cases
11071115
|> List.iter (fun (case : Parsetree.case) ->
@@ -1144,7 +1152,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11441152
];
11451153
}
11461154
when Res_parsetree_viewer.is_tagged_template_literal innerExpr ->
1147-
exprToContextPath innerExpr
1155+
exprToContextPath ~inJsxContext:!inJsxContext innerExpr
11481156
|> Option.iter (fun cpath ->
11491157
setResult
11501158
(Cpath
@@ -1154,6 +1162,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11541162
fieldName = "";
11551163
posOfDot;
11561164
exprLoc = expr.pexp_loc;
1165+
inJsx = !inJsxContext;
11571166
}));
11581167
setFound ())
11591168
(*
@@ -1174,7 +1183,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11741183
}
11751184
when Res_parsetree_viewer.is_tagged_template_literal innerExpr
11761185
&& expr.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor ->
1177-
exprToContextPath innerExpr
1186+
exprToContextPath ~inJsxContext:!inJsxContext innerExpr
11781187
|> Option.iter (fun cpath ->
11791188
setResult
11801189
(Cpath
@@ -1184,6 +1193,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11841193
fieldName;
11851194
posOfDot;
11861195
exprLoc = expr.pexp_loc;
1196+
inJsx = !inJsxContext;
11871197
}));
11881198
setFound ())
11891199
| _ -> (
@@ -1262,7 +1272,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
12621272
if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then
12631273
match fieldName.txt with
12641274
| Lident name -> (
1265-
match exprToContextPath e with
1275+
match exprToContextPath ~inJsxContext:!inJsxContext e with
12661276
| Some contextPath ->
12671277
let contextPath =
12681278
Completable.CPField
@@ -1271,6 +1281,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
12711281
fieldName = name;
12721282
posOfDot;
12731283
exprLoc = e.pexp_loc;
1284+
inJsx = !inJsxContext;
12741285
}
12751286
in
12761287
setResult (Cpath contextPath)
@@ -1294,12 +1305,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
12941305
else name);
12951306
posOfDot;
12961307
exprLoc = e.pexp_loc;
1308+
inJsx = !inJsxContext;
12971309
}
12981310
in
12991311
setResult (Cpath contextPath)
13001312
| Lapply _ -> ()
13011313
else if Loc.end_ e.pexp_loc = posBeforeCursor then
1302-
match exprToContextPath e with
1314+
match exprToContextPath ~inJsxContext:!inJsxContext e with
13031315
| Some contextPath ->
13041316
setResult
13051317
(Cpath
@@ -1309,6 +1321,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13091321
fieldName = "";
13101322
posOfDot;
13111323
exprLoc = e.pexp_loc;
1324+
inJsx = !inJsxContext;
13121325
}))
13131326
| None -> ())
13141327
| Pexp_apply {funct = {pexp_desc = Pexp_ident compName}; args}
@@ -1386,7 +1399,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13861399
if Debug.verbose () then
13871400
print_endline "[expr_iter] Complete fn arguments (piped)";
13881401
let args = extractExpApplyArgs ~args in
1389-
let funCtxPath = exprToContextPath funExpr in
1402+
let funCtxPath =
1403+
exprToContextPath ~inJsxContext:!inJsxContext funExpr
1404+
in
13901405
let argCompletable =
13911406
match funCtxPath with
13921407
| Some contextPath ->
@@ -1437,7 +1452,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
14371452
(Loc.toString exp.pexp_loc))
14381453
|> String.concat ", ");
14391454

1440-
let funCtxPath = exprToContextPath funExpr in
1455+
let funCtxPath =
1456+
exprToContextPath ~inJsxContext:!inJsxContext funExpr
1457+
in
14411458
let argCompletable =
14421459
match funCtxPath with
14431460
| Some contextPath ->
@@ -1481,7 +1498,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
14811498
labelRange |> Range.hasPos ~pos:posBeforeCursor
14821499
|| (label = "" && posCursor = fst labelRange)
14831500
then
1484-
match exprToContextPath lhs with
1501+
match exprToContextPath ~inJsxContext:!inJsxContext lhs with
14851502
| Some contextPath -> setResult (Cpath (CPObj (contextPath, label)))
14861503
| None -> ())
14871504
| Pexp_fun

analysis/src/Packages.ml

+1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ let newBsPackage ~rootPath =
156156
| ["RescriptCore"] -> true
157157
| _ -> false)
158158
|> Option.is_some
159+
|| fst rescriptVersion >= 12
159160
then
160161
{
161162
arrayModulePath = ["Array"];

analysis/src/SharedTypes.ml

+2
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,8 @@ module Completable = struct
616616
fieldName: string;
617617
posOfDot: (int * int) option;
618618
exprLoc: Location.t;
619+
inJsx: bool;
620+
(** Whether this field access was found in a JSX context. *)
619621
}
620622
| CPObj of contextPath * string
621623
| CPAwait of contextPath

analysis/src/TypeUtils.ml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,10 @@ let transformCompletionToPipeCompletion ?(synthetic = false) ~env ?posOfDot
11521152
{
11531153
completion with
11541154
name = nameWithPipe;
1155-
sortText = Some (name |> String.split_on_char '.' |> List.rev |> List.hd);
1155+
sortText =
1156+
(match completion.sortText with
1157+
| Some _ -> completion.sortText
1158+
| None -> Some (name |> String.split_on_char '.' |> List.rev |> List.hd));
11561159
insertText = Some nameWithPipe;
11571160
env;
11581161
synthetic;

tests/analysis_tests/tests/src/CompletionJsx.res

+4
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,7 @@ module Info = {
8989

9090
// <Info _type={#warning} >
9191
// ^com
92+
93+
94+
// let _ = <p>{"".s}</p>
95+
// ^com

0 commit comments

Comments
 (0)