Skip to content

Commit e8a477e

Browse files
committed
start work on following pattern path
1 parent 356320e commit e8a477e

File tree

5 files changed

+215
-44
lines changed

5 files changed

+215
-44
lines changed

Diff for: analysis/src/CompletionBackEnd.ml

+29-1
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ and getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
699699
(* Pipe completion with array just needs to know that it's an array, not
700700
what inner type it has. *)
701701
[
702-
Completion.create "array" ~env
702+
Completion.create "dummy" ~env
703703
~kind:
704704
(Completion.Value
705705
(Ctype.newconstr (Path.Pident (Ident.create "array")) []));
@@ -722,6 +722,7 @@ and getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
722722
|> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact
723723
~completionContext ~env ~scope
724724
| CPApply (cp, labels) -> (
725+
(* TODO: Also needs to support ExtractedType *)
725726
match
726727
cp
727728
|> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
@@ -806,6 +807,7 @@ and getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
806807
~kind:(Completion.Field (field, recordAsString)))
807808
else None))
808809
| CPObj (cp, label) -> (
810+
(* TODO: Also needs to support ExtractedType *)
809811
match
810812
cp
811813
|> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
@@ -1032,6 +1034,7 @@ and getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
10321034
~kind:(Completion.Value (Utils.unwrapIfOption typ));
10331035
])
10341036
| CArgument {functionContextPath; argumentLabel} -> (
1037+
(* TODO: Also needs to support ExtractedType *)
10351038
let labels, env =
10361039
match
10371040
functionContextPath
@@ -1068,6 +1071,31 @@ and getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
10681071
(Completion.Value
10691072
(if expandOption then Utils.unwrapIfOption typ else typ));
10701073
])
1074+
| CPatternPath {rootCtxPath; nested} -> (
1075+
match
1076+
rootCtxPath
1077+
|> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
1078+
~exact:true ~scope
1079+
|> completionsGetCompletionType2 ~full ~opens ~rawOpens ~allFiles ~pos
1080+
~scope
1081+
with
1082+
| Some (typ, env) -> (
1083+
let typ =
1084+
match typ with
1085+
| ExtractedType typ -> Some typ
1086+
| TypeExpr typ -> typ |> TypeUtils.extractType ~env ~package
1087+
in
1088+
match typ with
1089+
| None -> []
1090+
| Some typ -> (
1091+
match typ |> TypeUtils.resolveNested ~env ~full ~nested with
1092+
| Some (typ, env, _completionContext) ->
1093+
[
1094+
Completion.create "dummy" ~env
1095+
~kind:(Completion.ExtractedType (typ, `Value));
1096+
]
1097+
| None -> []))
1098+
| None -> [])
10711099

10721100
let getOpens ~debug ~rawOpens ~package ~env =
10731101
if debug && rawOpens <> [] then

Diff for: analysis/src/CompletionFrontEnd.ml

+86-20
Original file line numberDiff line numberDiff line change
@@ -292,39 +292,105 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
292292
scope :=
293293
!scope |> Scope.addValue ~name:vd.pval_name.txt ~loc:vd.pval_name.loc
294294
in
295-
let rec scopePattern ?contextPath (pat : Parsetree.pattern) =
295+
let rec scopePattern ?contextPath
296+
?(patternPath : Completable.nestedPath list = [])
297+
(pat : Parsetree.pattern) =
298+
let contextPathToSave =
299+
match (contextPath, patternPath) with
300+
| maybeContextPath, [] -> maybeContextPath
301+
| Some contextPath, patternPath ->
302+
Some
303+
(Completable.CPatternPath
304+
{rootCtxPath = contextPath; nested = List.rev patternPath})
305+
| _ -> None
306+
in
296307
match pat.ppat_desc with
297308
| Ppat_any -> ()
298309
| Ppat_var {txt; loc} ->
299-
scope := !scope |> Scope.addValue ~name:txt ~loc ?contextPath
310+
scope :=
311+
!scope |> Scope.addValue ~name:txt ~loc ?contextPath:contextPathToSave
300312
| Ppat_alias (p, asA) ->
301-
scopePattern p;
313+
scopePattern p ~patternPath ?contextPath;
314+
let ctxPath =
315+
if contextPathToSave = None then
316+
match p with
317+
| {ppat_desc = Ppat_var {txt}} ->
318+
Some (Completable.CPId ([txt], Value))
319+
| _ -> None
320+
else None
321+
in
302322
scope :=
303-
!scope
304-
|> Scope.addValue ~name:asA.txt ~loc:asA.loc
305-
?contextPath:
306-
(match p with
307-
| {ppat_desc = Ppat_var {txt}} -> Some (CPId ([txt], Value))
308-
| _ -> None)
323+
!scope |> Scope.addValue ~name:asA.txt ~loc:asA.loc ?contextPath:ctxPath
309324
| Ppat_constant _ | Ppat_interval _ -> ()
310-
| Ppat_tuple pl -> pl |> List.iter (scopePattern ?contextPath)
325+
| Ppat_tuple pl ->
326+
pl
327+
|> List.iteri (fun index p ->
328+
scopePattern p
329+
~patternPath:(NTupleItem {itemNum = index} :: patternPath)
330+
?contextPath)
311331
| Ppat_construct (_, None) -> ()
312-
| Ppat_construct (_, Some p) -> scopePattern ?contextPath p
332+
| Ppat_construct ({txt}, Some {ppat_desc = Ppat_tuple pl}) ->
333+
pl
334+
|> List.iteri (fun index p ->
335+
scopePattern p
336+
~patternPath:
337+
(NVariantPayload
338+
{
339+
itemNum = index;
340+
constructorName = Utils.getUnqualifiedName txt;
341+
}
342+
:: patternPath)
343+
?contextPath)
344+
| Ppat_construct ({txt}, Some p) ->
345+
scopePattern
346+
~patternPath:
347+
(NVariantPayload
348+
{itemNum = 0; constructorName = Utils.getUnqualifiedName txt}
349+
:: patternPath)
350+
?contextPath p
313351
| Ppat_variant (_, None) -> ()
314-
| Ppat_variant (_, Some p) -> scopePattern ?contextPath p
352+
| Ppat_variant (txt, Some {ppat_desc = Ppat_tuple pl}) ->
353+
pl
354+
|> List.iteri (fun index p ->
355+
scopePattern p
356+
~patternPath:
357+
(NPolyvariantPayload {itemNum = index; constructorName = txt}
358+
:: patternPath)
359+
?contextPath)
360+
| Ppat_variant (txt, Some p) ->
361+
scopePattern
362+
~patternPath:
363+
(NPolyvariantPayload {itemNum = 0; constructorName = txt}
364+
:: patternPath)
365+
?contextPath p
315366
| Ppat_record (fields, _) ->
316-
fields |> List.iter (fun (_, p) -> scopePattern ?contextPath p)
317-
| Ppat_array pl -> pl |> List.iter (scopePattern ?contextPath)
318-
| Ppat_or (p1, _) -> scopePattern ?contextPath p1
367+
fields
368+
|> List.iter (fun (fname, p) ->
369+
match fname with
370+
| {Location.txt = Longident.Lident fname} ->
371+
scopePattern
372+
~patternPath:
373+
(Completable.NFollowRecordField {fieldName = fname}
374+
:: patternPath)
375+
?contextPath p
376+
| _ -> ())
377+
| Ppat_array pl ->
378+
pl
379+
|> List.iter
380+
(scopePattern ~patternPath:(NArray :: patternPath) ?contextPath)
381+
| Ppat_or (p1, _) -> scopePattern ~patternPath ?contextPath p1
319382
| Ppat_constraint (p, coreType) ->
320-
scopePattern ?contextPath:(TypeUtils.contextPathFromCoreType coreType) p
383+
scopePattern ~patternPath
384+
?contextPath:(TypeUtils.contextPathFromCoreType coreType)
385+
p
321386
| Ppat_type _ -> ()
322-
| Ppat_lazy p -> scopePattern ?contextPath p
387+
| Ppat_lazy p -> scopePattern ~patternPath ?contextPath p
323388
| Ppat_unpack {txt; loc} ->
324-
scope := !scope |> Scope.addValue ~name:txt ~loc ?contextPath
325-
| Ppat_exception p -> scopePattern ?contextPath p
389+
scope :=
390+
!scope |> Scope.addValue ~name:txt ~loc ?contextPath:contextPathToSave
391+
| Ppat_exception p -> scopePattern ~patternPath ?contextPath p
326392
| Ppat_extension _ -> ()
327-
| Ppat_open (_, p) -> scopePattern ?contextPath p
393+
| Ppat_open (_, p) -> scopePattern ~patternPath ?contextPath p
328394
in
329395

330396
let lookingForPat = ref None in

Diff for: analysis/src/SharedTypes.ml

+31-23
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,29 @@ module Completable = struct
516516
| Labelled of string
517517
| Optional of string
518518

519+
(** Additional context for nested completion where needed. *)
520+
type nestedContext = RecordField of {seenFields: string list}
521+
522+
type nestedPath =
523+
| NTupleItem of {itemNum: int}
524+
| NFollowRecordField of {fieldName: string}
525+
| NRecordBody of {seenFields: string list}
526+
| NVariantPayload of {constructorName: string; itemNum: int}
527+
| NPolyvariantPayload of {constructorName: string; itemNum: int}
528+
| NArray
529+
530+
let nestedPathToString p =
531+
match p with
532+
| NTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")"
533+
| NFollowRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")"
534+
| NRecordBody _ -> "recordBody"
535+
| NVariantPayload {constructorName; itemNum} ->
536+
"variantPayload::" ^ constructorName ^ "($" ^ string_of_int itemNum ^ ")"
537+
| NPolyvariantPayload {constructorName; itemNum} ->
538+
"polyvariantPayload::" ^ constructorName ^ "($" ^ string_of_int itemNum
539+
^ ")"
540+
| NArray -> "array"
541+
519542
type contextPath =
520543
| CPString
521544
| CPArray of contextPath option
@@ -540,29 +563,7 @@ module Completable = struct
540563
argumentLabel: argumentLabel;
541564
}
542565
| CJsxPropValue of {pathToComponent: string list; propName: string}
543-
544-
(** Additional context for nested completion where needed. *)
545-
type nestedContext = RecordField of {seenFields: string list}
546-
547-
type nestedPath =
548-
| NTupleItem of {itemNum: int}
549-
| NFollowRecordField of {fieldName: string}
550-
| NRecordBody of {seenFields: string list}
551-
| NVariantPayload of {constructorName: string; itemNum: int}
552-
| NPolyvariantPayload of {constructorName: string; itemNum: int}
553-
| NArray
554-
555-
let nestedPathToString p =
556-
match p with
557-
| NTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")"
558-
| NFollowRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")"
559-
| NRecordBody _ -> "recordBody"
560-
| NVariantPayload {constructorName; itemNum} ->
561-
"variantPayload::" ^ constructorName ^ "($" ^ string_of_int itemNum ^ ")"
562-
| NPolyvariantPayload {constructorName; itemNum} ->
563-
"polyvariantPayload::" ^ constructorName ^ "($" ^ string_of_int itemNum
564-
^ ")"
565-
| NArray -> "array"
566+
| CPatternPath of {rootCtxPath: contextPath; nested: nestedPath list}
566567

567568
type t =
568569
| Cdecorator of string (** e.g. @module *)
@@ -631,6 +632,13 @@ module Completable = struct
631632
^ ")"
632633
| CJsxPropValue {pathToComponent; propName} ->
633634
"CJsxPropValue " ^ (pathToComponent |> list) ^ " " ^ propName
635+
| CPatternPath {rootCtxPath; nested} ->
636+
"CPatternPath("
637+
^ contextPathToString rootCtxPath
638+
^ ")" ^ "->"
639+
^ (nested
640+
|> List.map (fun nestedPath -> nestedPathToString nestedPath)
641+
|> String.concat "->")
634642

635643
let toString = function
636644
| Cpath cp -> "Cpath " ^ contextPathToString cp

Diff for: analysis/tests/src/CompletionInferValues.res

+21
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,24 @@ module Div = {
5757

5858
// let x: someRecord = {name: "Hello", age: 123}; x.
5959
// ^com
60+
61+
type someVariant = One | Two | Three(int, string)
62+
type someNestedRecord = {someRecord: someRecord}
63+
64+
type someRecordWithNestedStuff = {
65+
things: string,
66+
srecord: someRecord,
67+
nested: someNestedRecord,
68+
someStuff: bool,
69+
}
70+
71+
type otherNestedRecord = {someRecord: someRecord, someTuple: (someVariant, int)}
72+
73+
// let x: someRecordWithNestedStuff = {things: "", someRecord: {name: "Hello", age: 123}, someStuff: true}; let {srecord} = x; srecord.
74+
// ^com
75+
76+
// let x: someRecordWithNestedStuff = {things: "", someRecord: {name: "Hello", age: 123}, someStuff: true}; let {nested: aliased} = x; aliased.
77+
// ^com
78+
79+
// let x: someRecordWithNestedStuff = {things: "", someRecord: {name: "Hello", age: 123}, someStuff: true}; let {srecord, nested: {someRecord}} = x; someRecord.
80+
// ^com

Diff for: analysis/tests/src/expected/CompletionInferValues.res.txt

+48
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,51 @@ Completable: Cpath Value[x].""
220220
"documentation": null
221221
}]
222222

223+
Complete src/CompletionInferValues.res 72:135
224+
posCursor:[72:135] posNoWhite:[72:134] Found expr:[72:127->72:135]
225+
Pexp_field [72:127->72:134] _:[81:0->72:135]
226+
Completable: Cpath Value[srecord].""
227+
[{
228+
"label": "name",
229+
"kind": 5,
230+
"tags": [],
231+
"detail": "name: string\n\nsomeRecord",
232+
"documentation": null
233+
}, {
234+
"label": "age",
235+
"kind": 5,
236+
"tags": [],
237+
"detail": "age: int\n\nsomeRecord",
238+
"documentation": null
239+
}]
240+
241+
Complete src/CompletionInferValues.res 75:143
242+
posCursor:[75:143] posNoWhite:[75:142] Found expr:[75:135->75:143]
243+
Pexp_field [75:135->75:142] _:[81:0->75:143]
244+
Completable: Cpath Value[aliased].""
245+
[{
246+
"label": "someRecord",
247+
"kind": 5,
248+
"tags": [],
249+
"detail": "someRecord: someRecord\n\nsomeNestedRecord",
250+
"documentation": null
251+
}]
252+
253+
Complete src/CompletionInferValues.res 78:160
254+
posCursor:[78:160] posNoWhite:[78:159] Found expr:[78:149->78:160]
255+
Pexp_field [78:149->78:159] _:[81:0->78:160]
256+
Completable: Cpath Value[someRecord].""
257+
[{
258+
"label": "name",
259+
"kind": 5,
260+
"tags": [],
261+
"detail": "name: string\n\nsomeRecord",
262+
"documentation": null
263+
}, {
264+
"label": "age",
265+
"kind": 5,
266+
"tags": [],
267+
"detail": "age: int\n\nsomeRecord",
268+
"documentation": null
269+
}]
270+

0 commit comments

Comments
 (0)