@@ -101,6 +101,11 @@ type ctxPath =
101
101
id : string ;
102
102
lhsLoc : Location .t ; (* * Location of the left hand side. *)
103
103
} (* * Piped call. `foo->someFn`. *)
104
+ | CJsxPropValue of {
105
+ pathToComponent : string list ;
106
+ (* * The path to the component this property is from. *)
107
+ propName : string ; (* * The prop name we're going through. *)
108
+ } (* * A JSX property. *)
104
109
105
110
let rec ctxPathToString (ctxPath : ctxPath ) =
106
111
match ctxPath with
@@ -109,6 +114,8 @@ let rec ctxPathToString (ctxPath : ctxPath) =
109
114
| CFloat -> " float"
110
115
| CInt -> " int"
111
116
| CString -> " string"
117
+ | CJsxPropValue {pathToComponent; propName} ->
118
+ " CJsxPropValue " ^ (pathToComponent |> list ) ^ " " ^ propName
112
119
| CAwait ctxPath -> Printf. sprintf " await %s" (ctxPathToString ctxPath)
113
120
| CApply (ctxPath , args ) ->
114
121
Printf. sprintf " %s(%s)" (ctxPathToString ctxPath)
@@ -245,6 +252,15 @@ module CompletionInstruction = struct
245
252
(* * All the already seen labels in the function call. *)
246
253
prefix : string ; (* * The text the user has written so far.*)
247
254
}
255
+ | Cjsx of {
256
+ pathToComponent : string list ;
257
+ (* * The path to the component: `["M", "Comp"]`. *)
258
+ prefix : string ; (* * What the user has already written. `"id"`. *)
259
+ seenProps : string list ;
260
+ (* * A list of all of the props that has already been entered.*)
261
+ }
262
+ | ChtmlElement of {prefix : string (* * What the user has written so far. *) }
263
+ (* * Completing for a regular HTML element. *)
248
264
249
265
let ctxPath ctxPath = CtxPath ctxPath
250
266
@@ -267,6 +283,11 @@ module CompletionInstruction = struct
267
283
let namedArg ~prefix ~functionContextPath ~seenLabels =
268
284
CnamedArg {prefix; ctxPath = functionContextPath; seenLabels}
269
285
286
+ let jsx ~prefix ~pathToComponent ~seenProps =
287
+ Cjsx {prefix; pathToComponent; seenProps}
288
+
289
+ let htmlElement ~prefix = ChtmlElement {prefix}
290
+
270
291
let toString (c : t ) =
271
292
match c with
272
293
| CtxPath ctxPath ->
@@ -290,34 +311,51 @@ module CompletionInstruction = struct
290
311
" CnamedArg("
291
312
^ (ctxPath |> ctxPathToString)
292
313
^ " , " ^ str prefix ^ " , " ^ (seenLabels |> list ) ^ " )"
314
+ | Cjsx {prefix; pathToComponent; seenProps} ->
315
+ " Cjsx(" ^ (pathToComponent |> ident) ^ " , " ^ str prefix ^ " , "
316
+ ^ (seenProps |> list ) ^ " )"
317
+ | ChtmlElement {prefix} -> " ChtmlElement <" ^ prefix ^ " />"
293
318
end
294
319
295
320
module CompletionResult = struct
296
321
type t = (CompletionInstruction .t * CompletionContext .t ) option
297
322
323
+ let make (instruction : CompletionInstruction.t )
324
+ (context : CompletionContext.t ) =
325
+ Some (instruction, context)
326
+
298
327
let ctxPath (ctxPath : ctxPath ) (completionContext : CompletionContext.t ) =
299
328
let completionContext =
300
329
completionContext |> CompletionContext. addCtxPathItem ctxPath
301
330
in
302
- Some
303
- ( CompletionInstruction. ctxPath completionContext.ctxPath,
304
- completionContext )
331
+ make
332
+ (CompletionInstruction. ctxPath completionContext.ctxPath)
333
+ completionContext
305
334
306
335
let pattern ~(completionContext : CompletionContext.t ) ~prefix =
307
- Some
308
- ( CompletionInstruction. pattern ~completion Context ~prefix ,
309
- completionContext )
336
+ make
337
+ (CompletionInstruction. pattern ~completion Context ~prefix )
338
+ completionContext
310
339
311
340
let expression ~(completionContext : CompletionContext.t ) ~prefix =
312
- Some
313
- ( CompletionInstruction. expression ~completion Context ~prefix ,
314
- completionContext )
341
+ make
342
+ (CompletionInstruction. expression ~completion Context ~prefix )
343
+ completionContext
315
344
316
345
let namedArg ~(completionContext : CompletionContext.t ) ~prefix ~seenLabels
317
346
~functionContextPath =
318
- Some
319
- ( CompletionInstruction. namedArg ~function ContextPath ~prefix ~seen Labels,
320
- completionContext )
347
+ make
348
+ (CompletionInstruction. namedArg ~function ContextPath ~prefix ~seen Labels)
349
+ completionContext
350
+
351
+ let jsx ~(completionContext : CompletionContext.t ) ~prefix ~pathToComponent
352
+ ~seenProps =
353
+ make
354
+ (CompletionInstruction. jsx ~prefix ~path ToComponent ~seen Props)
355
+ completionContext
356
+
357
+ let htmlElement ~(completionContext : CompletionContext.t ) ~prefix =
358
+ make (CompletionInstruction. htmlElement ~prefix ) completionContext
321
359
end
322
360
323
361
let flattenLidCheckDot ?(jsx = true ) ~(completionContext : CompletionContext.t )
@@ -938,6 +976,95 @@ and completeExpr ~completionContext (expr : Parsetree.expression) :
938
976
if locHasPos lhs.pexp_loc then completeExpr ~completion Context lhs
939
977
else if locHasPos rhs.pexp_loc then completeExpr ~completion Context rhs
940
978
else None
979
+ | Pexp_apply ({pexp_desc = Pexp_ident compName}, args)
980
+ when Res_parsetree_viewer. isJsxExpression expr -> (
981
+ (* == JSX == *)
982
+ let jsxProps = CompletionJsx. extractJsxProps ~comp Name ~args in
983
+ let compNamePath =
984
+ flattenLidCheckDot ~completion Context ~jsx: true compName
985
+ in
986
+ let beforeCursor = completionContext.positionContext.beforeCursor in
987
+ let endPos = Loc. end_ expr.pexp_loc in
988
+ let posAfterCompName = Loc. end_ compName.loc in
989
+ let allLabels =
990
+ List. fold_right
991
+ (fun (prop : CompletionJsx.prop ) allLabels -> prop.name :: allLabels)
992
+ jsxProps.props []
993
+ in
994
+ let rec loop (props : CompletionJsx.prop list ) =
995
+ match props with
996
+ | prop :: rest ->
997
+ if prop.posStart < = beforeCursor && beforeCursor < prop.posEnd then
998
+ (* Cursor on the prop name *)
999
+ CompletionResult. jsx ~completion Context ~prefix: prop.name
1000
+ ~path ToComponent:
1001
+ (Utils. flattenLongIdent ~jsx: true jsxProps.compName.txt)
1002
+ ~seen Props:allLabels
1003
+ else if
1004
+ prop.posEnd < = beforeCursor
1005
+ && beforeCursor < Loc. start prop.exp.pexp_loc
1006
+ then (* Cursor between the prop name and expr assigned *)
1007
+ None
1008
+ else if locHasPos prop.exp.pexp_loc then
1009
+ (* Cursor in the expr assigned. Move into the expr and set that we're
1010
+ expecting the return type of the prop. *)
1011
+ let completionContext =
1012
+ completionContext
1013
+ |> CompletionContext. setCurrentlyExpecting
1014
+ (Type
1015
+ (CJsxPropValue
1016
+ {
1017
+ propName = prop.name;
1018
+ pathToComponent =
1019
+ Utils. flattenLongIdent ~jsx: true
1020
+ jsxProps.compName.txt;
1021
+ }))
1022
+ in
1023
+ completeExpr ~completion Context prop.exp
1024
+ else if prop.exp.pexp_loc |> Loc. end_ = (Location. none |> Loc. end_) then
1025
+ if CompletionExpressions. isExprHole prop.exp then
1026
+ let completionContext =
1027
+ completionContext
1028
+ |> CompletionContext. addCtxPathItem
1029
+ (CJsxPropValue
1030
+ {
1031
+ propName = prop.name;
1032
+ pathToComponent =
1033
+ Utils. flattenLongIdent ~jsx: true jsxProps.compName.txt;
1034
+ })
1035
+ in
1036
+ CompletionResult. expression ~completion Context ~prefix: " "
1037
+ else None
1038
+ else loop rest
1039
+ | [] ->
1040
+ let beforeChildrenStart =
1041
+ match jsxProps.childrenStart with
1042
+ | Some childrenPos -> beforeCursor < childrenPos
1043
+ | None -> beforeCursor < = endPos
1044
+ in
1045
+ let afterCompName = beforeCursor > = posAfterCompName in
1046
+ if afterCompName && beforeChildrenStart then
1047
+ CompletionResult. jsx ~completion Context ~prefix: " "
1048
+ ~path ToComponent:
1049
+ (Utils. flattenLongIdent ~jsx: true jsxProps.compName.txt)
1050
+ ~seen Props:allLabels
1051
+ else None
1052
+ in
1053
+ let jsxCompletable = loop jsxProps.props in
1054
+ match jsxCompletable with
1055
+ | Some jsxCompletable -> Some jsxCompletable
1056
+ | None ->
1057
+ if locHasPos compName.loc then
1058
+ (* The component name has the cursor.
1059
+ Check if this is a HTML element (lowercase initial char) or a component (uppercase initial char). *)
1060
+ match compNamePath with
1061
+ | [prefix] when Char. lowercase_ascii prefix.[0 ] = prefix.[0 ] ->
1062
+ CompletionResult. htmlElement ~completion Context ~prefix
1063
+ | _ ->
1064
+ CompletionResult. ctxPath
1065
+ (CId (compNamePath, Module ))
1066
+ (completionContext |> CompletionContext. resetCtx)
1067
+ else None )
941
1068
| Pexp_apply (fnExpr , _args ) when locHasPos fnExpr.pexp_loc ->
942
1069
(* Handle when the cursor is in the function expression itself. *)
943
1070
fnExpr
0 commit comments