Skip to content

Commit cd59a8c

Browse files
committed
Merge branch 'master' into move-tools-to-ocaml
2 parents dd1d3f0 + 0ec83cc commit cd59a8c

13 files changed

+275
-235
lines changed

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
1313
## master
1414

15+
#### :bug: Bug Fix
16+
17+
- Fix issue with ambigious wraps in JSX prop values (`<SomeComp someProp={<com>}`) - need to figure out if we're completing for a record body or if `{}` are just wraps for the type of `someProp`. In the case of ambiguity, completions for both scenarios are provided. https://github.com/rescript-lang/rescript-vscode/pull/894
18+
- Many bugfixes around nested pattern and expression completion. https://github.com/rescript-lang/rescript-vscode/pull/892
19+
1520
#### :nail_care: Polish
1621

1722
- More cases of not emitting `_` when completing in expressions. https://github.com/rescript-lang/rescript-vscode/pull/890
@@ -28,10 +33,6 @@
2833
- Complete for maker-style functions (functions returning type `t` of a module) when encountering a `type t` in relevant scenarios. https://github.com/rescript-lang/rescript-vscode/pull/884
2934
- Expand type aliases in hovers. https://github.com/rescript-lang/rescript-vscode/pull/881
3035

31-
#### :bug: Bug Fix
32-
33-
- Many bugfixes around nested pattern and expression completion. https://github.com/rescript-lang/rescript-vscode/pull/892
34-
3536
#### :nail_care: Polish
3637

3738
- Better error recovery when analysis fails. https://github.com/rescript-lang/rescript-vscode/pull/880

analysis/src/CompletionBackEnd.ml

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@ open SharedTypes
33
let showConstructor {Constructor.cname = {txt}; args; res} =
44
txt
55
^ (match args with
6-
| Args [] | InlineRecord _ -> ""
6+
| Args [] -> ""
7+
| InlineRecord fields ->
8+
"({"
9+
^ (fields
10+
|> List.map (fun (field : field) ->
11+
Printf.sprintf "%s%s: %s" field.fname.txt
12+
(if field.optional then "?" else "")
13+
(Shared.typeToString
14+
(if field.optional then Utils.unwrapIfOption field.typ
15+
else field.typ)))
16+
|> String.concat ", ")
17+
^ "})"
718
| Args args ->
819
"("
920
^ (args
@@ -686,41 +697,48 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
686697
let package = full.package in
687698
match contextPath with
688699
| CPString ->
700+
if Debug.verbose () then print_endline "[ctx_path]--> CPString";
689701
[
690702
Completion.create "dummy" ~env
691703
~kind:
692704
(Completion.Value
693705
(Ctype.newconstr (Path.Pident (Ident.create "string")) []));
694706
]
695707
| CPBool ->
708+
if Debug.verbose () then print_endline "[ctx_path]--> CPBool";
696709
[
697710
Completion.create "dummy" ~env
698711
~kind:
699712
(Completion.Value
700713
(Ctype.newconstr (Path.Pident (Ident.create "bool")) []));
701714
]
702715
| CPInt ->
716+
if Debug.verbose () then print_endline "[ctx_path]--> CPInt";
703717
[
704718
Completion.create "dummy" ~env
705719
~kind:
706720
(Completion.Value
707721
(Ctype.newconstr (Path.Pident (Ident.create "int")) []));
708722
]
709723
| CPFloat ->
724+
if Debug.verbose () then print_endline "[ctx_path]--> CPFloat";
710725
[
711726
Completion.create "dummy" ~env
712727
~kind:
713728
(Completion.Value
714729
(Ctype.newconstr (Path.Pident (Ident.create "float")) []));
715730
]
716731
| CPArray None ->
732+
if Debug.verbose () then print_endline "[ctx_path]--> CPArray (no payload)";
717733
[
718734
Completion.create "array" ~env
719735
~kind:
720736
(Completion.Value
721737
(Ctype.newconstr (Path.Pident (Ident.create "array")) []));
722738
]
723739
| CPArray (Some cp) -> (
740+
if Debug.verbose () then
741+
print_endline "[ctx_path]--> CPArray (with payload)";
724742
match mode with
725743
| Regular -> (
726744
match
@@ -746,6 +764,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
746764
(Ctype.newconstr (Path.Pident (Ident.create "array")) []));
747765
])
748766
| CPOption cp -> (
767+
if Debug.verbose () then print_endline "[ctx_path]--> CPOption";
749768
match
750769
cp
751770
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
@@ -760,6 +779,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
760779
(Completion.ExtractedType (Toption (env, ExtractedType typ), `Type));
761780
])
762781
| CPAwait cp -> (
782+
if Debug.verbose () then print_endline "[ctx_path]--> CPAwait";
763783
match
764784
cp
765785
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
@@ -770,10 +790,12 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
770790
[Completion.create "dummy" ~env ~kind:(Completion.Value typ)]
771791
| _ -> [])
772792
| CPId (path, completionContext) ->
793+
if Debug.verbose () then print_endline "[ctx_path]--> CPId";
773794
path
774795
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~completionContext
775796
~env ~scope
776797
| CPApply (cp, labels) -> (
798+
if Debug.verbose () then print_endline "[ctx_path]--> CPApply";
777799
match
778800
cp
779801
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
@@ -815,11 +837,13 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
815837
| _ -> [])
816838
| _ -> [])
817839
| CPField (CPId (path, Module), fieldName) ->
840+
if Debug.verbose () then print_endline "[ctx_path]--> CPField: M.field";
818841
(* M.field *)
819842
path @ [fieldName]
820843
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact
821844
~completionContext:Field ~env ~scope
822845
| CPField (cp, fieldName) -> (
846+
if Debug.verbose () then print_endline "[ctx_path]--> CPField";
823847
let completionsForCtxPath =
824848
cp
825849
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
@@ -858,6 +882,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
858882
else None))
859883
| CPObj (cp, label) -> (
860884
(* TODO: Also needs to support ExtractedType *)
885+
if Debug.verbose () then print_endline "[ctx_path]--> CPObj";
861886
match
862887
cp
863888
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
@@ -885,6 +910,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
885910
| None -> [])
886911
| None -> [])
887912
| CPPipe {contextPath = cp; id = funNamePrefix; lhsLoc; inJsx} -> (
913+
if Debug.verbose () then print_endline "[ctx_path]--> CPPipe";
888914
match
889915
cp
890916
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
@@ -997,6 +1023,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
9971023
| _ -> completions)
9981024
| None -> []))
9991025
| CTuple ctxPaths ->
1026+
if Debug.verbose () then print_endline "[ctx_path]--> CTuple";
10001027
(* Turn a list of context paths into a list of type expressions. *)
10011028
let typeExrps =
10021029
ctxPaths
@@ -1016,6 +1043,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10161043
]
10171044
else []
10181045
| CJsxPropValue {pathToComponent; propName} -> (
1046+
if Debug.verbose () then print_endline "[ctx_path]--> CJsxPropValue";
10191047
let findTypeOfValue path =
10201048
path
10211049
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:true
@@ -1027,6 +1055,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10271055
| [elName] when Char.lowercase_ascii elName.[0] = elName.[0] -> true
10281056
| _ -> false
10291057
in
1058+
(* TODO(env-stuff) Does this need to potentially be instantiated with type args too? *)
10301059
let targetLabel =
10311060
if lowercaseComponent then
10321061
let rec digToTypeForCompletion path =
@@ -1061,6 +1090,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10611090
~kind:(Completion.Value (Utils.unwrapIfOption typ));
10621091
])
10631092
| CArgument {functionContextPath; argumentLabel} -> (
1093+
if Debug.verbose () then print_endline "[ctx_path]--> CArgument";
10641094
if Debug.verbose () then
10651095
Printf.printf "--> function argument: %s\n"
10661096
(match argumentLabel with
@@ -1113,6 +1143,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
11131143
(if expandOption then Utils.unwrapIfOption typ else typ));
11141144
])
11151145
| CPatternPath {rootCtxPath; nested} -> (
1146+
if Debug.verbose () then print_endline "[ctx_path]--> CPatternPath";
11161147
(* TODO(env-stuff) Get rid of innerType etc *)
11171148
match
11181149
rootCtxPath
@@ -1127,6 +1158,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
11271158
| None -> [])
11281159
| None -> [])
11291160
| CTypeAtPos loc -> (
1161+
if Debug.verbose () then print_endline "[ctx_path]--> CTypeAtPos";
11301162
match
11311163
References.getLocItem ~full ~pos:(Pos.ofLexing loc.loc_start) ~debug
11321164
with
@@ -1861,6 +1893,22 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
18611893
fallbackOrEmpty ~items ())
18621894
| None -> fallbackOrEmpty ())
18631895
| Cexpression {contextPath; prefix; nested} -> (
1896+
let isAmbigiousRecordBodyOrJsxWrap =
1897+
match (contextPath, nested) with
1898+
| CJsxPropValue _, [NRecordBody _] -> true
1899+
| _ -> false
1900+
in
1901+
if Debug.verbose () then
1902+
(* This happens in this scenario: `<SomeComponent someProp={<com>}`
1903+
Here, we don't know whether `{}` is just wraps for the type of
1904+
`someProp`, or if it's a record body where we want to complete
1905+
for the fields in the record. We need to look up what the type is
1906+
first before deciding what completions to show. So we do that here.*)
1907+
if isAmbigiousRecordBodyOrJsxWrap then
1908+
print_endline
1909+
"[process_completable]--> Cexpression special case: JSX prop value \
1910+
that might be record body or JSX wrap"
1911+
else print_endline "[process_completable]--> Cexpression";
18641912
(* Completions for local things like variables in scope, modules in the
18651913
project, etc. We only add completions when there's a prefix of some sort
18661914
we can filter on, since we know we're in some sort of context, and
@@ -1879,17 +1927,32 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
18791927
with
18801928
| None ->
18811929
if Debug.verbose () then
1882-
print_endline "--> could not get completions for context path";
1930+
print_endline
1931+
"[process_completable]--> could not get completions for context path";
18831932
regularCompletions
18841933
| Some (typ, env) -> (
18851934
match typ |> TypeUtils.resolveNested ~env ~full ~nested with
18861935
| None ->
18871936
if Debug.verbose () then
1888-
print_endline "--> could not resolve nested expression path";
1889-
regularCompletions
1937+
print_endline
1938+
"[process_completable]--> could not resolve nested expression path";
1939+
if isAmbigiousRecordBodyOrJsxWrap then (
1940+
if Debug.verbose () then
1941+
print_endline
1942+
"[process_completable]--> case is ambigious Jsx prop vs record \
1943+
body case, complete also for the JSX prop value directly";
1944+
let itemsForRawJsxPropValue =
1945+
typ
1946+
|> completeTypedValue ~rawOpens ~mode:Expression ~full ~prefix
1947+
~completionContext:None
1948+
in
1949+
itemsForRawJsxPropValue @ regularCompletions)
1950+
else regularCompletions
18901951
| Some (typ, _env, completionContext, typeArgContext) -> (
18911952
if Debug.verbose () then
1892-
print_endline "--> found type in nested expression completion";
1953+
print_endline
1954+
"[process_completable]--> found type in nested expression \
1955+
completion";
18931956
(* Wrap the insert text in braces when we're completing the root of a
18941957
JSX prop value. *)
18951958
let wrapInsertTextInBraces =

analysis/src/CompletionJsx.ml

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -816,20 +816,27 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor
816816
let rec loop props =
817817
match props with
818818
| prop :: rest ->
819-
if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then
820-
(* Cursor on the prop name *)
819+
if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then (
820+
if Debug.verbose () then
821+
print_endline "[jsx_props_completable]--> Cursor on the prop name";
822+
821823
Some
822824
(Completable.Cjsx
823825
( Utils.flattenLongIdent ~jsx:true jsxProps.compName.txt,
824826
prop.name,
825-
allLabels ))
827+
allLabels )))
826828
else if
827829
prop.posEnd <= posBeforeCursor
828830
&& posBeforeCursor < Loc.start prop.exp.pexp_loc
829-
then (* Cursor between the prop name and expr assigned *)
830-
None
831-
else if prop.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then
832-
(* Cursor on expr assigned *)
831+
then (
832+
if Debug.verbose () then
833+
print_endline
834+
"[jsx_props_completable]--> Cursor between the prop name and expr \
835+
assigned";
836+
None)
837+
else if prop.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then (
838+
if Debug.verbose () then
839+
print_endline "[jsx_props_completable]--> Cursor on expr assigned";
833840
match
834841
CompletionExpressions.traverseExpr prop.exp ~exprPath:[]
835842
~pos:posBeforeCursor ~firstCharBeforeCursorNoWhite
@@ -848,9 +855,13 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor
848855
nested = List.rev nested;
849856
prefix;
850857
})
851-
| _ -> None
852-
else if prop.exp.pexp_loc |> Loc.end_ = (Location.none |> Loc.end_) then
853-
if CompletionExpressions.isExprHole prop.exp then
858+
| _ -> None)
859+
else if prop.exp.pexp_loc |> Loc.end_ = (Location.none |> Loc.end_) then (
860+
if Debug.verbose () then
861+
print_endline "[jsx_props_completable]--> Loc is broken";
862+
if CompletionExpressions.isExprHole prop.exp then (
863+
if Debug.verbose () then
864+
print_endline "[jsx_props_completable]--> Expr was expr hole";
854865
Some
855866
(Cexpression
856867
{
@@ -863,13 +874,17 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor
863874
};
864875
prefix = "";
865876
nested = [];
866-
})
867-
else None
868-
else if rest = [] && beforeChildrenStart && charAtCursor = '>' then
877+
}))
878+
else None)
879+
else if rest = [] && beforeChildrenStart && charAtCursor = '>' then (
869880
(* This is a special case for: <SomeComponent someProp=> (completing directly after the '=').
870881
The completion comes at the end of the component, after the equals sign, but before any
871882
children starts, and '>' marks that it's at the end of the component JSX.
872883
This little heuristic makes sure we pick up this special case. *)
884+
if Debug.verbose () then
885+
print_endline
886+
"[jsx_props_completable]--> Special case: last prop, '>' after \
887+
cursor";
873888
Some
874889
(Cexpression
875890
{
@@ -882,16 +897,18 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor
882897
};
883898
prefix = "";
884899
nested = [];
885-
})
900+
}))
886901
else loop rest
887902
| [] ->
888903
let afterCompName = posBeforeCursor >= posAfterCompName in
889-
if afterCompName && beforeChildrenStart then
904+
if afterCompName && beforeChildrenStart then (
905+
if Debug.verbose () then
906+
print_endline "[jsx_props_completable]--> Complete for JSX prop name";
890907
Some
891908
(Cjsx
892909
( Utils.flattenLongIdent ~jsx:true jsxProps.compName.txt,
893910
"",
894-
allLabels ))
911+
allLabels )))
895912
else None
896913
in
897914
loop jsxProps.props

analysis/tests/src/CompletionJsxProps.res

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@
2727
// let _ = <CompletionSupport.TestComponent testArr={[]}
2828
// ^com
2929

30+
let tsomeVar = #two
31+
32+
// let _ = <CompletionSupport.TestComponent polyArg={}
33+
// ^com
34+
35+
// let _ = <CompletionSupport.TestComponent on={t}
36+
// ^com

0 commit comments

Comments
 (0)