Skip to content

Commit a539143

Browse files
committed
handle completing for new record fields, and filtering already seen fields
1 parent aca764d commit a539143

File tree

5 files changed

+72
-23
lines changed

5 files changed

+72
-23
lines changed

analysis/src/CompletionBackEnd.ml

+5-3
Original file line numberDiff line numberDiff line change
@@ -1647,8 +1647,10 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
16471647
]
16481648
| Some (Trecord {env; fields; typeExpr}) -> (
16491649
match completionContext with
1650-
| Some Completable.RecordField ->
1650+
| Some (Completable.RecordField {seenFields}) ->
16511651
fields
1652+
|> List.filter (fun (field : field) ->
1653+
List.mem field.fname.txt seenFields = false)
16521654
|> List.map (fun (field : field) ->
16531655
Completion.create ~name:field.fname.txt
16541656
~kind:(Field (field, typeExpr |> Shared.typeToString))
@@ -1764,8 +1766,8 @@ let rec resolveNestedPattern typ ~env ~package ~nested =
17641766
with
17651767
| None -> None
17661768
| Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested)
1767-
| PRecordBody _, Some (Trecord {env; typeExpr}) ->
1768-
Some (typeExpr, env, Some Completable.RecordField)
1769+
| PRecordBody {seenFields}, Some (Trecord {env; typeExpr}) ->
1770+
Some (typeExpr, env, Some (Completable.RecordField {seenFields}))
17691771
| _ -> None)
17701772

17711773
let processCompletable ~debug ~full ~scope ~env ~pos ~forHover

analysis/src/CompletionFrontEnd.ml

+28-3
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,15 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
305305
let line, col = posCursor in
306306
(line, max 0 col - offset + offsetNoWhite)
307307
in
308+
(* Identifies the first character before the cursor that's not white space.
309+
Should be used very sparingly, but can be used to drive completion triggering
310+
in scenarios where the parser eats things we'd need to complete.
311+
Example: let {whatever, <cursor>}, char is ','. *)
312+
let firstCharBeforeCursorNoWhite =
313+
if offsetNoWhite < String.length text && offsetNoWhite >= 0 then
314+
Some text.[offsetNoWhite]
315+
else None
316+
in
308317
let posBeforeCursor = Pos.posBeforeCursor posCursor in
309318
let charBeforeCursor, blankAfterCursor =
310319
match Pos.positionToOffset text posCursor with
@@ -498,10 +507,10 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
498507
appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount})
499508
| _ -> ())
500509
| Ppat_record ([], _) ->
510+
(* Empty fields means we're in a record body `{}`. Complete for the fields. *)
501511
appendNestedPat (Completable.PRecordBody {seenFields = []});
502512
commitFoundPat ~prefix:"" ()
503513
| Ppat_record (fields, _) -> (
504-
(* TODO: Identify seen fields. *)
505514
let fieldWithCursor = ref None in
506515
let fieldWithPatHole = ref None in
507516
fields
@@ -516,6 +525,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
516525
| Lident fname, _ when isPatternHole f ->
517526
fieldWithPatHole := Some (fname, f)
518527
| _ -> ());
528+
let seenFields =
529+
fields
530+
|> List.filter_map (fun (fieldName, _f) ->
531+
match fieldName with
532+
| {Location.txt = Longident.Lident fieldName} -> Some fieldName
533+
| _ -> None)
534+
in
519535
match (!fieldWithCursor, !fieldWithPatHole) with
520536
| Some (fname, f), _ | None, Some (fname, f) -> (
521537
match f.ppat_desc with
@@ -528,10 +544,19 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
528544
commitFoundPat ~prefix:"" ()
529545
| Ppat_var {txt} ->
530546
(* A var means `{s}` or similar. Complete for fields. *)
531-
appendNestedPat (Completable.PRecordBody {seenFields = []});
547+
appendNestedPat (Completable.PRecordBody {seenFields});
532548
commitFoundPat ~prefix:txt ()
533549
| _ -> ())
534-
| _ -> ())
550+
| None, None -> (
551+
(* Figure out if we're completing for a new field.
552+
If the cursor is inside of the record body, but no field has the cursor,
553+
and there's no pattern hole. Check the first char to the left of the cursor,
554+
ignoring white space. If that's a comma, we assume you're completing for a new field. *)
555+
match firstCharBeforeCursorNoWhite with
556+
| Some ',' ->
557+
appendNestedPat (Completable.PRecordBody {seenFields});
558+
commitFoundPat ~prefix:"" ()
559+
| _ -> ()))
535560
| _ -> ()
536561
in
537562
let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) =

analysis/src/SharedTypes.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ module Completable = struct
551551
}
552552

553553
(** Additional context for a pattern completion where needed. *)
554-
type patternContext = RecordField
554+
type patternContext = RecordField of {seenFields: string list}
555555

556556
type patternPath =
557557
| PTupleItem of {itemNum: int}

analysis/tests/src/CompletionPattern.res

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ let z = (f, true)
4949
// switch f { | {}}
5050
// ^com
5151

52+
// switch f { | {first, , second }}
53+
// ^com
54+
5255
// switch f { | {fi}}
5356
// ^com
5457

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

+35-16
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,30 @@ Completable: Cpattern Value[f]->recordBody
119119
"documentation": null
120120
}]
121121

122-
Complete src/CompletionPattern.res 51:19
122+
Complete src/CompletionPattern.res 51:24
123123
looking for: Cpath Value[f]
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]
124+
posCursor:[51:24] posNoWhite:[51:22] Found expr:[51:3->51:36]
125+
posCursor:[51:24] posNoWhite:[51:22] Found pattern:[51:16->51:35]
126+
Completable: Cpattern Value[f]->recordBody
127+
[{
128+
"label": "optThird",
129+
"kind": 5,
130+
"tags": [],
131+
"detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord",
132+
"documentation": null
133+
}, {
134+
"label": "nest",
135+
"kind": 5,
136+
"tags": [],
137+
"detail": "nest: nestedRecord\n\nsomeRecord",
138+
"documentation": null
139+
}]
140+
141+
Complete src/CompletionPattern.res 54:19
142+
looking for: Cpath Value[f]
143+
posCursor:[54:19] posNoWhite:[54:18] Found expr:[54:3->54:21]
144+
posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:16->54:20]
145+
posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:17->54:19]
127146
Completable: Cpattern Value[f]=fi->recordBody
128147
[{
129148
"label": "first",
@@ -133,12 +152,12 @@ Completable: Cpattern Value[f]=fi->recordBody
133152
"documentation": null
134153
}]
135154

136-
Complete src/CompletionPattern.res 54:19
155+
Complete src/CompletionPattern.res 57:19
137156
looking for: Cpath Value[z]
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]
157+
posCursor:[57:19] posNoWhite:[57:18] Found expr:[57:3->57:25]
158+
posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:16->57:24]
159+
posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:17->57:20]
160+
posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:18->57:19]
142161
Completable: Cpattern Value[z]=o->tuple($0), recordBody
143162
[{
144163
"label": "optThird",
@@ -148,10 +167,10 @@ Completable: Cpattern Value[z]=o->tuple($0), recordBody
148167
"documentation": null
149168
}]
150169

151-
Complete src/CompletionPattern.res 57:22
170+
Complete src/CompletionPattern.res 60:22
152171
looking for: Cpath Value[f]
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]
172+
posCursor:[60:22] posNoWhite:[60:21] Found expr:[60:3->60:25]
173+
posCursor:[60:22] posNoWhite:[60:21] Found pattern:[60:16->60:25]
155174
Completable: Cpattern Value[f]->recordField(nest)
156175
[{
157176
"label": "{}",
@@ -164,11 +183,11 @@ Completable: Cpattern Value[f]->recordField(nest)
164183
"insertTextFormat": 2
165184
}]
166185

167-
Complete src/CompletionPattern.res 60:24
186+
Complete src/CompletionPattern.res 63:24
168187
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]
188+
posCursor:[63:24] posNoWhite:[63:23] Found expr:[63:3->63:27]
189+
posCursor:[63:24] posNoWhite:[63:23] Found pattern:[63:16->63:26]
190+
posCursor:[63:24] posNoWhite:[63:23] Found pattern:[63:23->63:25]
172191
Completable: Cpattern Value[f]->recordField(nest), recordBody
173192
[{
174193
"label": "nested",

0 commit comments

Comments
 (0)