Skip to content

Commit 59e555d

Browse files
committed
basic setup of some sort of structure, and complete tuple patterns
1 parent de45450 commit 59e555d

File tree

6 files changed

+232
-1
lines changed

6 files changed

+232
-1
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,17 @@ let getJsxLabels ~componentPath ~findTypeOfValue ~package =
17311731
typ |> getLabels
17321732
| None -> []
17331733

1734+
let rec resolveNestedPattern typ ~env ~package ~nested =
1735+
match nested with
1736+
| [] -> Some (typ, env)
1737+
| patternPath :: nested -> (
1738+
match (patternPath, typ |> extractType ~env ~package) with
1739+
| Completable.PTupleItem {itemNum}, Some (Tuple (env, tupleItems, _)) -> (
1740+
match List.nth_opt tupleItems itemNum with
1741+
| None -> None
1742+
| Some typ -> typ |> resolveNestedPattern ~env ~package ~nested)
1743+
| _ -> None)
1744+
17341745
let processCompletable ~debug ~full ~scope ~env ~pos ~forHover
17351746
(completable : Completable.t) =
17361747
let package = full.package in
@@ -2093,3 +2104,32 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
20932104
Utils.startsWith name prefix
20942105
&& (forHover || not (List.mem name identsSeen)))
20952106
|> List.map mkLabel
2107+
| Cpattern {typ; prefix; nested = None} -> (
2108+
let envWhereCompletionStarted = env in
2109+
match
2110+
typ
2111+
|> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
2112+
~exact:true ~scope
2113+
|> completionsGetTypeEnv
2114+
with
2115+
| Some (typ, env) ->
2116+
typ
2117+
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2118+
~expandOption:false
2119+
| None -> [])
2120+
| Cpattern {typ; prefix; nested = Some nested} -> (
2121+
let envWhereCompletionStarted = env in
2122+
match
2123+
typ
2124+
|> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
2125+
~exact:true ~scope
2126+
|> completionsGetTypeEnv
2127+
with
2128+
| Some (typ, env) -> (
2129+
match typ |> resolveNestedPattern ~env ~package:full.package ~nested with
2130+
| None -> []
2131+
| Some (typ, env) ->
2132+
typ
2133+
|> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
2134+
~expandOption:false)
2135+
| None -> [])

analysis/src/CompletionFrontEnd.ml

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ let isExprHole exp =
1313
| Pexp_extension ({txt = "rescript.exprhole"}, _) -> true
1414
| _ -> false
1515

16+
let isPatternHole pat =
17+
match pat.Parsetree.ppat_desc with
18+
| Ppat_extension ({txt = "rescript.patternhole"}, _) -> true
19+
| _ -> false
20+
1621
type prop = {
1722
name: string;
1823
posStart: int * int;
@@ -403,6 +408,96 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
403408
!scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc
404409
in
405410

411+
let lookingForPat = ref None in
412+
413+
let setLookingForPat ctxPath =
414+
lookingForPat :=
415+
Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None});
416+
Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath))
417+
in
418+
let appendNestedPat patternPat =
419+
match !lookingForPat with
420+
| Some (Completable.Cpattern ({nested = None} as p)) ->
421+
lookingForPat := Some (Cpattern {p with nested = Some [patternPat]})
422+
| Some (Completable.Cpattern ({nested = Some nested} as p)) ->
423+
lookingForPat :=
424+
Some (Cpattern {p with nested = Some (nested @ [patternPat])})
425+
| _ -> ()
426+
in
427+
let commitFoundPat ?prefix () =
428+
match !lookingForPat with
429+
| Some (Completable.Cpattern p as cpattern) ->
430+
let cpattern =
431+
match prefix with
432+
| None -> cpattern
433+
| Some prefix -> Cpattern {p with prefix}
434+
in
435+
setResult cpattern
436+
| _ -> ()
437+
in
438+
439+
let rec typedCompletionExpr (exp : Parsetree.expression) =
440+
if
441+
exp.pexp_loc
442+
|> CursorPosition.classifyLoc ~pos:posBeforeCursor
443+
= HasCursor
444+
then
445+
match exp.pexp_desc with
446+
| Pexp_match (_exp, []) ->
447+
(* No cases means there's no `|` yet in the switch *) ()
448+
| Pexp_match
449+
( exp,
450+
[
451+
{
452+
pc_lhs =
453+
{
454+
ppat_desc =
455+
Ppat_extension ({txt = "rescript.patternhole"}, _);
456+
};
457+
};
458+
] ) -> (
459+
(* A single case that's a pattern hole typically means `switch x { | }`. Complete as the pattern itself with nothing nested. *)
460+
match exprToContextPath exp with
461+
| None -> ()
462+
| Some ctxPath ->
463+
setResult
464+
(Completable.Cpattern {typ = ctxPath; nested = None; prefix = ""}))
465+
| Pexp_match (exp, _cases) -> (
466+
(* If there's more than one case, or the case isn't a pattern hole, set that we're looking for this path currently. *)
467+
match exp |> exprToContextPath with
468+
| None -> ()
469+
| Some ctxPath -> setLookingForPat ctxPath)
470+
| _ -> ()
471+
in
472+
473+
let rec typedCompletionPat (pat : Parsetree.pattern) =
474+
if
475+
pat.ppat_loc
476+
|> CursorPosition.classifyLoc ~pos:posBeforeCursor
477+
= HasCursor
478+
then
479+
match pat.ppat_desc with
480+
| Ppat_var {txt} -> commitFoundPat ~prefix:txt ()
481+
| Ppat_tuple patterns -> (
482+
let patCount = ref (-1) in
483+
let patCountWithPatHole = ref None in
484+
patterns
485+
|> List.iteri (fun index p ->
486+
match
487+
p.Parsetree.ppat_loc
488+
|> CursorPosition.classifyLoc ~pos:posBeforeCursor
489+
with
490+
| HasCursor -> patCount := index
491+
| EmptyLoc -> patCountWithPatHole := Some index
492+
| _ -> ());
493+
match (!patCount, !patCountWithPatHole) with
494+
| patCount, _ when patCount > -1 ->
495+
appendNestedPat (Completable.PTupleItem {itemNum = patCount})
496+
| _, Some patHoleCount ->
497+
appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount})
498+
| _ -> ())
499+
| _ -> ()
500+
in
406501
let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) =
407502
let oldScope = !scope in
408503
scopePattern case.pc_lhs;
@@ -541,6 +636,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
541636
setResult (Cpath (CPPipe {contextPath = pipe; id; lhsLoc}));
542637
true
543638
in
639+
typedCompletionExpr expr;
544640
match expr.pexp_desc with
545641
| Pexp_apply
546642
( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}},
@@ -800,7 +896,10 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
800896
(Loc.toString lid.loc);
801897
setResult (Cpath (CPId (lidPath, Value)))
802898
| _ -> ());
803-
Ast_iterator.default_iterator.pat iterator pat)
899+
let oldLookingForPat = !lookingForPat in
900+
typedCompletionPat pat;
901+
Ast_iterator.default_iterator.pat iterator pat;
902+
lookingForPat := oldLookingForPat)
804903
in
805904
let module_expr (iterator : Ast_iterator.iterator)
806905
(me : Parsetree.module_expr) =

analysis/src/SharedTypes.ml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,12 @@ module Completable = struct
550550
(** The loc item for the left hand side of the pipe. *)
551551
}
552552

553+
type patternPath = PTupleItem of {itemNum: int}
554+
555+
let patternPathToString p =
556+
match p with
557+
| PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")"
558+
553559
type t =
554560
| Cdecorator of string (** e.g. @module *)
555561
| CnamedArg of contextPath * string * string list
@@ -569,6 +575,11 @@ module Completable = struct
569575
propName: string;
570576
prefix: string;
571577
}
578+
| Cpattern of {
579+
typ: contextPath;
580+
nested: patternPath list option;
581+
prefix: string;
582+
}
572583

573584
(** An extracted type from a type expr *)
574585
type extractedType =
@@ -614,6 +625,7 @@ module Completable = struct
614625
| CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]"
615626
| CPPipe {contextPath; id} -> contextPathToString contextPath ^ "->" ^ id
616627
in
628+
617629
function
618630
| Cpath cp -> "Cpath " ^ contextPathToString cp
619631
| Cdecorator s -> "Cdecorator(" ^ str s ^ ")"
@@ -637,6 +649,17 @@ module Completable = struct
637649
| CjsxPropValue {prefix; pathToComponent; propName} ->
638650
"CjsxPropValue " ^ (pathToComponent |> list) ^ " " ^ propName ^ "="
639651
^ prefix
652+
| Cpattern {typ; nested; prefix} -> (
653+
"Cpattern " ^ contextPathToString typ
654+
^ (if prefix = "" then "" else "=" ^ prefix)
655+
^
656+
match nested with
657+
| None -> ""
658+
| Some patternPaths ->
659+
"->"
660+
^ (patternPaths
661+
|> List.map (fun patternPath -> patternPathToString patternPath)
662+
|> String.concat ", "))
640663
end
641664

642665
module CursorPosition = struct
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
let v = (true, Some(false))
2+
3+
// switch v {
4+
// ^com
5+
6+
// switch v { | }
7+
// ^com
8+
9+
// switch v { | (t, _) }
10+
// ^com
11+
12+
let x = true
13+
14+
// switch x { |
15+
// ^com
16+
17+
// switch x { | t
18+
// ^com

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,7 @@ Resolved opens 2 Completion.res Completion.res
965965
[]
966966

967967
Complete src/Completion.res 243:8
968+
looking for: Cpath Value[someR]
968969
posCursor:[243:8] posNoWhite:[243:7] Found expr:[241:8->246:1]
969970
posCursor:[243:8] posNoWhite:[243:7] Found expr:[242:14->243:8]
970971
Pexp_apply ...[243:3->243:4] (...[242:14->242:15], ...[243:5->243:8])
@@ -1442,6 +1443,7 @@ posCursor:[355:23] posNoWhite:[355:22] Found expr:[355:12->355:23]
14421443

14431444
Complete src/Completion.res 362:8
14441445
posCursor:[362:8] posNoWhite:[362:7] Found expr:[360:8->365:3]
1446+
looking for: Cpath Value[x]
14451447
posCursor:[362:8] posNoWhite:[362:7] Found expr:[361:2->365:3]
14461448
posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->364:5]
14471449
posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->362:8]
@@ -1477,6 +1479,7 @@ Resolved opens 2 Completion.res Completion.res
14771479

14781480
Complete src/Completion.res 373:21
14791481
posCursor:[373:21] posNoWhite:[373:20] Found expr:[371:8->376:3]
1482+
looking for: Cpath Value[x]
14801483
posCursor:[373:21] posNoWhite:[373:20] Found expr:[372:2->376:3]
14811484
posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->375:5]
14821485
posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->373:21]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
Complete src/CompletionPattern.res 2:13
2+
posCursor:[2:13] posNoWhite:[2:12] Found expr:[2:3->2:13]
3+
[]
4+
5+
Complete src/CompletionPattern.res 5:15
6+
XXX Not found!
7+
Completable: Cpattern Value[v]
8+
[{
9+
"label": "(_, _)",
10+
"kind": 12,
11+
"tags": [],
12+
"detail": "(bool, option<bool>)",
13+
"documentation": null,
14+
"insertText": "(${1:_}, ${2:_})",
15+
"insertTextFormat": 2
16+
}]
17+
18+
Complete src/CompletionPattern.res 8:18
19+
looking for: Cpath Value[v]
20+
posCursor:[8:18] posNoWhite:[8:17] Found expr:[8:3->8:24]
21+
posCursor:[8:18] posNoWhite:[8:17] Found pattern:[8:16->8:22]
22+
posCursor:[8:18] posNoWhite:[8:17] Found pattern:[8:17->8:18]
23+
Completable: Cpattern Value[v]=t->tuple($0)
24+
[{
25+
"label": "true",
26+
"kind": 4,
27+
"tags": [],
28+
"detail": "bool",
29+
"documentation": null
30+
}]
31+
32+
Complete src/CompletionPattern.res 13:16
33+
posCursor:[13:16] posNoWhite:[13:14] Found expr:[13:3->13:15]
34+
[]
35+
36+
Complete src/CompletionPattern.res 16:17
37+
looking for: Cpath Value[x]
38+
posCursor:[16:17] posNoWhite:[16:16] Found expr:[16:3->16:17]
39+
posCursor:[16:17] posNoWhite:[16:16] Found pattern:[16:16->16:17]
40+
Completable: Cpattern Value[x]=t
41+
[{
42+
"label": "true",
43+
"kind": 4,
44+
"tags": [],
45+
"detail": "bool",
46+
"documentation": null
47+
}]
48+

0 commit comments

Comments
 (0)