Skip to content

Commit 4b2964e

Browse files
committed
initial basic version of leveraging type annotations when completing
1 parent 0fcb318 commit 4b2964e

File tree

5 files changed

+180
-62
lines changed

5 files changed

+180
-62
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,13 @@ let completionsGetTypeEnv = function
582582
| {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env)
583583
| _ -> None
584584

585+
let completionsGetCompletionType = function
586+
| {Completion.kind = Value typ; env} :: _ -> Some (TypeExpr typ, env)
587+
| {Completion.kind = ObjLabel typ; env} :: _ -> Some (TypeExpr typ, env)
588+
| {Completion.kind = Field ({typ}, _); env} :: _ -> Some (TypeExpr typ, env)
589+
| {Completion.kind = Type typ; env} :: _ -> Some (ResolvedType typ, env)
590+
| _ -> None
591+
585592
let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
586593
~exact ~scope (contextPath : Completable.contextPath) =
587594
let package = full.package in
@@ -996,11 +1003,7 @@ type completionMode = Pattern | Expression
9961003

9971004
let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix
9981005
~completionContext ~mode =
999-
let extractedType =
1000-
match t with
1001-
| TypeExpr t -> t |> TypeUtils.extractType ~env ~package:full.package
1002-
| InlineRecord fields -> Some (TinlineRecord {env; fields})
1003-
in
1006+
let extractedType = t |> TypeUtils.extractTypeFromCompletionType ~env ~full in
10041007
match extractedType with
10051008
| Some (Tbool env) ->
10061009
[
@@ -1308,10 +1311,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover
13081311
|> completionsGetTypeEnv
13091312
with
13101313
| Some (typ, env) -> (
1311-
match
1312-
TypeExpr typ
1313-
|> TypeUtils.resolveNested ~env ~package:full.package ~nested
1314-
with
1314+
match TypeExpr typ |> TypeUtils.resolveNested ~env ~full ~nested with
13151315
| None -> fallbackOrEmpty ()
13161316
| Some (typ, env, completionContext) ->
13171317
let items =
@@ -1326,14 +1326,11 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover
13261326
contextPath
13271327
|> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
13281328
~exact:true ~scope
1329-
|> completionsGetTypeEnv
1329+
|> completionsGetCompletionType
13301330
with
13311331
| None -> []
13321332
| Some (typ, env) -> (
1333-
match
1334-
TypeExpr typ
1335-
|> TypeUtils.resolveNested ~env ~package:full.package ~nested
1336-
with
1333+
match typ |> TypeUtils.resolveNested ~env ~full ~nested with
13371334
| None -> []
13381335
| Some (typ, env, completionContext) -> (
13391336
let isJsx =

analysis/src/CompletionFrontEnd.ml

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -316,27 +316,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
316316
| _ -> ()
317317
in
318318
let scopeValueBinding (vb : Parsetree.value_binding) =
319-
scopePattern vb.pvb_pat;
320-
(* Identify relevant destructures for completion, like `let {<com>} = someVar` or `let (true, false) = someFn()`. *)
321-
match vb with
322-
| {pvb_pat; pvb_expr} when locHasCursor pvb_pat.ppat_loc -> (
323-
match
324-
( pvb_pat
325-
|> CompletionPatterns.traversePattern ~patternPath:[] ~locHasCursor
326-
~firstCharBeforeCursorNoWhite ~posBeforeCursor,
327-
exprToContextPath pvb_expr )
328-
with
329-
| Some (prefix, nestedPattern), Some ctxPath ->
330-
setResult
331-
(Completable.Cpattern
332-
{
333-
contextPath = ctxPath;
334-
prefix;
335-
nested = List.rev nestedPattern;
336-
fallback = None;
337-
})
338-
| _ -> ())
339-
| _ -> ()
319+
scopePattern vb.pvb_pat
340320
in
341321
let scopeTypeKind (tk : Parsetree.type_kind) =
342322
match tk with
@@ -490,6 +470,58 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
490470
(value_binding : Parsetree.value_binding) =
491471
let oldInJsxContext = !inJsxContext in
492472
if Utils.isReactComponent value_binding then inJsxContext := true;
473+
(match value_binding with
474+
| {pvb_pat = {ppat_desc = Ppat_constraint (_pat, coreType)}; pvb_expr}
475+
when locHasCursor pvb_expr.pexp_loc -> (
476+
(* Expression with derivable type annotation.
477+
E.g: let x: someRecord = {<com>} *)
478+
match
479+
( TypeUtils.contextPathFromCoreType coreType,
480+
pvb_expr
481+
|> CompletionExpressions.traverseExpr ~exprPath:[]
482+
~pos:posBeforeCursor ~firstCharBeforeCursorNoWhite )
483+
with
484+
| Some ctxPath, Some (prefix, nested) ->
485+
setResult
486+
(Completable.Cexpression
487+
{contextPath = ctxPath; prefix; nested = List.rev nested})
488+
| _ -> ())
489+
| {
490+
pvb_pat = {ppat_desc = Ppat_constraint (_pat, coreType); ppat_loc};
491+
pvb_expr;
492+
}
493+
when locHasCursor value_binding.pvb_loc
494+
&& locHasCursor ppat_loc = false
495+
&& locHasCursor pvb_expr.pexp_loc = false
496+
&& CompletionExpressions.isExprHole pvb_expr -> (
497+
(* Expression with derivable type annotation, when the expression is empty (expr hole).
498+
E.g: let x: someRecord = <com> *)
499+
match TypeUtils.contextPathFromCoreType coreType with
500+
| Some ctxPath ->
501+
setResult
502+
(Completable.Cexpression
503+
{contextPath = ctxPath; prefix = ""; nested = []})
504+
| _ -> ())
505+
| {pvb_pat; pvb_expr} when locHasCursor pvb_pat.ppat_loc -> (
506+
(* Completing a destructuring.
507+
E.g: let {<com>} = someVar *)
508+
match
509+
( pvb_pat
510+
|> CompletionPatterns.traversePattern ~patternPath:[] ~locHasCursor
511+
~firstCharBeforeCursorNoWhite ~posBeforeCursor,
512+
exprToContextPath pvb_expr )
513+
with
514+
| Some (prefix, nested), Some ctxPath ->
515+
setResult
516+
(Completable.Cpattern
517+
{
518+
contextPath = ctxPath;
519+
prefix;
520+
nested = List.rev nested;
521+
fallback = None;
522+
})
523+
| _ -> ())
524+
| _ -> ());
493525
Ast_iterator.default_iterator.value_binding iterator value_binding;
494526
inJsxContext := oldInJsxContext
495527
in

analysis/src/SharedTypes.ml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ type field = {
3434
docstring: string list;
3535
}
3636

37-
type completionType = TypeExpr of Types.type_expr | InlineRecord of field list
38-
3937
type constructorArgs =
4038
| InlineRecord of field list
4139
| Args of (Types.type_expr * Location.t) list
@@ -142,6 +140,11 @@ module Declared = struct
142140
}
143141
end
144142

143+
type completionType =
144+
| TypeExpr of Types.type_expr
145+
| InlineRecord of field list
146+
| ResolvedType of Type.t
147+
145148
module Stamps : sig
146149
type t
147150

analysis/src/TypeUtils.ml

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -242,21 +242,33 @@ let rec resolveTypeForPipeCompletion ~env ~package ~lhsLoc ~full
242242
in
243243
digToRelevantType ~env ~package t)
244244

245+
let extractTypeFromCompletionType (t : completionType) ~env ~full =
246+
match t with
247+
| TypeExpr t -> t |> extractType ~env ~package:full.package
248+
| InlineRecord fields -> Some (TinlineRecord {env; fields})
249+
| ResolvedType typ -> (
250+
match typ.kind with
251+
| Tuple items -> Some (Tuple (env, items, Ctype.newty (Ttuple items)))
252+
| Record fields -> Some (TinlineRecord {env; fields})
253+
| Variant constructors ->
254+
Some
255+
(Tvariant {env; constructors; variantName = ""; variantDecl = typ.decl})
256+
| Abstract _ | Open -> (
257+
match typ.decl.type_manifest with
258+
| None -> None
259+
| Some t -> t |> extractType ~env ~package:full.package))
260+
245261
(** This moves through a nested path via a set of instructions, trying to resolve the type at the end of the path. *)
246-
let rec resolveNested (typ : completionType) ~env ~package ~nested =
262+
let rec resolveNested (typ : completionType) ~env ~full ~nested =
247263
match nested with
248264
| [] -> Some (typ, env, None)
249265
| patternPath :: nested -> (
250-
let extractedType =
251-
match typ with
252-
| TypeExpr typ -> typ |> extractType ~env ~package
253-
| InlineRecord fields -> Some (TinlineRecord {env; fields})
254-
in
266+
let extractedType = typ |> extractTypeFromCompletionType ~env ~full in
255267
match (patternPath, extractedType) with
256268
| Completable.NTupleItem {itemNum}, Some (Tuple (env, tupleItems, _)) -> (
257269
match List.nth_opt tupleItems itemNum with
258270
| None -> None
259-
| Some typ -> TypeExpr typ |> resolveNested ~env ~package ~nested)
271+
| Some typ -> TypeExpr typ |> resolveNested ~env ~full ~nested)
260272
| ( NFollowRecordField {fieldName},
261273
Some (TinlineRecord {env; fields} | Trecord {env; fields}) ) -> (
262274
match
@@ -266,15 +278,15 @@ let rec resolveNested (typ : completionType) ~env ~package ~nested =
266278
| None -> None
267279
| Some {typ; optional} ->
268280
let typ = if optional then Utils.unwrapIfOption typ else typ in
269-
TypeExpr typ |> resolveNested ~env ~package ~nested)
281+
TypeExpr typ |> resolveNested ~env ~full ~nested)
270282
| NRecordBody {seenFields}, Some (Trecord {env; typeExpr}) ->
271283
Some (TypeExpr typeExpr, env, Some (Completable.RecordField {seenFields}))
272284
| NRecordBody {seenFields}, Some (TinlineRecord {env; fields}) ->
273285
Some
274286
(InlineRecord fields, env, Some (Completable.RecordField {seenFields}))
275287
| ( NVariantPayload {constructorName = "Some"; itemNum = 0},
276288
Some (Toption (env, typ)) ) ->
277-
TypeExpr typ |> resolveNested ~env ~package ~nested
289+
TypeExpr typ |> resolveNested ~env ~full ~nested
278290
| ( NVariantPayload {constructorName; itemNum},
279291
Some (Tvariant {env; constructors}) ) -> (
280292
match
@@ -285,9 +297,9 @@ let rec resolveNested (typ : completionType) ~env ~package ~nested =
285297
| Some {args = Args args} -> (
286298
match List.nth_opt args itemNum with
287299
| None -> None
288-
| Some (typ, _) -> TypeExpr typ |> resolveNested ~env ~package ~nested)
300+
| Some (typ, _) -> TypeExpr typ |> resolveNested ~env ~full ~nested)
289301
| Some {args = InlineRecord fields} when itemNum = 0 ->
290-
InlineRecord fields |> resolveNested ~env ~package ~nested
302+
InlineRecord fields |> resolveNested ~env ~full ~nested
291303
| _ -> None)
292304
| ( NPolyvariantPayload {constructorName; itemNum},
293305
Some (Tpolyvariant {env; constructors}) ) -> (
@@ -300,9 +312,9 @@ let rec resolveNested (typ : completionType) ~env ~package ~nested =
300312
| Some constructor -> (
301313
match List.nth_opt constructor.args itemNum with
302314
| None -> None
303-
| Some typ -> TypeExpr typ |> resolveNested ~env ~package ~nested))
315+
| Some typ -> TypeExpr typ |> resolveNested ~env ~full ~nested))
304316
| NArray, Some (Tarray (env, typ)) ->
305-
TypeExpr typ |> resolveNested ~env ~package ~nested
317+
TypeExpr typ |> resolveNested ~env ~full ~nested
306318
| _ -> None)
307319

308320
let getArgs ~env (t : Types.type_expr) ~full =
@@ -344,3 +356,9 @@ let typeIsUnit (typ : Types.type_expr) =
344356
when Ident.name id = "unit" ->
345357
true
346358
| _ -> false
359+
360+
let contextPathFromCoreType (coreType : Parsetree.core_type) =
361+
match coreType.ptyp_desc with
362+
| Ptyp_constr (loc, []) ->
363+
Some (Completable.CPId (loc.txt |> Utils.flattenLongIdent, Type))
364+
| _ -> None
Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,67 @@
11
Complete src/CompletionTypeAnnotation.res 9:22
22
XXX Not found!
3-
[]
3+
Completable: Cexpression Type[someRecord]
4+
[{
5+
"label": "{}",
6+
"kind": 4,
7+
"tags": [],
8+
"detail": "Inline record",
9+
"documentation": null,
10+
"sortText": "A",
11+
"insertText": "{$0}",
12+
"insertTextFormat": 2
13+
}]
414

515
Complete src/CompletionTypeAnnotation.res 12:24
6-
posCursor:[12:24] posNoWhite:[12:23] Found expr:[12:23->12:25]
7-
[]
16+
XXX Not found!
17+
Completable: Cexpression Type[someRecord]->recordBody
18+
[{
19+
"label": "age",
20+
"kind": 4,
21+
"tags": [],
22+
"detail": "Inline record",
23+
"documentation": null
24+
}, {
25+
"label": "name",
26+
"kind": 4,
27+
"tags": [],
28+
"detail": "Inline record",
29+
"documentation": null
30+
}]
831

932
Complete src/CompletionTypeAnnotation.res 15:23
1033
XXX Not found!
11-
[]
34+
Completable: Cexpression Type[someVariant]
35+
[{
36+
"label": "One",
37+
"kind": 4,
38+
"tags": [],
39+
"detail": "One\n\ntype = One | Two(bool)",
40+
"documentation": null,
41+
"insertText": "One",
42+
"insertTextFormat": 2
43+
}, {
44+
"label": "Two(_)",
45+
"kind": 4,
46+
"tags": [],
47+
"detail": "Two(bool)\n\ntype = One | Two(bool)",
48+
"documentation": null,
49+
"insertText": "Two(${1:_})",
50+
"insertTextFormat": 2
51+
}]
1252

1353
Complete src/CompletionTypeAnnotation.res 18:25
14-
posCursor:[18:25] posNoWhite:[18:24] Found expr:[18:24->18:25]
15-
Pexp_construct O:[18:24->18:25] None
16-
Completable: Cpath Value[O]
54+
XXX Not found!
55+
Completable: Cexpression Type[someVariant]=O
1756
[{
1857
"label": "One",
1958
"kind": 4,
2059
"tags": [],
21-
"detail": "One\n\ntype someVariant = One | Two(bool)",
22-
"documentation": null
60+
"detail": "One\n\ntype = One | Two(bool)",
61+
"documentation": null,
62+
"sortText": "A One",
63+
"insertText": "One",
64+
"insertTextFormat": 2
2365
}, {
2466
"label": "Obj",
2567
"kind": 9,
@@ -36,9 +78,35 @@ Completable: Cpath Value[O]
3678

3779
Complete src/CompletionTypeAnnotation.res 21:27
3880
XXX Not found!
39-
[]
81+
Completable: Cexpression Type[somePolyVariant]
82+
[{
83+
"label": "#one",
84+
"kind": 4,
85+
"tags": [],
86+
"detail": "#one\n\n[#one | #two(bool)]",
87+
"documentation": null,
88+
"insertText": "#one",
89+
"insertTextFormat": 2
90+
}, {
91+
"label": "#two(_)",
92+
"kind": 4,
93+
"tags": [],
94+
"detail": "#two(bool)\n\n[#one | #two(bool)]",
95+
"documentation": null,
96+
"insertText": "#two(${1:_})",
97+
"insertTextFormat": 2
98+
}]
4099

41100
Complete src/CompletionTypeAnnotation.res 24:30
42-
posCursor:[24:30] posNoWhite:[24:29] Found expr:[24:28->24:30]
43-
[]
101+
XXX Not found!
102+
Completable: Cexpression Type[somePolyVariant]=#o
103+
[{
104+
"label": "#one",
105+
"kind": 4,
106+
"tags": [],
107+
"detail": "#one\n\n[#one | #two(bool)]",
108+
"documentation": null,
109+
"insertText": "one",
110+
"insertTextFormat": 2
111+
}]
44112

0 commit comments

Comments
 (0)