Skip to content

Commit 48065b0

Browse files
committed
more pattern completion
1 parent 3415acc commit 48065b0

File tree

4 files changed

+111
-6
lines changed

4 files changed

+111
-6
lines changed

analysis/src/CompletionBackEndRevamped.ml

+34-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,34 @@ open SharedTypes
33
let resolveOpens = CompletionBackEnd.resolveOpens
44
let getOpens = CompletionBackEnd.getOpens
55

6-
let findFields ~env ~package ~hint typ =
6+
let findFields ~env ~package ~hint ?(seenFields = []) typ =
77
match TypeUtils.extractRecordType ~env ~package typ with
88
| None -> []
99
| Some (_recordEnv, fields, decl) ->
1010
fields
11-
|> DotCompletionUtils.filterRecordFields ~env ~prefix:hint ~exact:false
11+
|> DotCompletionUtils.filterRecordFields ~env ~prefix:hint ~seenFields
12+
~exact:false
1213
~recordAsString:(decl.item.decl |> Shared.declToString decl.name.txt)
1314

15+
let findRecordField ~env ~package ~fieldName typ =
16+
match TypeUtils.extractRecordType ~env ~package typ with
17+
| None -> None
18+
| Some (_recordEnv, fields, _decl) ->
19+
fields |> List.find_opt (fun (field : field) -> field.fname.txt = fieldName)
20+
21+
let completeEmptyPattern ~env ~package typ =
22+
match TypeUtils.extractType ~env ~package typ with
23+
| None -> []
24+
| Some (completionType, typeArgContext) -> (
25+
(* Fill this out with the different completions *)
26+
match completionType with
27+
| Trecord _ ->
28+
[
29+
Completion.create ?typeArgContext "{}" ~includesSnippets:true
30+
~insertText:"{$0}" ~sortText:"A" ~kind:(Value typ) ~env;
31+
]
32+
| _ -> [])
33+
1434
let processCompletable ~debug ~full ~scope ~env ~pos
1535
(completable : CompletableRevamped.t) =
1636
let package = full.package in
@@ -29,6 +49,18 @@ let processCompletable ~debug ~full ~scope ~env ~pos
2949
| Some typ -> (
3050
match kind with
3151
| Field {hint} -> findFields ~env ~package ~hint typ))
52+
| Cpattern {kind; typeLoc} -> (
53+
match TypeUtils.findTypeViaLoc typeLoc ~full ~debug with
54+
| None -> []
55+
| Some typ -> (
56+
match kind with
57+
| Empty -> completeEmptyPattern ~env ~package typ
58+
| Field {hint; seenFields} ->
59+
findFields ~env ~package ~hint ~seenFields typ
60+
| FieldValue {fieldName} -> (
61+
match findRecordField ~env ~package ~fieldName typ with
62+
| None -> []
63+
| Some field -> completeEmptyPattern ~env ~package field.typ)))
3264
| Cnone -> []
3365
| CextensionNode _ -> []
3466
| Cdecorator prefix ->

analysis/src/CompletionFrontEndRevamped.ml

+65
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ let completionWithParser ~currentFile ~debug ~offset ~path ~posCursor text =
4848

4949
let found = ref false in
5050
let result = ref None in
51+
let currentTypeLoc = ref None in
5152
let scope = ref (Scope.create ()) in
5253
let setResultOpt x =
5354
if !result = None then
@@ -403,6 +404,18 @@ let completionWithParser ~currentFile ~debug ~offset ~path ~posCursor text =
403404
if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite && !result = None then (
404405
setFound ();
405406
match expr.pexp_desc with
407+
| Pexp_match (switchExpr, [{pc_lhs = lhsPat}])
408+
when CompletionPatterns.isPatternHole lhsPat
409+
&& locHasCursor switchExpr.pexp_loc = false ->
410+
setResult (Cpattern {kind = Empty; typeLoc = switchExpr.pexp_loc})
411+
| Pexp_match (switchExpr, cases) ->
412+
let oldTypeLoc = !currentTypeLoc in
413+
currentTypeLoc := Some switchExpr.pexp_loc;
414+
cases
415+
|> List.iter (fun case ->
416+
Ast_iterator.default_iterator.case iterator case);
417+
currentTypeLoc := oldTypeLoc;
418+
processed := true
406419
| Pexp_extension ({txt = "obj"}, PStr [str_item]) ->
407420
Ast_iterator.default_iterator.structure_item iterator str_item
408421
| Pexp_extension ({txt}, _) -> setResult (CextensionNode txt)
@@ -614,6 +627,58 @@ let completionWithParser ~currentFile ~debug ~offset ~path ~posCursor text =
614627
(Pos.toString posCursor) (Pos.toString posNoWhite)
615628
(Loc.toString pat.ppat_loc);
616629
(match pat.ppat_desc with
630+
| Ppat_record ([], _) ->
631+
(* No fields means empty record body.*)
632+
setResult
633+
(Cpattern
634+
{kind = Field {hint = ""; seenFields = []}; typeLoc = pat.ppat_loc})
635+
| Ppat_record (fields, _) -> (
636+
let fieldWithCursor = ref None in
637+
let fieldWithPatHole = ref None in
638+
fields
639+
|> List.iter (fun (fname, f, _) ->
640+
match
641+
( fname.Location.txt,
642+
f.Parsetree.ppat_loc
643+
|> CursorPosition.classifyLoc ~pos:posBeforeCursor )
644+
with
645+
| Longident.Lident fname, HasCursor ->
646+
fieldWithCursor := Some (fname, f)
647+
| Lident fname, _ when CompletionPatterns.isPatternHole f ->
648+
fieldWithPatHole := Some (fname, f)
649+
| _ -> ());
650+
let seenFields =
651+
fields
652+
|> List.filter_map (fun (fieldName, _f, _) ->
653+
match fieldName with
654+
| {Location.txt = Longident.Lident fieldName} -> Some fieldName
655+
| _ -> None)
656+
in
657+
match (!fieldWithCursor, !fieldWithPatHole) with
658+
| Some (fname, f), _ | None, Some (fname, f) -> (
659+
match f.ppat_desc with
660+
| Ppat_extension ({txt = "rescript.patternhole"}, _) ->
661+
(* A pattern hole means for example `{someField: <com>}`. We want to complete for the type of `someField`. *)
662+
setResult
663+
(Cpattern
664+
{kind = FieldValue {fieldName = fname}; typeLoc = pat.ppat_loc})
665+
| Ppat_var {txt} ->
666+
(* A var means `{s}` or similar. Complete for fields. *)
667+
setResult
668+
(Cpattern
669+
{kind = Field {hint = txt; seenFields}; typeLoc = pat.ppat_loc})
670+
| _ -> ())
671+
| None, None -> (
672+
(* Figure out if we're completing for a new field.
673+
If the cursor is inside of the record body, but no field has the cursor,
674+
and there's no pattern hole. Check the first char to the left of the cursor,
675+
ignoring white space. If that's a comma, we assume you're completing for a new field. *)
676+
match firstCharBeforeCursorNoWhite with
677+
| Some ',' ->
678+
setResult
679+
(Cpattern
680+
{kind = Field {hint = ""; seenFields}; typeLoc = pat.ppat_loc})
681+
| _ -> ()))
617682
| Ppat_construct (lid, _) ->
618683
let lidPath = flattenLidCheckDot lid in
619684
if debug then

analysis/src/DotCompletionUtils.ml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
let filterRecordFields ~env ~recordAsString ~prefix ~exact fields =
1+
let filterRecordFields ~env ~recordAsString ~prefix ~exact ?(seenFields = [])
2+
fields =
23
fields
34
|> Utils.filterMap (fun (field : SharedTypes.field) ->
4-
if Utils.checkName field.fname.txt ~prefix ~exact then
5+
if List.mem field.fname.txt seenFields then None
6+
else if Utils.checkName field.fname.txt ~prefix ~exact then
57
Some
68
(SharedTypes.Completion.create field.fname.txt ~env
79
?deprecated:field.deprecated ~docstring:field.docstring

analysis/src/SharedTypes.ml

+8-2
Original file line numberDiff line numberDiff line change
@@ -775,14 +775,20 @@ module CompletableRevamped = struct
775775
| ModuleWithImportAttributes of {prefix: string}
776776
| JsxConfig of {prefix: string}
777777

778-
type completionKind = Field of {hint: string}
778+
type completionExprKind = Field of {hint: string}
779+
780+
type completionPatternKind =
781+
| Empty
782+
| Field of {hint: string; seenFields: string list}
783+
| FieldValue of {fieldName: string}
779784

780785
type t =
781786
| Cexpression of {
782-
kind: completionKind;
787+
kind: completionExprKind;
783788
typeLoc: Location.t;
784789
posOfDot: Pos.t option;
785790
}
791+
| Cpattern of {kind: completionPatternKind; typeLoc: Location.t}
786792
| Cnone
787793
| CextensionNode of string
788794
| Cdecorator of string

0 commit comments

Comments
 (0)