@@ -397,6 +397,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
397
397
398
398
let lookingForPat = ref None in
399
399
400
+ let locHasCursor = CursorPosition. locHasCursor ~pos: posBeforeCursor in
401
+ let locIsEmpty = CursorPosition. locIsEmpty ~pos: posBeforeCursor in
402
+
400
403
let rec traverseTupleItems tupleItems ~nextPatternPath ~resultFromFoundItemNum
401
404
=
402
405
let itemNum = ref (- 1 ) in
@@ -418,219 +421,200 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
418
421
if ! posNum > - 1 then Some (" " , resultFromFoundItemNum ! posNum) else None
419
422
| v , _ -> v
420
423
and traversePattern (pat : Parsetree.pattern ) ~patternPath =
421
- if
422
- pat.ppat_loc
423
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
424
- = HasCursor
425
- then
426
- match pat.ppat_desc with
427
- | Ppat_any | Ppat_constant _ | Ppat_interval _ -> None
428
- | Ppat_lazy p
429
- | Ppat_constraint (p, _)
430
- | Ppat_alias (p, _)
431
- | Ppat_exception p
432
- | Ppat_open (_ , p ) ->
433
- p |> traversePattern ~pattern Path
434
- | Ppat_or (p1 , p2 ) ->
424
+ let someIfHasCursor v =
425
+ if locHasCursor pat.Parsetree. ppat_loc then Some v else None
426
+ in
427
+ match pat.ppat_desc with
428
+ | Ppat_any | Ppat_constant _ | Ppat_interval _ -> None
429
+ | Ppat_lazy p
430
+ | Ppat_constraint (p, _)
431
+ | Ppat_alias (p, _)
432
+ | Ppat_exception p
433
+ | Ppat_open (_ , p ) ->
434
+ p |> traversePattern ~pattern Path
435
+ | Ppat_or (p1 , p2 ) -> (
436
+ let orPatWithItem =
435
437
[p1; p2] |> List. find_map (fun p -> p |> traversePattern ~pattern Path)
436
- | Ppat_var {txt} -> Some (txt, patternPath)
437
- | Ppat_construct ({txt = Lident "()" } , None) ->
438
- (* switch s { | (<com>) }*)
439
- Some (" " , patternPath @ [Completable. PTupleItem {itemNum = 0 }])
440
- | Ppat_construct ({txt = Lident prefix } , None) ->
441
- Some (prefix, patternPath)
442
- | Ppat_variant (prefix , None) -> Some (" #" ^ prefix, patternPath)
443
- | Ppat_array arrayPatterns ->
444
- let nextPatternPath = [Completable. PArray ] @ patternPath in
445
- if List. length arrayPatterns = 0 then Some (" " , nextPatternPath)
446
- else
447
- arrayPatterns
448
- |> List. find_map (fun pat ->
449
- pat |> traversePattern ~pattern Path:nextPatternPath)
450
- | Ppat_tuple tupleItems ->
451
- tupleItems
452
- |> traverseTupleItems
453
- ~next PatternPath:(fun itemNum ->
454
- [Completable. PTupleItem {itemNum}] @ patternPath)
455
- ~result FromFoundItemNum:(fun itemNum ->
456
- [Completable. PTupleItem {itemNum = itemNum + 1 }] @ patternPath)
457
- | Ppat_record ([] , _ ) ->
458
- (* Empty fields means we're in a record body `{}`. Complete for the fields. *)
459
- Some (" " , [Completable. PRecordBody {seenFields = [] }] @ patternPath)
460
- | Ppat_record (fields , _ ) -> (
461
- let fieldWithCursor = ref None in
462
- let fieldWithPatHole = ref None in
438
+ in
439
+ match orPatWithItem with
440
+ | None when isPatternHole p1 || isPatternHole p2 -> Some (" " , patternPath)
441
+ | v -> v)
442
+ | Ppat_var {txt} -> someIfHasCursor (txt, patternPath)
443
+ | Ppat_construct ({txt = Lident "()" } , None) ->
444
+ (* switch s { | (<com>) }*)
445
+ someIfHasCursor (" " , patternPath @ [Completable. PTupleItem {itemNum = 0 }])
446
+ | Ppat_construct ({txt = Lident prefix } , None) ->
447
+ someIfHasCursor (prefix, patternPath)
448
+ | Ppat_variant (prefix , None) -> someIfHasCursor (" #" ^ prefix, patternPath)
449
+ | Ppat_array arrayPatterns ->
450
+ let nextPatternPath = [Completable. PArray ] @ patternPath in
451
+ if List. length arrayPatterns = 0 && locHasCursor pat.ppat_loc then
452
+ Some (" " , nextPatternPath)
453
+ else
454
+ arrayPatterns
455
+ |> List. find_map (fun pat ->
456
+ pat |> traversePattern ~pattern Path:nextPatternPath)
457
+ | Ppat_tuple tupleItems when locHasCursor pat.ppat_loc ->
458
+ tupleItems
459
+ |> traverseTupleItems
460
+ ~next PatternPath:(fun itemNum ->
461
+ [Completable. PTupleItem {itemNum}] @ patternPath)
462
+ ~result FromFoundItemNum:(fun itemNum ->
463
+ [Completable. PTupleItem {itemNum = itemNum + 1 }] @ patternPath)
464
+ | Ppat_record ([] , _ ) ->
465
+ (* Empty fields means we're in a record body `{}`. Complete for the fields. *)
466
+ someIfHasCursor
467
+ (" " , [Completable. PRecordBody {seenFields = [] }] @ patternPath)
468
+ | Ppat_record (fields , _ ) -> (
469
+ let fieldWithCursor = ref None in
470
+ let fieldWithPatHole = ref None in
471
+ fields
472
+ |> List. iter (fun (fname , f ) ->
473
+ match
474
+ ( fname.Location. txt,
475
+ f.Parsetree. ppat_loc
476
+ |> CursorPosition. classifyLoc ~pos: posBeforeCursor )
477
+ with
478
+ | Longident. Lident fname , HasCursor ->
479
+ fieldWithCursor := Some (fname, f)
480
+ | Lident fname , _ when isPatternHole f ->
481
+ fieldWithPatHole := Some (fname, f)
482
+ | _ -> () );
483
+ let seenFields =
463
484
fields
464
- |> List. iter (fun (fname , f ) ->
465
- match
466
- ( fname.Location. txt,
467
- f.Parsetree. ppat_loc
468
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor )
469
- with
470
- | Longident. Lident fname , HasCursor ->
471
- fieldWithCursor := Some (fname, f)
472
- | Lident fname , _ when isPatternHole f ->
473
- fieldWithPatHole := Some (fname, f)
474
- | _ -> () );
475
- let seenFields =
476
- fields
477
- |> List. filter_map (fun (fieldName , _f ) ->
478
- match fieldName with
479
- | {Location. txt = Longident. Lident fieldName } -> Some fieldName
480
- | _ -> None )
481
- in
482
- match (! fieldWithCursor, ! fieldWithPatHole) with
483
- | Some (fname , f ), _ | None , Some (fname , f ) -> (
484
- match f.ppat_desc with
485
- | Ppat_record _ | Ppat_construct _ | Ppat_variant _ | Ppat_tuple _ ->
486
- (* These are things we can continue into in the pattern. *)
487
- f
488
- |> traversePattern
489
- ~pattern Path:
490
- ([Completable. PFollowRecordField {fieldName = fname}]
491
- @ patternPath)
492
- | Ppat_extension ({txt = "rescript.patternhole" } , _ ) ->
493
- (* A pattern hole means for example `{someField: <com>}`. We want to complete for the type of `someField`. *)
494
- Some
495
- ( " " ,
496
- [Completable. PFollowRecordField {fieldName = fname}]
497
- @ patternPath )
498
- | Ppat_var {txt} ->
499
- (* A var means `{s}` or similar. Complete for fields. *)
500
- Some (txt, [Completable. PRecordBody {seenFields}] @ patternPath)
501
- | _ -> None )
502
- | None , None -> (
503
- (* Figure out if we're completing for a new field.
504
- If the cursor is inside of the record body, but no field has the cursor,
505
- and there's no pattern hole. Check the first char to the left of the cursor,
506
- ignoring white space. If that's a comma, we assume you're completing for a new field. *)
507
- match firstCharBeforeCursorNoWhite with
508
- | Some ',' ->
509
- Some (" " , [Completable. PRecordBody {seenFields}] @ patternPath)
510
- | _ -> None ))
511
- | Ppat_construct
512
- ( {txt},
513
- Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident " ()" }, _)}
514
- )
515
- when ppat_loc
516
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
517
- = HasCursor ->
518
- (* Empty payload with cursor, like: Test(<com>) *)
519
- Some
520
- ( " " ,
521
- [
522
- Completable. PVariantPayload
523
- {constructorName = getUnqualifiedName txt; itemNum = 0 };
524
- ]
525
- @ patternPath )
526
- | Ppat_construct ({txt}, Some pat)
527
- when posBeforeCursor > = (pat.ppat_loc |> Loc. end_)
528
- && firstCharBeforeCursorNoWhite = Some ','
529
- && isPatternTuple pat = false ->
530
- (* Empty payload with trailing ',', like: Test(true, <com>) *)
531
- Some
532
- ( " " ,
533
- [
534
- Completable. PVariantPayload
535
- {constructorName = getUnqualifiedName txt; itemNum = 1 };
536
- ]
537
- @ patternPath )
538
- | Ppat_construct ({txt}, Some pat)
539
- when pat.ppat_loc
540
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
541
- = HasCursor
542
- && isPatternTuple pat = false ->
543
- (* Single payload *)
544
- pat
545
- |> traversePattern
546
- ~pattern Path:
547
- ([
548
- Completable. PVariantPayload
549
- {constructorName = getUnqualifiedName txt; itemNum = 0 };
550
- ]
551
- @ patternPath)
552
- | Ppat_construct
553
- ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems})
554
- when ppat_loc
555
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
556
- = HasCursor ->
557
- tupleItems
558
- |> traverseTupleItems
559
- ~next PatternPath:(fun itemNum ->
560
- [
561
- Completable. PVariantPayload
562
- {constructorName = getUnqualifiedName txt; itemNum};
563
- ]
564
- @ patternPath)
565
- ~result FromFoundItemNum:(fun itemNum ->
566
- [
567
- Completable. PVariantPayload
568
- {
569
- constructorName = getUnqualifiedName txt;
570
- itemNum = itemNum + 1 ;
571
- };
572
- ]
573
- @ patternPath)
574
- | Ppat_variant
575
- ( txt,
576
- Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident " ()" }, _)}
577
- )
578
- when ppat_loc
579
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
580
- = HasCursor ->
581
- (* Empty payload with cursor, like: #test(<com>) *)
582
- Some
583
- ( " " ,
584
- [
585
- Completable. PPolyvariantPayload
586
- {constructorName = txt; itemNum = 0 };
587
- ]
588
- @ patternPath )
589
- | Ppat_variant (txt, Some pat)
590
- when posBeforeCursor > = (pat.ppat_loc |> Loc. end_)
591
- && firstCharBeforeCursorNoWhite = Some ','
592
- && isPatternTuple pat = false ->
593
- (* Empty payload with trailing ',', like: #test(true, <com>) *)
594
- Some
595
- ( " " ,
596
- [
597
- Completable. PPolyvariantPayload
598
- {constructorName = txt; itemNum = 1 };
599
- ]
600
- @ patternPath )
601
- | Ppat_variant (txt, Some pat)
602
- when pat.ppat_loc
603
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
604
- = HasCursor
605
- && isPatternTuple pat = false ->
606
- (* Single payload *)
607
- pat
608
- |> traversePattern
609
- ~pattern Path:
610
- ([
611
- Completable. PPolyvariantPayload
612
- {constructorName = txt; itemNum = 0 };
613
- ]
614
- @ patternPath)
615
- | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems})
616
- when ppat_loc
617
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
618
- = HasCursor ->
619
- tupleItems
620
- |> traverseTupleItems
621
- ~next PatternPath:(fun itemNum ->
622
- [
623
- Completable. PPolyvariantPayload {constructorName = txt; itemNum};
624
- ]
625
- @ patternPath)
626
- ~result FromFoundItemNum:(fun itemNum ->
627
- [
628
- Completable. PPolyvariantPayload
629
- {constructorName = txt; itemNum = itemNum + 1 };
630
- ]
631
- @ patternPath)
632
- | _ -> None
633
- else None
485
+ |> List. filter_map (fun (fieldName , _f ) ->
486
+ match fieldName with
487
+ | {Location. txt = Longident. Lident fieldName } -> Some fieldName
488
+ | _ -> None )
489
+ in
490
+ match (! fieldWithCursor, ! fieldWithPatHole) with
491
+ | Some (fname , f ), _ | None , Some (fname , f ) -> (
492
+ match f.ppat_desc with
493
+ | Ppat_extension ({txt = "rescript.patternhole" } , _ ) ->
494
+ (* A pattern hole means for example `{someField: <com>}`. We want to complete for the type of `someField`. *)
495
+ someIfHasCursor
496
+ ( " " ,
497
+ [Completable. PFollowRecordField {fieldName = fname}] @ patternPath
498
+ )
499
+ | Ppat_var {txt} ->
500
+ (* A var means `{s}` or similar. Complete for fields. *)
501
+ someIfHasCursor
502
+ (txt, [Completable. PRecordBody {seenFields}] @ patternPath)
503
+ | _ ->
504
+ f
505
+ |> traversePattern
506
+ ~pattern Path:
507
+ ([Completable. PFollowRecordField {fieldName = fname}]
508
+ @ patternPath))
509
+ | None , None -> (
510
+ (* Figure out if we're completing for a new field.
511
+ If the cursor is inside of the record body, but no field has the cursor,
512
+ and there's no pattern hole. Check the first char to the left of the cursor,
513
+ ignoring white space. If that's a comma, we assume you're completing for a new field. *)
514
+ match firstCharBeforeCursorNoWhite with
515
+ | Some ',' ->
516
+ someIfHasCursor
517
+ (" " , [Completable. PRecordBody {seenFields}] @ patternPath)
518
+ | _ -> None ))
519
+ | Ppat_construct
520
+ ( {txt},
521
+ Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident " ()" }, _)}
522
+ )
523
+ when locHasCursor ppat_loc ->
524
+ (* Empty payload with cursor, like: Test(<com>) *)
525
+ Some
526
+ ( " " ,
527
+ [
528
+ Completable. PVariantPayload
529
+ {constructorName = getUnqualifiedName txt; itemNum = 0 };
530
+ ]
531
+ @ patternPath )
532
+ | Ppat_construct ({txt}, Some pat)
533
+ when posBeforeCursor > = (pat.ppat_loc |> Loc. end_)
534
+ && firstCharBeforeCursorNoWhite = Some ','
535
+ && isPatternTuple pat = false ->
536
+ (* Empty payload with trailing ',', like: Test(true, <com>) *)
537
+ Some
538
+ ( " " ,
539
+ [
540
+ Completable. PVariantPayload
541
+ {constructorName = getUnqualifiedName txt; itemNum = 1 };
542
+ ]
543
+ @ patternPath )
544
+ | Ppat_construct ({txt}, Some pat)
545
+ when locHasCursor pat.ppat_loc && isPatternTuple pat = false ->
546
+ (* Single payload *)
547
+ pat
548
+ |> traversePattern
549
+ ~pattern Path:
550
+ ([
551
+ Completable. PVariantPayload
552
+ {constructorName = getUnqualifiedName txt; itemNum = 0 };
553
+ ]
554
+ @ patternPath)
555
+ | Ppat_construct ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems})
556
+ when locHasCursor ppat_loc ->
557
+ tupleItems
558
+ |> traverseTupleItems
559
+ ~next PatternPath:(fun itemNum ->
560
+ [
561
+ Completable. PVariantPayload
562
+ {constructorName = getUnqualifiedName txt; itemNum};
563
+ ]
564
+ @ patternPath)
565
+ ~result FromFoundItemNum:(fun itemNum ->
566
+ [
567
+ Completable. PVariantPayload
568
+ {
569
+ constructorName = getUnqualifiedName txt;
570
+ itemNum = itemNum + 1 ;
571
+ };
572
+ ]
573
+ @ patternPath)
574
+ | Ppat_variant
575
+ ( txt,
576
+ Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident " ()" }, _)}
577
+ )
578
+ when locHasCursor ppat_loc ->
579
+ (* Empty payload with cursor, like: #test(<com>) *)
580
+ Some
581
+ ( " " ,
582
+ [Completable. PPolyvariantPayload {constructorName = txt; itemNum = 0 }]
583
+ @ patternPath )
584
+ | Ppat_variant (txt, Some pat)
585
+ when posBeforeCursor > = (pat.ppat_loc |> Loc. end_)
586
+ && firstCharBeforeCursorNoWhite = Some ','
587
+ && isPatternTuple pat = false ->
588
+ (* Empty payload with trailing ',', like: #test(true, <com>) *)
589
+ Some
590
+ ( " " ,
591
+ [Completable. PPolyvariantPayload {constructorName = txt; itemNum = 1 }]
592
+ @ patternPath )
593
+ | Ppat_variant (txt, Some pat)
594
+ when locHasCursor pat.ppat_loc && isPatternTuple pat = false ->
595
+ (* Single payload *)
596
+ pat
597
+ |> traversePattern
598
+ ~pattern Path:
599
+ ([
600
+ Completable. PPolyvariantPayload
601
+ {constructorName = txt; itemNum = 0 };
602
+ ]
603
+ @ patternPath)
604
+ | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems})
605
+ when locHasCursor ppat_loc ->
606
+ tupleItems
607
+ |> traverseTupleItems
608
+ ~next PatternPath:(fun itemNum ->
609
+ [Completable. PPolyvariantPayload {constructorName = txt; itemNum}]
610
+ @ patternPath)
611
+ ~result FromFoundItemNum:(fun itemNum ->
612
+ [
613
+ Completable. PPolyvariantPayload
614
+ {constructorName = txt; itemNum = itemNum + 1 };
615
+ ]
616
+ @ patternPath)
617
+ | _ -> None
634
618
in
635
619
let completePattern (pat : Parsetree.pattern ) =
636
620
match (pat |> traversePattern ~pattern Path:[] , ! lookingForPat) with
@@ -714,17 +698,16 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
714
698
let hasCaseWithCursor =
715
699
cases
716
700
|> List. find_opt (fun case ->
717
- case.Parsetree. pc_lhs.ppat_loc
718
- |> CursorPosition. classifyLoc ~pos: posBeforeCursor
719
- = HasCursor )
701
+ locHasCursor case.Parsetree. pc_lhs.ppat_loc)
720
702
|> Option. is_some
721
703
in
722
- let hasCaseWithPatHole =
704
+ let hasCaseWithEmptyLoc =
723
705
cases
724
- |> List. find_opt (fun case -> isPatternHole case.Parsetree. pc_lhs)
706
+ |> List. find_opt (fun case ->
707
+ locIsEmpty case.Parsetree. pc_lhs.ppat_loc)
725
708
|> Option. is_some
726
709
in
727
- match (hasCaseWithPatHole , hasCaseWithCursor) with
710
+ match (hasCaseWithEmptyLoc , hasCaseWithCursor) with
728
711
| _ , true ->
729
712
(* Always continue if there's a case with the cursor *)
730
713
setLookingForPat ctxPath
0 commit comments