@@ -238,6 +238,13 @@ module CompletionInstruction = struct
238
238
to the record itself. *)
239
239
prefix : string ;
240
240
} (* * Completing inside of an expression. *)
241
+ | CnamedArg of {
242
+ ctxPath : ctxPath ;
243
+ (* * Context path to the function with the argument. *)
244
+ seenLabels : string list ;
245
+ (* * All the already seen labels in the function call. *)
246
+ prefix : string ; (* * The text the user has written so far.*)
247
+ }
241
248
242
249
let ctxPath ctxPath = CtxPath ctxPath
243
250
@@ -257,6 +264,9 @@ module CompletionInstruction = struct
257
264
ctxPath = completionContext.ctxPath;
258
265
}
259
266
267
+ let namedArg ~prefix ~functionContextPath ~seenLabels =
268
+ CnamedArg {prefix; ctxPath = functionContextPath; seenLabels}
269
+
260
270
let toString (c : t ) =
261
271
match c with
262
272
| CtxPath ctxPath ->
@@ -276,6 +286,10 @@ module CompletionInstruction = struct
276
286
(match prefix with
277
287
| "" -> " "
278
288
| prefix -> Printf. sprintf " , prefix: \" %s\" " prefix)
289
+ | CnamedArg {prefix; ctxPath; seenLabels} ->
290
+ " CnamedArg("
291
+ ^ (ctxPath |> ctxPathToString)
292
+ ^ " , " ^ str prefix ^ " , " ^ (seenLabels |> list ) ^ " )"
279
293
end
280
294
281
295
module CompletionResult = struct
@@ -298,6 +312,12 @@ module CompletionResult = struct
298
312
Some
299
313
( CompletionInstruction. expression ~completion Context ~prefix ,
300
314
completionContext )
315
+
316
+ let namedArg ~(completionContext : CompletionContext.t ) ~prefix ~seenLabels
317
+ ~functionContextPath =
318
+ Some
319
+ ( CompletionInstruction. namedArg ~function ContextPath ~prefix ~seen Labels,
320
+ completionContext )
301
321
end
302
322
303
323
let flattenLidCheckDot ?(jsx = true ) ~(completionContext : CompletionContext.t )
@@ -908,55 +928,126 @@ and completeExpr ~completionContext (expr : Parsetree.expression) :
908
928
if locHasPos lhs.pexp_loc then completeExpr ~completion Context lhs
909
929
else if locHasPos rhs.pexp_loc then completeExpr ~completion Context rhs
910
930
else None
931
+ | Pexp_apply (fnExpr , _args ) when locHasPos fnExpr.pexp_loc ->
932
+ (* Handle when the cursor is in the function expression itself. *)
933
+ fnExpr
934
+ |> completeExpr
935
+ ~completion Context:(completionContext |> CompletionContext. resetCtx)
911
936
| Pexp_apply (fnExpr , args ) -> (
912
- if locHasPos fnExpr.pexp_loc then
913
- (* someFn<com>(). Cursor in the function expression itself. *)
914
- completeExpr
915
- ~completion Context:(CompletionContext. resetCtx completionContext)
916
- fnExpr
917
- else
918
- (* Check if the args has the cursor. *)
919
- (* Keep track of the positions of unlabelled arguments. *)
920
- let unlabelledArgPos = ref (- 1 ) in
921
- let fnContextPath = exprToContextPath fnExpr in
922
- let argWithCursorInExpr =
923
- args
924
- |> List. find_opt
925
- (fun ((arg , argExpr ) : Asttypes. arg_label * Parsetree. expression )
926
- ->
927
- if arg = Nolabel then unlabelledArgPos := ! unlabelledArgPos + 1 ;
928
- locHasPos argExpr.pexp_loc)
937
+ (* Handle when the cursor isn't in the function expression. Possibly in an argument. *)
938
+ (* TODO: Are we moving into all expressions we need here? The fn expression itself? *)
939
+ let fnContextPath = exprToContextPath fnExpr in
940
+ match fnContextPath with
941
+ | None -> None
942
+ | Some functionContextPath -> (
943
+ let beforeCursor = completionContext.positionContext.beforeCursor in
944
+ let isPipedExpr = false (* TODO: Implement *) in
945
+ let args = extractExpApplyArgs ~args in
946
+ let endPos = Loc. end_ expr.pexp_loc in
947
+ let posAfterFnExpr = Loc. end_ fnExpr.pexp_loc in
948
+ let fnHasCursor =
949
+ posAfterFnExpr < = beforeCursor && beforeCursor < endPos
950
+ in
951
+ (* All of the labels already written in the application. *)
952
+ let seenLabels =
953
+ List. fold_right
954
+ (fun arg seenLabels ->
955
+ match arg with
956
+ | {label = Some labelled } -> labelled.name :: seenLabels
957
+ | {label = None } -> seenLabels)
958
+ args []
929
959
in
930
- (* TODO: Complete labelled argument names, pipes *)
931
960
let makeCompletionContextWithArgumentLabel argumentLabel
932
961
~functionContextPath =
933
962
completionContext |> CompletionContext. resetCtx
934
963
|> CompletionContext. currentlyExpectingOrReset
935
964
(Some
936
965
(Type (CFunctionArgument {functionContextPath; argumentLabel})))
937
966
in
938
- match (argWithCursorInExpr, fnContextPath) with
939
- | None , _ -> None
940
- | Some (Nolabel, argExpr ), Some functionContextPath ->
967
+ (* Piped expressions always have an initial unlabelled argument. *)
968
+ let unlabelledCount = ref (if isPipedExpr then 1 else 0 ) in
969
+ let rec loop args =
970
+ match args with
971
+ | {label = Some labelled ; exp} :: rest ->
972
+ if labelled.posStart < = beforeCursor && beforeCursor < labelled.posEnd
973
+ then
974
+ (* Complete for a label: `someFn(~labelNam<com>)` *)
975
+ CompletionResult. namedArg ~completion Context ~prefix: labelled.name
976
+ ~seen Labels ~function ContextPath
977
+ else if locHasPos exp.pexp_loc then
978
+ (* Completing in the assignment of labelled argument, with a value.
979
+ `someFn(~someLabel=someIden<com>)` *)
980
+ let completionContext =
981
+ makeCompletionContextWithArgumentLabel (Labelled labelled.name)
982
+ ~function ContextPath
983
+ in
984
+ completeExpr ~completion Context exp
985
+ else if CompletionExpressions. isExprHole exp then
986
+ (* Completing in the assignment of labelled argument, with no value yet.
987
+ The parser inserts an expr hole. `someFn(~someLabel=<com>)` *)
988
+ let completionContext =
989
+ makeCompletionContextWithArgumentLabel (Labelled labelled.name)
990
+ ~function ContextPath
991
+ in
992
+ CompletionResult. expression ~completion Context ~prefix: " "
993
+ else loop rest
994
+ | {label = None ; exp} :: rest ->
995
+ if Res_parsetree_viewer. isTemplateLiteral exp then
996
+ (* Ignore template literals, or we mess up completion inside of them. *)
997
+ None
998
+ else if locHasPos exp.pexp_loc then
999
+ (* Completing in an unlabelled argument with a value. `someFn(someV<com>) *)
1000
+ let completionContext =
1001
+ makeCompletionContextWithArgumentLabel
1002
+ (Unlabelled {argumentPosition = ! unlabelledCount})
1003
+ ~function ContextPath
1004
+ in
1005
+ completeExpr ~completion Context exp
1006
+ else if CompletionExpressions. isExprHole exp then
1007
+ (* Completing in an unlabelled argument without a value. `someFn(true, <com>) *)
1008
+ let completionContext =
1009
+ makeCompletionContextWithArgumentLabel
1010
+ (Unlabelled {argumentPosition = ! unlabelledCount})
1011
+ ~function ContextPath
1012
+ in
1013
+ CompletionResult. expression ~completion Context ~prefix: " "
1014
+ else (
1015
+ unlabelledCount := ! unlabelledCount + 1 ;
1016
+ loop rest)
1017
+ | [] ->
1018
+ if fnHasCursor then
1019
+ (* No matches, but we know we have the cursor. Check the first char
1020
+ behind the cursor. '~' means label completion. *)
1021
+ match completionContext.positionContext.charBeforeCursor with
1022
+ | Some '~' ->
1023
+ CompletionResult. namedArg ~completion Context ~prefix: " "
1024
+ ~seen Labels ~function ContextPath
1025
+ | _ ->
1026
+ (* No '~'. Assume we want to complete for the next unlabelled argument. *)
1027
+ let completionContext =
1028
+ makeCompletionContextWithArgumentLabel
1029
+ (Unlabelled {argumentPosition = ! unlabelledCount})
1030
+ ~function ContextPath
1031
+ in
1032
+ CompletionResult. expression ~completion Context ~prefix: " "
1033
+ else None
1034
+ in
1035
+ match args with
1036
+ (* Special handling for empty fn calls, e.g. `let _ = someFn(<com>)` *)
1037
+ | [
1038
+ {
1039
+ label = None ;
1040
+ exp = {pexp_desc = Pexp_construct ({txt = Lident " ()" }, _)};
1041
+ };
1042
+ ]
1043
+ when fnHasCursor ->
941
1044
let completionContext =
942
1045
makeCompletionContextWithArgumentLabel
943
- (Unlabelled {argumentPosition = ! unlabelledArgPos})
944
- ~function ContextPath
945
- in
946
- completeExpr ~completion Context argExpr
947
- | Some (Labelled label , argExpr ), Some functionContextPath ->
948
- let completionContext =
949
- makeCompletionContextWithArgumentLabel (Labelled label)
1046
+ (Unlabelled {argumentPosition = 0 })
950
1047
~function ContextPath
951
1048
in
952
- completeExpr ~completion Context argExpr
953
- | Some (Optional label , argExpr ), Some functionContextPath ->
954
- let completionContext =
955
- makeCompletionContextWithArgumentLabel (Optional label)
956
- ~function ContextPath
957
- in
958
- completeExpr ~completion Context argExpr
959
- | _ -> None )
1049
+ CompletionResult. expression ~completion Context ~prefix: " "
1050
+ | _ -> loop args))
960
1051
| Pexp_fun _ ->
961
1052
(* We've found a function definition, like `let whatever = (someStr: string) => {}` *)
962
1053
let rec loopFnExprs ~(completionContext : CompletionContext.t )
0 commit comments