Skip to content

Commit 3bc47b4

Browse files
committed
work
1 parent 0df59dc commit 3bc47b4

File tree

3 files changed

+162
-17
lines changed

3 files changed

+162
-17
lines changed

analysis/src/CompletionFrontEndNew.ml

+119-16
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ type ctxPath =
6565
| CId of string list * completionCategory
6666
(** A regular id of an expected category. `let fff = thisIsAnId<com>` and `let fff = SomePath.alsoAnId<com>` *)
6767
| 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 *)
6971
| CRecordField of {seenFields: string list; prefix: string}
7072
(** A record field. `let f = {on: true, s<com>}` seenFields = [on], prefix = "s",*)
7173
| COption of ctxPath (** An option with an inner type. *)
@@ -120,6 +122,7 @@ let rec ctxPathToString (ctxPath : ctxPath) =
120122
| Field -> "Field")
121123
(ident prefix)
122124
| CVariantPayload {itemNum} -> Printf.sprintf "CVariantPayload($%i)" itemNum
125+
| CTupleItem {itemNum} -> Printf.sprintf "CTupleItem($%i)" itemNum
123126
| CRecordField {prefix} -> Printf.sprintf "CRecordField=%s" prefix
124127
| COption ctxPath -> Printf.sprintf "COption<%s>" (ctxPathToString ctxPath)
125128
| CArray ctxPath ->
@@ -882,6 +885,22 @@ and completePattern ~(completionContext : CompletionContext.t)
882885
(* Can just continue into these patterns. *)
883886
if locHasPos pat.ppat_loc then p |> completePattern ~completionContext
884887
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 ~completionContext 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 ~completionContext ~prefix:""
903+
| res -> res)
885904
| Ppat_var {txt; loc} ->
886905
(* A variable, like `{ someThing: someV<com>}*)
887906
if locHasPos loc then
@@ -897,7 +916,7 @@ and completePattern ~(completionContext : CompletionContext.t)
897916
in
898917
CompletionResult.pattern ~completionContext ~prefix:""
899918
else None
900-
| Ppat_record (fields, _) when locHasPos pat.ppat_loc -> (
919+
| Ppat_record (fields, _) -> (
901920
(* Record body with fields, where we know the cursor is inside of the record body somewhere. *)
902921
let seenFields =
903922
fields
@@ -938,22 +957,106 @@ and completePattern ~(completionContext : CompletionContext.t)
938957
in
939958
completePattern ~completionContext fieldPattern
940959
| 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 ~completionContext 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 ~completionContext ~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 ~completionContext ~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 ~completionContext ~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 ~completionContext 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 ~completionContext ~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 ~completionContext ~prefix:""
1054+
else None
1055+
| Ppat_construct (_, _)
9461056
| 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 _ ->
9551059
None
956-
| Ppat_constant _ | Ppat_interval _ -> None
9571060

9581061
let completion ~currentFile ~path ~debug ~offset ~posCursor text =
9591062
let positionContext = PositionContext.make ~offset ~posCursor text in

analysis/tests/src/CompletionNew.res

+18-1
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,26 @@ type fn = (~name: string=?, string) => bool
8181
// let someFun: fn = (str, ~name) => {let whatever = t}
8282
// ^co2
8383

84-
// Let binding patterns
84+
// == Let binding patterns ==
8585
// let someVar: bool =
8686
// ^co2
8787

8888
// let {someField: s } = someRecordVar
8989
// ^co2
90+
91+
// == Tuple patterns ==
92+
// let (true, ) = someRecordVar
93+
// ^co2
94+
95+
// let (true, true, , false) = someRecordVar
96+
// ^co2
97+
98+
// == Arrays ==
99+
// let [ ] = someArr
100+
// ^co2
101+
102+
// let [(true, [false, ])] = someArr
103+
// ^co2
104+
105+
// let [(true, [false, f])] = someArr
106+
// ^co2

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

+25
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,28 @@ Result: Cpattern: ctxPath: CRecordField=someField, rootType: Type<CId(Value)=som
113113
Scope: 0 items
114114
Looking for type: Type<CId(Value)=someRecordVar>
115115

116+
Complete2 src/CompletionNew.res 91:13
117+
Result: Cpattern: ctxPath: CTupleItem($1), rootType: Type<CId(Value)=someRecordVar>
118+
Scope: 0 items
119+
Looking for type: Type<CId(Value)=someRecordVar>
120+
121+
Complete2 src/CompletionNew.res 94:20
122+
Result: Cpattern: ctxPath: CTupleItem($2), rootType: Type<CId(Value)=someRecordVar>
123+
Scope: 0 items
124+
Looking for type: Type<CId(Value)=someRecordVar>
125+
126+
Complete2 src/CompletionNew.res 98:9
127+
Result: Cpattern: ctxPath: array, rootType: Type<CId(Value)=someArr>
128+
Scope: 0 items
129+
Looking for type: Type<CId(Value)=someArr>
130+
131+
Complete2 src/CompletionNew.res 101:22
132+
Result: Cpattern: ctxPath: array->CTupleItem($1)->array, rootType: Type<CId(Value)=someArr>
133+
Scope: 0 items
134+
Looking for type: Type<CId(Value)=someArr>
135+
136+
Complete2 src/CompletionNew.res 104:24
137+
Result: Cpattern: ctxPath: array->CTupleItem($1)->array, rootType: Type<CId(Value)=someArr>, prefix: "f"
138+
Scope: 0 items
139+
Looking for type: Type<CId(Value)=someArr>
140+

0 commit comments

Comments
 (0)