@@ -65,7 +65,9 @@ type ctxPath =
65
65
| CId of string list * completionCategory
66
66
(* * A regular id of an expected category. `let fff = thisIsAnId<com>` and `let fff = SomePath.alsoAnId<com>` *)
67
67
| CVariantPayload of {itemNum : int }
68
- (* * A variant payload. `Some(<com>)` = itemNum 0, `Whatever(true, f<com>)` = itemNum 1*)
68
+ (* * A variant payload. `Some(<com>)` = itemNum 0, `Whatever(true, f<com>)` = itemNum 1 *)
69
+ | CTupleItem of {itemNum : int }
70
+ (* * A tuple item. `(true, false, <com>)` = itemNum 2 *)
69
71
| CRecordField of {seenFields : string list ; prefix : string }
70
72
(* * A record field. `let f = {on: true, s<com>}` seenFields = [on], prefix = "s",*)
71
73
| COption of ctxPath (* * An option with an inner type. *)
@@ -120,6 +122,7 @@ let rec ctxPathToString (ctxPath : ctxPath) =
120
122
| Field -> " Field" )
121
123
(ident prefix)
122
124
| CVariantPayload {itemNum} -> Printf. sprintf " CVariantPayload($%i)" itemNum
125
+ | CTupleItem {itemNum} -> Printf. sprintf " CTupleItem($%i)" itemNum
123
126
| CRecordField {prefix} -> Printf. sprintf " CRecordField=%s" prefix
124
127
| COption ctxPath -> Printf. sprintf " COption<%s>" (ctxPathToString ctxPath)
125
128
| CArray ctxPath ->
@@ -882,6 +885,22 @@ and completePattern ~(completionContext : CompletionContext.t)
882
885
(* Can just continue into these patterns. *)
883
886
if locHasPos pat.ppat_loc then p |> completePattern ~completion Context
884
887
else None
888
+ | Ppat_or (p1 , p2 ) -> (
889
+ (* Try to complete each `or` pattern *)
890
+ let orPatCompleted =
891
+ [p1; p2]
892
+ |> List. find_map (fun p ->
893
+ if locHasPos p.Parsetree. ppat_loc then
894
+ completePattern ~completion Context p
895
+ else None )
896
+ in
897
+ match orPatCompleted with
898
+ | None
899
+ when CompletionPatterns. isPatternHole p1
900
+ || CompletionPatterns. isPatternHole p2 ->
901
+ (* TODO(1) explain this *)
902
+ CompletionResult. pattern ~completion Context ~prefix: " "
903
+ | res -> res)
885
904
| Ppat_var {txt; loc} ->
886
905
(* A variable, like `{ someThing: someV<com>}*)
887
906
if locHasPos loc then
@@ -897,7 +916,7 @@ and completePattern ~(completionContext : CompletionContext.t)
897
916
in
898
917
CompletionResult. pattern ~completion Context ~prefix: " "
899
918
else None
900
- | Ppat_record (fields , _ ) when locHasPos pat.ppat_loc -> (
919
+ | Ppat_record (fields , _ ) -> (
901
920
(* Record body with fields, where we know the cursor is inside of the record body somewhere. *)
902
921
let seenFields =
903
922
fields
@@ -938,22 +957,106 @@ and completePattern ~(completionContext : CompletionContext.t)
938
957
in
939
958
completePattern ~completion Context fieldPattern
940
959
| None , None ->
941
- (* We know the cursor is here, but it's not in a field name nor a field pattern.
942
- Check empty field patterns. *)
943
- None )
944
- | Ppat_any | Ppat_tuple _
945
- | Ppat_construct ({loc = {loc_start = _; loc_end = _; _}; _}, _)
960
+ if locHasPos pat.ppat_loc then
961
+ (* We know the cursor is here, but it's not in a field name nor a field pattern.
962
+ Check empty field patterns. TODO(1) *)
963
+ None
964
+ else None )
965
+ | Ppat_tuple tupleItems -> (
966
+ let tupleItemWithCursor =
967
+ tupleItems
968
+ |> Utils. findMapWithIndex (fun index (tupleItem : Parsetree.pattern ) ->
969
+ if locHasPos tupleItem.ppat_loc then Some (index, tupleItem)
970
+ else None )
971
+ in
972
+ match tupleItemWithCursor with
973
+ | Some (itemNum , tupleItem ) ->
974
+ let completionContext =
975
+ completionContext
976
+ |> CompletionContext. addCtxPathItem (CTupleItem {itemNum})
977
+ in
978
+ completePattern ~completion Context tupleItem
979
+ | None ->
980
+ if locHasPos pat.ppat_loc then
981
+ (* We found no tuple item with the cursor, but we know the cursor is in the
982
+ pattern. Check if the user is trying to complete an empty tuple item *)
983
+ match completionContext.positionContext.charBeforeNoWhitespace with
984
+ | Some ',' ->
985
+ (* `(true, false, <com>)` itemNum = 2, or `(true, <com>, false)` itemNum = 1 *)
986
+ (* Figure out which tuple item is active. *)
987
+ let itemNum = ref (- 1 ) in
988
+ tupleItems
989
+ |> List. iteri (fun index (pat : Parsetree.pattern ) ->
990
+ if
991
+ completionContext.positionContext.beforeCursor
992
+ > = Loc. start pat.ppat_loc
993
+ then itemNum := index);
994
+ if ! itemNum > - 1 then
995
+ let completionContext =
996
+ completionContext
997
+ |> CompletionContext. addCtxPathItem
998
+ (CTupleItem {itemNum = ! itemNum + 1 })
999
+ in
1000
+ CompletionResult. pattern ~completion Context ~prefix: " "
1001
+ else None
1002
+ | Some '(' ->
1003
+ (* TODO: This should work (start of tuple), but the parser is broken for this case:
1004
+ let (<com> , true) = someRecordVar. If we fix that completing in the first position
1005
+ could work too. *)
1006
+ let completionContext =
1007
+ completionContext
1008
+ |> CompletionContext. addCtxPathItem (CTupleItem {itemNum = 0 })
1009
+ in
1010
+ CompletionResult. pattern ~completion Context ~prefix: " "
1011
+ | _ -> None
1012
+ else None )
1013
+ | Ppat_array items ->
1014
+ if locHasPos pat.ppat_loc then
1015
+ if List. length items = 0 then
1016
+ (* {someArr: [<com>]} *)
1017
+ let completionContext =
1018
+ completionContext |> CompletionContext. addCtxPathItem (CArray None )
1019
+ in
1020
+ CompletionResult. pattern ~completion Context ~prefix: " "
1021
+ else
1022
+ let arrayItemWithCursor =
1023
+ items
1024
+ |> List. find_opt (fun (item : Parsetree.pattern ) ->
1025
+ locHasPos item.ppat_loc)
1026
+ in
1027
+ match
1028
+ ( arrayItemWithCursor,
1029
+ completionContext.positionContext.charBeforeNoWhitespace )
1030
+ with
1031
+ | Some item , _ ->
1032
+ (* Found an array item with the cursor. *)
1033
+ let completionContext =
1034
+ completionContext |> CompletionContext. addCtxPathItem (CArray None )
1035
+ in
1036
+ completePattern ~completion Context item
1037
+ | None , Some ',' ->
1038
+ (* No array item with the cursor, but we know the cursor is in the pattern.
1039
+ Check for "," which would signify the user is looking to add another
1040
+ array item to the pattern. *)
1041
+ let completionContext =
1042
+ completionContext |> CompletionContext. addCtxPathItem (CArray None )
1043
+ in
1044
+ CompletionResult. pattern ~completion Context ~prefix: " "
1045
+ | _ -> None
1046
+ else None
1047
+ | Ppat_any ->
1048
+ (* We treat any `_` as an empty completion. This is mainly because we're
1049
+ inserting `_` in snippets and automatically put the cursor there. So
1050
+ letting it trigger an empty completion improves the ergonomics by a
1051
+ lot. *)
1052
+ if locHasPos pat.ppat_loc then
1053
+ CompletionResult. pattern ~completion Context ~prefix: " "
1054
+ else None
1055
+ | Ppat_construct (_, _)
946
1056
| Ppat_variant (_, _)
947
- | Ppat_record (_, _)
948
- | Ppat_array _
949
- | Ppat_or
950
- ( {ppat_loc = {loc_start = _; loc_end = _; _}; _},
951
- {ppat_loc = {loc_start = _; loc_end = _; _}; _} )
952
- | Ppat_type {loc = {loc_start = _; loc_end = _; _}; _}
953
- | Ppat_unpack {loc = {loc_start = _; loc_end = _; _}; _}
954
- | Ppat_extension ({loc = {loc_start = _ ; loc_end = _ ; _} ; _} , _ ) ->
1057
+ | Ppat_type _ | Ppat_unpack _ | Ppat_extension _ | Ppat_constant _
1058
+ | Ppat_interval _ ->
955
1059
None
956
- | Ppat_constant _ | Ppat_interval _ -> None
957
1060
958
1061
let completion ~currentFile ~path ~debug ~offset ~posCursor text =
959
1062
let positionContext = PositionContext. make ~offset ~pos Cursor text in
0 commit comments