Skip to content

Commit aca764d

Browse files
committed
complete record bodies/fields in patterns
1 parent b3fccdb commit aca764d

File tree

5 files changed

+109
-52
lines changed

5 files changed

+109
-52
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,7 @@ let printConstructorArgs argsLen ~asSnippet =
15701570
else ""
15711571

15721572
let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
1573-
~expandOption ~includeLocalValues =
1573+
~expandOption ~includeLocalValues ~completionContext =
15741574
let namesUsed = Hashtbl.create 10 in
15751575
let rec completeTypedValueInner t ~env ~full ~prefix ~expandOption =
15761576
let items =
@@ -1645,13 +1645,21 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
16451645
~insertText:(printConstructorArgs numExprs ~asSnippet:true)
16461646
~kind:(Value typ) ~env ();
16471647
]
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
1648+
| Some (Trecord {env; fields; typeExpr}) -> (
1649+
match completionContext with
1650+
| Some Completable.RecordField ->
1651+
fields
1652+
|> List.map (fun (field : field) ->
1653+
Completion.create ~name:field.fname.txt
1654+
~kind:(Field (field, typeExpr |> Shared.typeToString))
1655+
~env)
1656+
|> filterItems ~prefix
1657+
| None ->
1658+
[
1659+
Completion.createWithSnippet ~name:"{}"
1660+
~insertText:(if !Cfg.supportsSnippets then "{$0}" else "{}")
1661+
~sortText:"a" ~kind:(Value typeExpr) ~env ();
1662+
])
16551663
| _ -> []
16561664
in
16571665
(* Include all values and modules in completion if there's a prefix, not otherwise *)
@@ -1742,20 +1750,22 @@ let getJsxLabels ~componentPath ~findTypeOfValue ~package =
17421750

17431751
let rec resolveNestedPattern typ ~env ~package ~nested =
17441752
match nested with
1745-
| [] -> Some (typ, env)
1753+
| [] -> Some (typ, env, None)
17461754
| patternPath :: nested -> (
17471755
match (patternPath, typ |> extractType ~env ~package) with
17481756
| Completable.PTupleItem {itemNum}, Some (Tuple (env, tupleItems, _)) -> (
17491757
match List.nth_opt tupleItems itemNum with
17501758
| None -> None
17511759
| Some typ -> typ |> resolveNestedPattern ~env ~package ~nested)
1752-
| Completable.PRecordField {fieldName}, Some (Trecord {env; fields}) -> (
1760+
| PFollowRecordField {fieldName}, Some (Trecord {env; fields}) -> (
17531761
match
17541762
fields
17551763
|> List.find_opt (fun (field : field) -> field.fname.txt = fieldName)
17561764
with
17571765
| None -> None
17581766
| Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested)
1767+
| PRecordBody _, Some (Trecord {env; typeExpr}) ->
1768+
Some (typeExpr, env, Some Completable.RecordField)
17591769
| _ -> None)
17601770

17611771
let processCompletable ~debug ~full ~scope ~env ~pos ~forHover
@@ -1821,7 +1831,7 @@ let processCompletable ~debug ~full ~scope ~env ~pos ~forHover
18211831
| Some (_, typ, env) ->
18221832
typ
18231833
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
1824-
~expandOption:true ~includeLocalValues:true)
1834+
~expandOption:true ~includeLocalValues:true ~completionContext:None)
18251835
| Cdecorator prefix ->
18261836
let mkDecorator (name, docstring) =
18271837
{(Completion.create ~name ~kind:(Label "") ~env) with docstring}
@@ -2086,11 +2096,11 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
20862096
| Some (Optional _, typ) ->
20872097
typ
20882098
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2089-
~expandOption:true ~includeLocalValues:true
2099+
~expandOption:true ~includeLocalValues:true ~completionContext:None
20902100
| Some ((Unlabelled _ | Labelled _), typ) ->
20912101
typ
20922102
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2093-
~expandOption:false ~includeLocalValues:true)
2103+
~expandOption:false ~includeLocalValues:true ~completionContext:None)
20942104
| CnamedArg (cp, prefix, identsSeen) ->
20952105
let labels =
20962106
match
@@ -2131,7 +2141,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
21312141
| Some (typ, env) ->
21322142
typ
21332143
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2134-
~expandOption:false ~includeLocalValues:false
2144+
~expandOption:false ~includeLocalValues:false ~completionContext:None
21352145
| None -> [])
21362146
| Cpattern {typ; prefix; nested = Some nested} -> (
21372147
let envWhereCompletionStarted = env in
@@ -2144,8 +2154,8 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
21442154
| Some (typ, env) -> (
21452155
match typ |> resolveNestedPattern ~env ~package:full.package ~nested with
21462156
| None -> []
2147-
| Some (typ, env) ->
2157+
| Some (typ, env, completionContext) ->
21482158
typ
21492159
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2150-
~expandOption:false ~includeLocalValues:false)
2160+
~expandOption:false ~includeLocalValues:false ~completionContext)
21512161
| None -> [])

analysis/src/CompletionFrontEnd.ml

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -497,34 +497,40 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
497497
| _, Some patHoleCount ->
498498
appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount})
499499
| _ -> ())
500-
| Ppat_record ([], _) -> commitFoundPat ~prefix:"" ()
500+
| Ppat_record ([], _) ->
501+
appendNestedPat (Completable.PRecordBody {seenFields = []});
502+
commitFoundPat ~prefix:"" ()
501503
| Ppat_record (fields, _) -> (
504+
(* TODO: Identify seen fields. *)
502505
let fieldWithCursor = ref None in
503506
let fieldWithPatHole = ref None in
504507
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)
512508
|> List.iter (fun (fname, f) ->
513509
match
514510
( fname.Location.txt,
515511
f.Parsetree.ppat_loc
516512
|> CursorPosition.classifyLoc ~pos:posBeforeCursor )
517513
with
518514
| Longident.Lident fname, HasCursor ->
519-
fieldWithCursor := Some fname
520-
| Lident fname, EmptyLoc when isPatternHole f ->
521-
fieldWithPatHole := Some fname
515+
fieldWithCursor := Some (fname, f)
516+
| Lident fname, _ when isPatternHole f ->
517+
fieldWithPatHole := Some (fname, f)
522518
| _ -> ());
523519
match (!fieldWithCursor, !fieldWithPatHole) with
524-
| Some fname, _ ->
525-
appendNestedPat (Completable.PRecordField {fieldName = fname})
526-
| None, Some fname ->
527-
appendNestedPat (Completable.PRecordField {fieldName = fname})
520+
| Some (fname, f), _ | None, Some (fname, f) -> (
521+
match f.ppat_desc with
522+
| Ppat_record _ | Ppat_construct _ | Ppat_variant _ | Ppat_tuple _ ->
523+
(* These are things we can continue into in the pattern. *)
524+
appendNestedPat (Completable.PFollowRecordField {fieldName = fname})
525+
| Ppat_extension ({txt = "rescript.patternhole"}, _) ->
526+
(* A pattern hole means for example `{someField: <com>}`. We want to complete for the type of `someField`. *)
527+
appendNestedPat (Completable.PFollowRecordField {fieldName = fname});
528+
commitFoundPat ~prefix:"" ()
529+
| Ppat_var {txt} ->
530+
(* A var means `{s}` or similar. Complete for fields. *)
531+
appendNestedPat (Completable.PRecordBody {seenFields = []});
532+
commitFoundPat ~prefix:txt ()
533+
| _ -> ())
528534
| _ -> ())
529535
| _ -> ()
530536
in

analysis/src/SharedTypes.ml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,14 +550,19 @@ module Completable = struct
550550
(** The loc item for the left hand side of the pipe. *)
551551
}
552552

553+
(** Additional context for a pattern completion where needed. *)
554+
type patternContext = RecordField
555+
553556
type patternPath =
554557
| PTupleItem of {itemNum: int}
555-
| PRecordField of {fieldName: string}
558+
| PFollowRecordField of {fieldName: string}
559+
| PRecordBody of {seenFields: string list}
556560

557561
let patternPathToString p =
558562
match p with
559563
| PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")"
560-
| PRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")"
564+
| PFollowRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")"
565+
| PRecordBody _ -> "recordBody"
561566

562567
type t =
563568
| Cdecorator of string (** e.g. @module *)

analysis/tests/src/CompletionPattern.res

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ let f: someRecord = {
4343

4444
let z = (f, true)
4545

46+
// switch f { | }
47+
// ^com
48+
4649
// switch f { | {}}
4750
// ^com
4851

@@ -52,5 +55,8 @@ let z = (f, true)
5255
// switch z { | ({o}, _)}
5356
// ^com
5457

58+
// switch f { | {nest: }}
59+
// ^com
60+
5561
// switch f { | {nest: {}}}
5662
// ^com

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

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,25 @@ Completable: Cpattern Value[x]=t
7474
"documentation": null
7575
}]
7676

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]
77+
Complete src/CompletionPattern.res 45:15
78+
XXX Not found!
8179
Completable: Cpattern Value[f]
80+
[{
81+
"label": "{}",
82+
"kind": 12,
83+
"tags": [],
84+
"detail": "someRecord",
85+
"documentation": null,
86+
"sortText": "a",
87+
"insertText": "{$0}",
88+
"insertTextFormat": 2
89+
}]
90+
91+
Complete src/CompletionPattern.res 48:17
92+
looking for: Cpath Value[f]
93+
posCursor:[48:17] posNoWhite:[48:16] Found expr:[48:3->48:19]
94+
posCursor:[48:17] posNoWhite:[48:16] Found pattern:[48:16->48:18]
95+
Completable: Cpattern Value[f]->recordBody
8296
[{
8397
"label": "first",
8498
"kind": 5,
@@ -105,12 +119,12 @@ Completable: Cpattern Value[f]
105119
"documentation": null
106120
}]
107121

108-
Complete src/CompletionPattern.res 48:19
122+
Complete src/CompletionPattern.res 51:19
109123
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
124+
posCursor:[51:19] posNoWhite:[51:18] Found expr:[51:3->51:21]
125+
posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:16->51:20]
126+
posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:17->51:19]
127+
Completable: Cpattern Value[f]=fi->recordBody
114128
[{
115129
"label": "first",
116130
"kind": 5,
@@ -119,13 +133,13 @@ Completable: Cpattern Value[f]=fi
119133
"documentation": null
120134
}]
121135

122-
Complete src/CompletionPattern.res 51:19
136+
Complete src/CompletionPattern.res 54:19
123137
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)
138+
posCursor:[54:19] posNoWhite:[54:18] Found expr:[54:3->54:25]
139+
posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:16->54:24]
140+
posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:17->54:20]
141+
posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:18->54:19]
142+
Completable: Cpattern Value[z]=o->tuple($0), recordBody
129143
[{
130144
"label": "optThird",
131145
"kind": 5,
@@ -134,12 +148,28 @@ Completable: Cpattern Value[z]=o->tuple($0)
134148
"documentation": null
135149
}]
136150

137-
Complete src/CompletionPattern.res 54:24
151+
Complete src/CompletionPattern.res 57:22
138152
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]
153+
posCursor:[57:22] posNoWhite:[57:21] Found expr:[57:3->57:25]
154+
posCursor:[57:22] posNoWhite:[57:21] Found pattern:[57:16->57:25]
142155
Completable: Cpattern Value[f]->recordField(nest)
156+
[{
157+
"label": "{}",
158+
"kind": 12,
159+
"tags": [],
160+
"detail": "nestedRecord",
161+
"documentation": null,
162+
"sortText": "a",
163+
"insertText": "{$0}",
164+
"insertTextFormat": 2
165+
}]
166+
167+
Complete src/CompletionPattern.res 60:24
168+
looking for: Cpath Value[f]
169+
posCursor:[60:24] posNoWhite:[60:23] Found expr:[60:3->60:27]
170+
posCursor:[60:24] posNoWhite:[60:23] Found pattern:[60:16->60:26]
171+
posCursor:[60:24] posNoWhite:[60:23] Found pattern:[60:23->60:25]
172+
Completable: Cpattern Value[f]->recordField(nest), recordBody
143173
[{
144174
"label": "nested",
145175
"kind": 5,

0 commit comments

Comments
 (0)