@@ -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
0 commit comments