Skip to content

Commit fa68c9e

Browse files
committed
add completion for React primitives
1 parent 82bbdc5 commit fa68c9e

File tree

6 files changed

+241
-32
lines changed

6 files changed

+241
-32
lines changed

analysis/src/CompletionBackEnd.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
990990
path @ [fieldName]
991991
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact
992992
~completionContext:Field ~env ~scope
993-
| CPField {contextPath = cp; fieldName; posOfDot; exprLoc} -> (
993+
| CPField {contextPath = cp; fieldName; posOfDot; exprLoc; inJsx} -> (
994994
if Debug.verbose () then print_endline "[dot_completion]--> Triggered";
995995
let completionsFromCtxPath =
996996
cp
@@ -1024,7 +1024,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10241024
CPApply (cp, [Asttypes.Nolabel])
10251025
| _ -> cp);
10261026
id = fieldName;
1027-
inJsx = false;
1027+
inJsx;
10281028
lhsLoc = exprLoc;
10291029
}
10301030
in

analysis/src/CompletionFrontEnd.ml

+44-29
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 ("|." | "|.u")} -> 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
@@ -261,7 +264,7 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
261264
(_, {pexp_desc = Pexp_apply (d, args); pexp_loc; pexp_attributes});
262265
] ) ->
263266
(* Transform away pipe with apply call *)
264-
exprToContextPath
267+
exprToContextPath ~inJsxContext
265268
{
266269
pexp_desc = Pexp_apply (d, (Nolabel, lhs) :: args);
267270
pexp_loc;
@@ -272,7 +275,7 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
272275
[(_, lhs); (_, {pexp_desc = Pexp_ident id; pexp_loc; pexp_attributes})]
273276
) ->
274277
(* Transform away pipe with identifier *)
275-
exprToContextPath
278+
exprToContextPath ~inJsxContext
276279
{
277280
pexp_desc =
278281
Pexp_apply
@@ -282,26 +285,28 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
282285
pexp_attributes;
283286
}
284287
| Pexp_apply (e1, args) -> (
285-
match exprToContextPath e1 with
288+
match exprToContextPath ~inJsxContext e1 with
286289
| None -> None
287290
| Some contexPath -> Some (CPApply (contexPath, args |> List.map fst)))
288291
| Pexp_tuple exprs ->
289-
let exprsAsContextPaths = exprs |> List.filter_map exprToContextPath in
292+
let exprsAsContextPaths =
293+
exprs |> List.filter_map (exprToContextPath ~inJsxContext)
294+
in
290295
if List.length exprs = List.length exprsAsContextPaths then
291296
Some (CTuple exprsAsContextPaths)
292297
else None
293298
| _ -> None
294299

295-
and exprToContextPath (e : Parsetree.expression) =
300+
and exprToContextPath ~(inJsxContext : bool) (e : Parsetree.expression) =
296301
match
297302
( Res_parsetree_viewer.has_await_attribute e.pexp_attributes,
298-
exprToContextPathInner e )
303+
exprToContextPathInner ~inJsxContext e )
299304
with
300305
| true, Some ctxPath -> Some (CPAwait ctxPath)
301306
| false, Some ctxPath -> Some ctxPath
302307
| _, None -> None
303308

304-
let completePipeChain (exp : Parsetree.expression) =
309+
let completePipeChain ~(inJsxContext : bool) (exp : Parsetree.expression) =
305310
(* Complete the end of pipe chains by reconstructing the pipe chain as a single pipe,
306311
so it can be completed.
307312
Example:
@@ -315,13 +320,15 @@ let completePipeChain (exp : Parsetree.expression) =
315320
| Pexp_apply
316321
( {pexp_desc = Pexp_ident {txt = Lident ("|." | "|.u")}},
317322
[_; (_, {pexp_desc = Pexp_apply (d, _)})] ) ->
318-
exprToContextPath exp |> Option.map (fun ctxPath -> (ctxPath, d.pexp_loc))
323+
exprToContextPath ~inJsxContext exp
324+
|> Option.map (fun ctxPath -> (ctxPath, d.pexp_loc))
319325
(* When the left side of the pipe we're completing is an identifier application.
320326
Example: someArray->filterAllTheGoodStuff-> *)
321327
| Pexp_apply
322328
( {pexp_desc = Pexp_ident {txt = Lident ("|." | "|.u")}},
323329
[_; (_, {pexp_desc = Pexp_ident _; pexp_loc})] ) ->
324-
exprToContextPath exp |> Option.map (fun ctxPath -> (ctxPath, pexp_loc))
330+
exprToContextPath ~inJsxContext exp
331+
|> Option.map (fun ctxPath -> (ctxPath, pexp_loc))
325332
| _ -> None
326333

327334
let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
@@ -408,6 +415,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
408415
(Completable.toString x);
409416
result := Some (x, !scope)
410417
in
418+
let inJsxContext = ref false in
411419
let setResult x = setResultOpt (Some x) in
412420
let scopeValueDescription (vd : Parsetree.value_description) =
413421
scope :=
@@ -542,9 +550,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
542550
(* Pipe chains get special treatment here, because when assigning values
543551
we want the return of the entire pipe chain as a function call, rather
544552
than as a pipe completion call. *)
545-
match completePipeChain vb.pvb_expr with
553+
match completePipeChain ~inJsxContext:!inJsxContext vb.pvb_expr with
546554
| Some (ctxPath, _) -> Some ctxPath
547-
| None -> exprToContextPath vb.pvb_expr
555+
| None -> exprToContextPath ~inJsxContext:!inJsxContext vb.pvb_expr
548556
in
549557
scopePattern ?contextPath vb.pvb_pat
550558
in
@@ -576,7 +584,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
576584
scope :=
577585
!scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc
578586
in
579-
let inJsxContext = ref false in
587+
580588
(* Identifies expressions where we can do typed pattern or expr completion. *)
581589
let typedCompletionExpr (exp : Parsetree.expression) =
582590
let debugTypedCompletionExpr = false in
@@ -593,7 +601,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
593601
print_endline "[typedCompletionExpr] No cases - has cursor";
594602
(* We can do exhaustive switch completion if this is an ident we can
595603
complete from. *)
596-
match exprToContextPath expr with
604+
match exprToContextPath ~inJsxContext:!inJsxContext expr with
597605
| None -> ()
598606
| Some contextPath ->
599607
setResult (CexhaustiveSwitch {contextPath; exprLoc = exp.pexp_loc}))
@@ -623,7 +631,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
623631
};
624632
] ) -> (
625633
(* A single case that's a pattern hole typically means `switch x { | }`. Complete as the pattern itself with nothing nested. *)
626-
match exprToContextPath exp with
634+
match exprToContextPath ~inJsxContext:!inJsxContext exp with
627635
| None -> ()
628636
| Some ctxPath ->
629637
setResult
@@ -640,7 +648,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
640648
print_endline "[typedCompletionExpr] Has cases";
641649
(* If there's more than one case, or the case isn't a pattern hole, figure out if we're completing another
642650
broken parser case (`switch x { | true => () | <com> }` for example). *)
643-
match exp |> exprToContextPath with
651+
match exp |> exprToContextPath ~inJsxContext:!inJsxContext with
644652
| None ->
645653
if Debug.verbose () && debugTypedCompletionExpr then
646654
print_endline "[typedCompletionExpr] Has cases - no ctx path"
@@ -781,7 +789,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
781789
( pvb_pat
782790
|> CompletionPatterns.traversePattern ~patternPath:[] ~locHasCursor
783791
~firstCharBeforeCursorNoWhite ~posBeforeCursor,
784-
exprToContextPath pvb_expr )
792+
exprToContextPath ~inJsxContext:!inJsxContext pvb_expr )
785793
with
786794
| Some (prefix, nested), Some ctxPath ->
787795
setResult
@@ -1037,14 +1045,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
10371045
in
10381046
(match findThisExprLoc with
10391047
| Some loc when expr.pexp_loc = loc -> (
1040-
match exprToContextPath expr with
1048+
match exprToContextPath ~inJsxContext:!inJsxContext expr with
10411049
| None -> ()
10421050
| Some ctxPath -> setResult (Cpath ctxPath))
10431051
| _ -> ());
10441052
let setPipeResult ~(lhs : Parsetree.expression) ~id =
1045-
match completePipeChain lhs with
1053+
match completePipeChain ~inJsxContext:!inJsxContext lhs with
10461054
| None -> (
1047-
match exprToContextPath lhs with
1055+
match exprToContextPath ~inJsxContext:!inJsxContext lhs with
10481056
| Some pipe ->
10491057
setResult
10501058
(Cpath
@@ -1079,7 +1087,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
10791087
&& Option.is_none findThisExprLoc ->
10801088
if Debug.verbose () then
10811089
print_endline "[completionFrontend] Checking each case";
1082-
let ctxPath = exprToContextPath expr in
1090+
let ctxPath = exprToContextPath ~inJsxContext:!inJsxContext expr in
10831091
let oldCtxPath = !currentCtxPath in
10841092
cases
10851093
|> List.iter (fun (case : Parsetree.case) ->
@@ -1178,7 +1186,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11781186
if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then
11791187
match fieldName.txt with
11801188
| Lident name -> (
1181-
match exprToContextPath e with
1189+
match exprToContextPath ~inJsxContext:!inJsxContext e with
11821190
| Some contextPath ->
11831191
let contextPath =
11841192
Completable.CPField
@@ -1187,6 +1195,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11871195
fieldName = name;
11881196
posOfDot;
11891197
exprLoc = e.pexp_loc;
1198+
inJsx = !inJsxContext;
11901199
}
11911200
in
11921201
setResult (Cpath contextPath)
@@ -1210,12 +1219,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
12101219
else name);
12111220
posOfDot;
12121221
exprLoc = e.pexp_loc;
1222+
inJsx = !inJsxContext;
12131223
}
12141224
in
12151225
setResult (Cpath contextPath)
12161226
| Lapply _ -> ()
12171227
else if Loc.end_ e.pexp_loc = posBeforeCursor then
1218-
match exprToContextPath e with
1228+
match exprToContextPath ~inJsxContext:!inJsxContext e with
12191229
| Some contextPath ->
12201230
setResult
12211231
(Cpath
@@ -1225,6 +1235,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
12251235
fieldName = "";
12261236
posOfDot;
12271237
exprLoc = e.pexp_loc;
1238+
inJsx = !inJsxContext;
12281239
}))
12291240
| None -> ())
12301241
| Pexp_apply ({pexp_desc = Pexp_ident compName}, args)
@@ -1295,7 +1306,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
12951306
if Debug.verbose () then
12961307
print_endline "[expr_iter] Complete fn arguments (piped)";
12971308
let args = extractExpApplyArgs ~args in
1298-
let funCtxPath = exprToContextPath funExpr in
1309+
let funCtxPath =
1310+
exprToContextPath ~inJsxContext:!inJsxContext funExpr
1311+
in
12991312
let argCompletable =
13001313
match funCtxPath with
13011314
| Some contextPath ->
@@ -1343,7 +1356,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13431356
(Loc.toString exp.pexp_loc))
13441357
|> String.concat ", ");
13451358

1346-
let funCtxPath = exprToContextPath funExpr in
1359+
let funCtxPath =
1360+
exprToContextPath ~inJsxContext:!inJsxContext funExpr
1361+
in
13471362
let argCompletable =
13481363
match funCtxPath with
13491364
| Some contextPath ->
@@ -1387,7 +1402,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13871402
labelRange |> Range.hasPos ~pos:posBeforeCursor
13881403
|| (label = "" && posCursor = fst labelRange)
13891404
then
1390-
match exprToContextPath lhs with
1405+
match exprToContextPath ~inJsxContext:!inJsxContext lhs with
13911406
| Some contextPath -> setResult (Cpath (CPObj (contextPath, label)))
13921407
| None -> ())
13931408
| Pexp_fun (lbl, defaultExpOpt, pat, e) ->

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
@@ -1169,7 +1169,10 @@ let transformCompletionToPipeCompletion ?(synthetic = false) ~env ?posOfDot
11691169
{
11701170
completion with
11711171
name = nameWithPipe;
1172-
sortText = Some (name |> String.split_on_char '.' |> List.rev |> List.hd);
1172+
sortText =
1173+
(match completion.sortText with
1174+
| Some _ -> completion.sortText
1175+
| None -> Some (name |> String.split_on_char '.' |> List.rev |> List.hd));
11731176
insertText = Some nameWithPipe;
11741177
env;
11751178
synthetic;

analysis/tests/src/CompletionJsx.res

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

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

0 commit comments

Comments
 (0)