Skip to content

Commit b3fccdb

Browse files
committed
start integrating completion for records
1 parent c5b6d1d commit b3fccdb

File tree

5 files changed

+186
-15
lines changed

5 files changed

+186
-15
lines changed

analysis/src/CompletionBackEnd.ml

+23-7
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,8 @@ let rec extractType ~env ~package (t : Types.type_expr) =
15301530
Some
15311531
(Tvariant
15321532
{env; constructors; variantName = name.txt; variantDecl = decl})
1533+
| Some (env, {item = {kind = Record fields}}) ->
1534+
Some (Trecord {env; fields; typeExpr = t})
15331535
| _ -> None)
15341536
| Ttuple expressions -> Some (Tuple (env, expressions, t))
15351537
| Tvariant {row_fields} ->
@@ -1568,7 +1570,7 @@ let printConstructorArgs argsLen ~asSnippet =
15681570
else ""
15691571

15701572
let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
1571-
~expandOption =
1573+
~expandOption ~includeLocalValues =
15721574
let namesUsed = Hashtbl.create 10 in
15731575
let rec completeTypedValueInner t ~env ~full ~prefix ~expandOption =
15741576
let items =
@@ -1643,10 +1645,17 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
16431645
~insertText:(printConstructorArgs numExprs ~asSnippet:true)
16441646
~kind:(Value typ) ~env ();
16451647
]
1648+
| Some (Trecord {env; fields; typeExpr}) ->
1649+
fields
1650+
|> List.map (fun (field : field) ->
1651+
Completion.create ~name:field.fname.txt
1652+
~kind:(Field (field, typeExpr |> Shared.typeToString))
1653+
~env)
1654+
|> filterItems ~prefix
16461655
| _ -> []
16471656
in
16481657
(* Include all values and modules in completion if there's a prefix, not otherwise *)
1649-
if prefix = "" then items
1658+
if prefix = "" || includeLocalValues = false then items
16501659
else
16511660
items
16521661
@ completionForExportedValues ~env:envWhereCompletionStarted ~prefix
@@ -1740,6 +1749,13 @@ let rec resolveNestedPattern typ ~env ~package ~nested =
17401749
match List.nth_opt tupleItems itemNum with
17411750
| None -> None
17421751
| Some typ -> typ |> resolveNestedPattern ~env ~package ~nested)
1752+
| Completable.PRecordField {fieldName}, Some (Trecord {env; fields}) -> (
1753+
match
1754+
fields
1755+
|> List.find_opt (fun (field : field) -> field.fname.txt = fieldName)
1756+
with
1757+
| None -> None
1758+
| Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested)
17431759
| _ -> None)
17441760

17451761
let processCompletable ~debug ~full ~scope ~env ~pos ~forHover
@@ -1805,7 +1821,7 @@ let processCompletable ~debug ~full ~scope ~env ~pos ~forHover
18051821
| Some (_, typ, env) ->
18061822
typ
18071823
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
1808-
~expandOption:true)
1824+
~expandOption:true ~includeLocalValues:true)
18091825
| Cdecorator prefix ->
18101826
let mkDecorator (name, docstring) =
18111827
{(Completion.create ~name ~kind:(Label "") ~env) with docstring}
@@ -2070,11 +2086,11 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
20702086
| Some (Optional _, typ) ->
20712087
typ
20722088
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2073-
~expandOption:true
2089+
~expandOption:true ~includeLocalValues:true
20742090
| Some ((Unlabelled _ | Labelled _), typ) ->
20752091
typ
20762092
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2077-
~expandOption:false)
2093+
~expandOption:false ~includeLocalValues:true)
20782094
| CnamedArg (cp, prefix, identsSeen) ->
20792095
let labels =
20802096
match
@@ -2115,7 +2131,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
21152131
| Some (typ, env) ->
21162132
typ
21172133
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2118-
~expandOption:false
2134+
~expandOption:false ~includeLocalValues:false
21192135
| None -> [])
21202136
| Cpattern {typ; prefix; nested = Some nested} -> (
21212137
let envWhereCompletionStarted = env in
@@ -2131,5 +2147,5 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
21312147
| Some (typ, env) ->
21322148
typ
21332149
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2134-
~expandOption:false)
2150+
~expandOption:false ~includeLocalValues:false)
21352151
| None -> [])

analysis/src/CompletionFrontEnd.ml

+33-3
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
413413
let setLookingForPat ctxPath =
414414
lookingForPat :=
415415
Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None});
416-
Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath))
416+
if debug then
417+
Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath))
417418
in
418419
let appendNestedPat patternPat =
419420
match !lookingForPat with
@@ -436,7 +437,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
436437
| _ -> ()
437438
in
438439

439-
let rec typedCompletionExpr (exp : Parsetree.expression) =
440+
let typedCompletionExpr (exp : Parsetree.expression) =
440441
if
441442
exp.pexp_loc
442443
|> CursorPosition.classifyLoc ~pos:posBeforeCursor
@@ -470,7 +471,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
470471
| _ -> ()
471472
in
472473

473-
let rec typedCompletionPat (pat : Parsetree.pattern) =
474+
let typedCompletionPat (pat : Parsetree.pattern) =
474475
if
475476
pat.ppat_loc
476477
|> CursorPosition.classifyLoc ~pos:posBeforeCursor
@@ -496,6 +497,35 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
496497
| _, Some patHoleCount ->
497498
appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount})
498499
| _ -> ())
500+
| Ppat_record ([], _) -> commitFoundPat ~prefix:"" ()
501+
| Ppat_record (fields, _) -> (
502+
let fieldWithCursor = ref None in
503+
let fieldWithPatHole = ref None in
504+
fields
505+
|> List.filter (fun (_, f) ->
506+
(* Ensure we only include fields with patterns we can continue into. *)
507+
match f.Parsetree.ppat_desc with
508+
| Ppat_tuple _ | Ppat_construct _ | Ppat_variant _
509+
| Ppat_record _ ->
510+
true
511+
| _ -> false)
512+
|> List.iter (fun (fname, f) ->
513+
match
514+
( fname.Location.txt,
515+
f.Parsetree.ppat_loc
516+
|> CursorPosition.classifyLoc ~pos:posBeforeCursor )
517+
with
518+
| Longident.Lident fname, HasCursor ->
519+
fieldWithCursor := Some fname
520+
| Lident fname, EmptyLoc when isPatternHole f ->
521+
fieldWithPatHole := Some fname
522+
| _ -> ());
523+
match (!fieldWithCursor, !fieldWithPatHole) with
524+
| Some fname, _ ->
525+
appendNestedPat (Completable.PRecordField {fieldName = fname})
526+
| None, Some fname ->
527+
appendNestedPat (Completable.PRecordField {fieldName = fname})
528+
| _ -> ())
499529
| _ -> ()
500530
in
501531
let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) =

analysis/src/SharedTypes.ml

+9-1
Original file line numberDiff line numberDiff line change
@@ -550,11 +550,14 @@ module Completable = struct
550550
(** The loc item for the left hand side of the pipe. *)
551551
}
552552

553-
type patternPath = PTupleItem of {itemNum: int}
553+
type patternPath =
554+
| PTupleItem of {itemNum: int}
555+
| PRecordField of {fieldName: string}
554556

555557
let patternPathToString p =
556558
match p with
557559
| PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")"
560+
| PRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")"
558561

559562
type t =
560563
| Cdecorator of string (** e.g. @module *)
@@ -597,6 +600,11 @@ module Completable = struct
597600
constructors: polyVariantConstructor list;
598601
typeExpr: Types.type_expr;
599602
}
603+
| Trecord of {
604+
env: QueryEnv.t;
605+
fields: field list;
606+
typeExpr: Types.type_expr;
607+
}
600608

601609
let toString =
602610
let completionContextToString = function

analysis/tests/src/CompletionPattern.res

+31-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,37 @@ let _ = switch v {
2020
let x = true
2121

2222
// switch x { |
23-
// ^com
23+
// ^com
2424

2525
// switch x { | t
2626
// ^com
27+
28+
type nestedRecord = {nested: bool}
29+
30+
type rec someRecord = {
31+
first: int,
32+
second: (bool, option<someRecord>),
33+
optThird: option<[#first | #second(someRecord)]>,
34+
nest: nestedRecord,
35+
}
36+
37+
let f: someRecord = {
38+
first: 123,
39+
second: (true, None),
40+
optThird: None,
41+
nest: {nested: true},
42+
}
43+
44+
let z = (f, true)
45+
46+
// switch f { | {}}
47+
// ^com
48+
49+
// switch f { | {fi}}
50+
// ^com
51+
52+
// switch z { | ({o}, _)}
53+
// ^com
54+
55+
// switch f { | {nest: {}}}
56+
// ^com

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

+90-3
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,22 @@ Completable: Cpattern Value[v]=f->tuple($2), tuple($0)
4444
"documentation": null
4545
}]
4646

47-
Complete src/CompletionPattern.res 21:16
48-
posCursor:[21:16] posNoWhite:[21:14] Found expr:[21:3->21:15]
49-
[]
47+
Complete src/CompletionPattern.res 21:15
48+
XXX Not found!
49+
Completable: Cpattern Value[x]
50+
[{
51+
"label": "true",
52+
"kind": 4,
53+
"tags": [],
54+
"detail": "bool",
55+
"documentation": null
56+
}, {
57+
"label": "false",
58+
"kind": 4,
59+
"tags": [],
60+
"detail": "bool",
61+
"documentation": null
62+
}]
5063

5164
Complete src/CompletionPattern.res 24:17
5265
looking for: Cpath Value[x]
@@ -61,3 +74,77 @@ Completable: Cpattern Value[x]=t
6174
"documentation": null
6275
}]
6376

77+
Complete src/CompletionPattern.res 45:17
78+
looking for: Cpath Value[f]
79+
posCursor:[45:17] posNoWhite:[45:16] Found expr:[45:3->45:19]
80+
posCursor:[45:17] posNoWhite:[45:16] Found pattern:[45:16->45:18]
81+
Completable: Cpattern Value[f]
82+
[{
83+
"label": "first",
84+
"kind": 5,
85+
"tags": [],
86+
"detail": "first: int\n\nsomeRecord",
87+
"documentation": null
88+
}, {
89+
"label": "second",
90+
"kind": 5,
91+
"tags": [],
92+
"detail": "second: (bool, option<someRecord>)\n\nsomeRecord",
93+
"documentation": null
94+
}, {
95+
"label": "optThird",
96+
"kind": 5,
97+
"tags": [],
98+
"detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord",
99+
"documentation": null
100+
}, {
101+
"label": "nest",
102+
"kind": 5,
103+
"tags": [],
104+
"detail": "nest: nestedRecord\n\nsomeRecord",
105+
"documentation": null
106+
}]
107+
108+
Complete src/CompletionPattern.res 48:19
109+
looking for: Cpath Value[f]
110+
posCursor:[48:19] posNoWhite:[48:18] Found expr:[48:3->48:21]
111+
posCursor:[48:19] posNoWhite:[48:18] Found pattern:[48:16->48:20]
112+
posCursor:[48:19] posNoWhite:[48:18] Found pattern:[48:17->48:19]
113+
Completable: Cpattern Value[f]=fi
114+
[{
115+
"label": "first",
116+
"kind": 5,
117+
"tags": [],
118+
"detail": "first: int\n\nsomeRecord",
119+
"documentation": null
120+
}]
121+
122+
Complete src/CompletionPattern.res 51:19
123+
looking for: Cpath Value[z]
124+
posCursor:[51:19] posNoWhite:[51:18] Found expr:[51:3->51:25]
125+
posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:16->51:24]
126+
posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:17->51:20]
127+
posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:18->51:19]
128+
Completable: Cpattern Value[z]=o->tuple($0)
129+
[{
130+
"label": "optThird",
131+
"kind": 5,
132+
"tags": [],
133+
"detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord",
134+
"documentation": null
135+
}]
136+
137+
Complete src/CompletionPattern.res 54:24
138+
looking for: Cpath Value[f]
139+
posCursor:[54:24] posNoWhite:[54:23] Found expr:[54:3->54:27]
140+
posCursor:[54:24] posNoWhite:[54:23] Found pattern:[54:16->54:26]
141+
posCursor:[54:24] posNoWhite:[54:23] Found pattern:[54:23->54:25]
142+
Completable: Cpattern Value[f]->recordField(nest)
143+
[{
144+
"label": "nested",
145+
"kind": 5,
146+
"tags": [],
147+
"detail": "nested: bool\n\nnestedRecord",
148+
"documentation": null
149+
}]
150+

0 commit comments

Comments
 (0)