Skip to content

Commit aa9aec1

Browse files
committed
refactor and unify typed expression completions under context path
1 parent bb58058 commit aa9aec1

11 files changed

+498
-311
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 249 additions & 186 deletions
Large diffs are not rendered by default.

analysis/src/CompletionFrontEnd.ml

Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ let rec traverseExpr (exp : Parsetree.expression) ~exprPath ~pos
101101
someIfHasCursor
102102
("", [Completable.NFollowRecordField {fieldName = fname}] @ exprPath)
103103
| Pexp_ident {txt = Lident txt} ->
104-
(* A var means `{s}` or similar. Complete for fields. *)
105-
someIfHasCursor (txt, [Completable.NRecordBody {seenFields}] @ exprPath)
104+
(* A var means `{someField: s}` or similar. Complete for identifiers or values. *)
105+
someIfHasCursor (txt, exprPath)
106106
| _ ->
107107
f
108108
|> traverseExpr ~firstCharBeforeCursorNoWhite ~pos
@@ -273,24 +273,32 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor
273273
with
274274
| Some (prefix, nested) ->
275275
Some
276-
(CjsxPropValue
276+
(Cexpression
277277
{
278-
pathToComponent =
279-
Utils.flattenLongIdent ~jsx:true jsxProps.compName.txt;
280-
prefix;
281-
propName = prop.name;
278+
contextPath =
279+
CJsxPropValue
280+
{
281+
pathToComponent =
282+
Utils.flattenLongIdent ~jsx:true jsxProps.compName.txt;
283+
propName = prop.name;
284+
};
282285
nested = List.rev nested;
286+
prefix;
283287
})
284288
| _ -> None
285289
else if prop.exp.pexp_loc |> Loc.end_ = (Location.none |> Loc.end_) then
286290
if isExprHole prop.exp then
287291
Some
288-
(CjsxPropValue
292+
(Cexpression
289293
{
290-
pathToComponent =
291-
Utils.flattenLongIdent ~jsx:true jsxProps.compName.txt;
294+
contextPath =
295+
CJsxPropValue
296+
{
297+
pathToComponent =
298+
Utils.flattenLongIdent ~jsx:true jsxProps.compName.txt;
299+
propName = prop.name;
300+
};
292301
prefix = "";
293-
propName = prop.name;
294302
nested = [];
295303
})
296304
else None
@@ -382,19 +390,27 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
382390
| None -> None
383391
| Some (prefix, nested) ->
384392
Some
385-
(Cargument
393+
(Cexpression
386394
{
387-
functionContextPath = contextPath;
388-
argumentLabel = Labelled labelled.name;
395+
contextPath =
396+
CArgument
397+
{
398+
functionContextPath = contextPath;
399+
argumentLabel = Labelled labelled.name;
400+
};
389401
prefix;
390402
nested = List.rev nested;
391403
})
392404
else if isExprHole exp then
393405
Some
394-
(Cargument
406+
(Cexpression
395407
{
396-
functionContextPath = contextPath;
397-
argumentLabel = Labelled labelled.name;
408+
contextPath =
409+
CArgument
410+
{
411+
functionContextPath = contextPath;
412+
argumentLabel = Labelled labelled.name;
413+
};
398414
prefix = "";
399415
nested = [];
400416
})
@@ -410,20 +426,29 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
410426
| None -> None
411427
| Some (prefix, nested) ->
412428
Some
413-
(Cargument
429+
(Cexpression
414430
{
415-
functionContextPath = contextPath;
416-
argumentLabel =
417-
Unlabelled {argumentPosition = !unlabelledCount};
431+
contextPath =
432+
CArgument
433+
{
434+
functionContextPath = contextPath;
435+
argumentLabel =
436+
Unlabelled {argumentPosition = !unlabelledCount};
437+
};
418438
prefix;
419439
nested = List.rev nested;
420440
})
421441
else if isExprHole exp then
422442
Some
423-
(Cargument
443+
(Cexpression
424444
{
425-
functionContextPath = contextPath;
426-
argumentLabel = Unlabelled {argumentPosition = !unlabelledCount};
445+
contextPath =
446+
CArgument
447+
{
448+
functionContextPath = contextPath;
449+
argumentLabel =
450+
Unlabelled {argumentPosition = !unlabelledCount};
451+
};
427452
prefix = "";
428453
nested = [];
429454
})
@@ -436,11 +461,15 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
436461
| Some '~' -> Some (Completable.CnamedArg (contextPath, "", allNames))
437462
| _ ->
438463
Some
439-
(Cargument
464+
(Cexpression
440465
{
441-
functionContextPath = contextPath;
442-
argumentLabel =
443-
Unlabelled {argumentPosition = !unlabelledCount};
466+
contextPath =
467+
CArgument
468+
{
469+
functionContextPath = contextPath;
470+
argumentLabel =
471+
Unlabelled {argumentPosition = !unlabelledCount};
472+
};
444473
prefix = "";
445474
nested = [];
446475
})
@@ -453,10 +482,14 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
453482
]
454483
when fnHasCursor ->
455484
Some
456-
(Completable.Cargument
485+
(Completable.Cexpression
457486
{
458-
functionContextPath = contextPath;
459-
argumentLabel = Unlabelled {argumentPosition = 0};
487+
contextPath =
488+
CArgument
489+
{
490+
functionContextPath = contextPath;
491+
argumentLabel = Unlabelled {argumentPosition = 0};
492+
};
460493
prefix = "";
461494
nested = [];
462495
})
@@ -841,7 +874,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
841874
setResult
842875
(Completable.Cpattern
843876
{
844-
typ = ctxPath;
877+
contextPath = ctxPath;
845878
prefix;
846879
nested = List.rev nestedPattern;
847880
fallback = None;
@@ -910,7 +943,12 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
910943
| Some ctxPath ->
911944
setResult
912945
(Completable.Cpattern
913-
{typ = ctxPath; nested = []; prefix = ""; fallback = None}))
946+
{
947+
contextPath = ctxPath;
948+
nested = [];
949+
prefix = "";
950+
fallback = None;
951+
}))
914952
| Pexp_match (exp, cases) -> (
915953
(* If there's more than one case, or the case isn't a pattern hole, figure out if we're completing another
916954
broken parser case (`switch x { | true => () | <com> }` for example). *)
@@ -937,7 +975,12 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
937975
(* If there's no case with the cursor, but a broken parser case, complete for the top level. *)
938976
setResult
939977
(Completable.Cpattern
940-
{typ = ctxPath; nested = []; prefix = ""; fallback = None})
978+
{
979+
contextPath = ctxPath;
980+
nested = [];
981+
prefix = "";
982+
fallback = None;
983+
})
941984
| false, false -> ()))
942985
| _ -> unsetLookingForPat ()
943986
in

analysis/src/SharedTypes.ml

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,11 @@ module Completable = struct
550550
(** The loc item for the left hand side of the pipe. *)
551551
}
552552
| CTuple of contextPath list
553+
| CArgument of {
554+
functionContextPath: contextPath;
555+
argumentLabel: argumentLabel;
556+
}
557+
| CJsxPropValue of {pathToComponent: string list; propName: string}
553558

554559
(** Additional context for nested completion where needed. *)
555560
type nestedContext = RecordField of {seenFields: string list}
@@ -582,21 +587,13 @@ module Completable = struct
582587
| Cpath of contextPath
583588
| Cjsx of string list * string * string list
584589
(** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for <M.Comp id1=... id2=... ... id *)
585-
| Cargument of {
586-
functionContextPath: contextPath;
587-
argumentLabel: argumentLabel;
588-
nested: nestedPath list;
589-
prefix: string;
590-
}
591-
(** e.g. someFunction(~someBoolArg=<com>), complete for the value of `someBoolArg` (true or false). *)
592-
| CjsxPropValue of {
593-
pathToComponent: string list;
594-
propName: string;
590+
| Cexpression of {
591+
contextPath: contextPath;
595592
nested: nestedPath list;
596593
prefix: string;
597594
}
598595
| Cpattern of {
599-
typ: contextPath;
596+
contextPath: contextPath;
600597
nested: nestedPath list;
601598
prefix: string;
602599
fallback: t option;
@@ -655,6 +652,18 @@ module Completable = struct
655652
"CTuple("
656653
^ (ctxPaths |> List.map contextPathToString |> String.concat ", ")
657654
^ ")"
655+
| CArgument {functionContextPath; argumentLabel} ->
656+
"CArgument "
657+
^ contextPathToString functionContextPath
658+
^ "("
659+
^ (match argumentLabel with
660+
| Unlabelled {argumentPosition} ->
661+
"$" ^ string_of_int argumentPosition
662+
| Labelled name -> "~" ^ name
663+
| Optional name -> "~" ^ name ^ "=?")
664+
^ ")"
665+
| CJsxPropValue {pathToComponent; propName} ->
666+
"CJsxPropValue " ^ (pathToComponent |> list) ^ " " ^ propName
658667
in
659668

660669
function
@@ -667,16 +676,10 @@ module Completable = struct
667676
| Cnone -> "Cnone"
668677
| Cjsx (sl1, s, sl2) ->
669678
"Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")"
670-
| Cargument {functionContextPath; argumentLabel; prefix; nested} -> (
671-
"Cargument "
672-
^ contextPathToString functionContextPath
673-
^ "("
674-
^ (match argumentLabel with
675-
| Unlabelled {argumentPosition} -> "$" ^ string_of_int argumentPosition
676-
| Labelled name -> "~" ^ name
677-
| Optional name -> "~" ^ name ^ "=?")
678-
^ (if prefix <> "" then "=" ^ prefix else "")
679-
^ ")"
679+
| Cpattern {contextPath; nested; prefix} -> (
680+
"Cpattern "
681+
^ contextPathToString contextPath
682+
^ (if prefix = "" then "" else "=" ^ prefix)
680683
^
681684
match nested with
682685
| [] -> ""
@@ -685,11 +688,9 @@ module Completable = struct
685688
^ (nestedPaths
686689
|> List.map (fun nestedPath -> nestedPathToString nestedPath)
687690
|> String.concat ", "))
688-
| CjsxPropValue {prefix; pathToComponent; propName} ->
689-
"CjsxPropValue " ^ (pathToComponent |> list) ^ " " ^ propName ^ "="
690-
^ prefix
691-
| Cpattern {typ; nested; prefix} -> (
692-
"Cpattern " ^ contextPathToString typ
691+
| Cexpression {contextPath; nested; prefix} -> (
692+
"Cexpression "
693+
^ contextPathToString contextPath
693694
^ (if prefix = "" then "" else "=" ^ prefix)
694695
^
695696
match nested with

analysis/src/Utils.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,9 @@ let rec skipWhite text i =
162162

163163
let hasBraces attributes =
164164
attributes |> List.exists (fun (loc, _) -> loc.Location.txt = "ns.braces")
165+
166+
let rec unwrapIfOption (t : Types.type_expr) =
167+
match t.desc with
168+
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> unwrapIfOption t1
169+
| Tconstr (Path.Pident {name = "option"}, [unwrappedType], _) -> unwrappedType
170+
| _ -> t

analysis/tests/src/CompletionExpressions.res

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ let fnTakingArray = (arr: array<option<bool>>) => {
7373
// let _ = fnTakingArray([])
7474
// ^com
7575

76+
// let _ = fnTakingArray(s)
77+
// ^com
78+
7679
// let _ = fnTakingArray([Some()])
7780
// ^com
7881

@@ -81,3 +84,8 @@ let fnTakingArray = (arr: array<option<bool>>) => {
8184

8285
// let _ = fnTakingArray([None, , None])
8386
// ^com
87+
88+
let someBoolVar = true
89+
90+
// let _ = fnTakingRecord({offline: so })
91+
// ^com

analysis/tests/src/CompletionFunctionArguments.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ let someFnTakingVariant = (
5555
// let _ = someFnTakingVariant(~config=O)
5656
// ^com
5757

58-
// let _ = someFnTakingVariant(S)
59-
// ^com
58+
// let _ = someFnTakingVariant(So)
59+
// ^com
6060

6161
// let _ = someFnTakingVariant(~configOpt2=O)
6262
// ^com

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ Completable: Cpath Value[Js, Dict, u]
463463
Complete src/Completion.res 59:30
464464
posCursor:[59:30] posNoWhite:[59:29] Found expr:[59:15->59:30]
465465
JSX <O.Comp:[59:15->59:21] second[59:22->59:28]=...[59:29->59:30]> _children:None
466-
Completable: CjsxPropValue [O, Comp] second=z
466+
Completable: Cexpression CJsxPropValue [O, Comp] second=z
467467
[{
468468
"label": "zzz",
469469
"kind": 12,

0 commit comments

Comments
 (0)