From dc2472d50e51a04373084815d017f71db74e0d44 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 21 Jan 2023 14:00:21 +0100 Subject: [PATCH 01/14] add tests to focus on for first iteration --- .../tests/src/CompletionTypeAnnotation.res | 26 +++++++++++ .../expected/CompletionTypeAnnotation.res.txt | 44 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 analysis/tests/src/CompletionTypeAnnotation.res create mode 100644 analysis/tests/src/expected/CompletionTypeAnnotation.res.txt diff --git a/analysis/tests/src/CompletionTypeAnnotation.res b/analysis/tests/src/CompletionTypeAnnotation.res new file mode 100644 index 000000000..4a2df9661 --- /dev/null +++ b/analysis/tests/src/CompletionTypeAnnotation.res @@ -0,0 +1,26 @@ +type someRecord = { + age: int, + name: string, +} + +type someVariant = One | Two(bool) + +type somePolyVariant = [#one | #two(bool)] + +// let x: someRecord = +// ^com + +// let x: someRecord = {} +// ^com + +// let x: someVariant = +// ^com + +// let x: someVariant = O +// ^com + +// let x: somePolyVariant = +// ^com + +// let x: somePolyVariant = #o +// ^com diff --git a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt new file mode 100644 index 000000000..88a6a0ae0 --- /dev/null +++ b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt @@ -0,0 +1,44 @@ +Complete src/CompletionTypeAnnotation.res 9:22 +XXX Not found! +[] + +Complete src/CompletionTypeAnnotation.res 12:24 +posCursor:[12:24] posNoWhite:[12:23] Found expr:[12:23->12:25] +[] + +Complete src/CompletionTypeAnnotation.res 15:23 +XXX Not found! +[] + +Complete src/CompletionTypeAnnotation.res 18:25 +posCursor:[18:25] posNoWhite:[18:24] Found expr:[18:24->18:25] +Pexp_construct O:[18:24->18:25] None +Completable: Cpath Value[O] +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\ntype someVariant = One | Two(bool)", + "documentation": null + }, { + "label": "Obj", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }, { + "label": "Object", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }] + +Complete src/CompletionTypeAnnotation.res 21:27 +XXX Not found! +[] + +Complete src/CompletionTypeAnnotation.res 24:30 +posCursor:[24:30] posNoWhite:[24:29] Found expr:[24:28->24:30] +[] + From 1d1f4f6c6cc8ae6a045fa05800f387c3f1d62507 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 21 Jan 2023 18:48:28 +0100 Subject: [PATCH 02/14] initial basic version of leveraging type annotations when completing --- analysis/src/CompletionBackEnd.ml | 25 +++-- analysis/src/CompletionFrontEnd.ml | 74 ++++++++++----- analysis/src/SharedTypes.ml | 7 +- analysis/src/TypeUtils.ml | 44 ++++++--- .../expected/CompletionTypeAnnotation.res.txt | 92 ++++++++++++++++--- 5 files changed, 180 insertions(+), 62 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index f503721cb..479e0892b 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -582,6 +582,13 @@ let completionsGetTypeEnv = function | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) | _ -> None +let completionsGetCompletionType = function + | {Completion.kind = Value typ; env} :: _ -> Some (TypeExpr typ, env) + | {Completion.kind = ObjLabel typ; env} :: _ -> Some (TypeExpr typ, env) + | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (TypeExpr typ, env) + | {Completion.kind = Type typ; env} :: _ -> Some (ResolvedType typ, env) + | _ -> None + let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env ~exact ~scope (contextPath : Completable.contextPath) = let package = full.package in @@ -996,11 +1003,7 @@ type completionMode = Pattern | Expression let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix ~completionContext ~mode = - let extractedType = - match t with - | TypeExpr t -> t |> TypeUtils.extractType ~env ~package:full.package - | InlineRecord fields -> Some (TinlineRecord {env; fields}) - in + let extractedType = t |> TypeUtils.extractTypeFromCompletionType ~env ~full in match extractedType with | Some (Tbool env) -> [ @@ -1308,10 +1311,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover |> completionsGetTypeEnv with | Some (typ, env) -> ( - match - TypeExpr typ - |> TypeUtils.resolveNested ~env ~package:full.package ~nested - with + match TypeExpr typ |> TypeUtils.resolveNested ~env ~full ~nested with | None -> fallbackOrEmpty () | Some (typ, env, completionContext) -> let items = @@ -1326,14 +1326,11 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover contextPath |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env ~exact:true ~scope - |> completionsGetTypeEnv + |> completionsGetCompletionType with | None -> [] | Some (typ, env) -> ( - match - TypeExpr typ - |> TypeUtils.resolveNested ~env ~package:full.package ~nested - with + match typ |> TypeUtils.resolveNested ~env ~full ~nested with | None -> [] | Some (typ, env, completionContext) -> ( let isJsx = diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 0d28404c9..c54e44a07 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -316,27 +316,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | _ -> () in let scopeValueBinding (vb : Parsetree.value_binding) = - scopePattern vb.pvb_pat; - (* Identify relevant destructures for completion, like `let {} = someVar` or `let (true, false) = someFn()`. *) - match vb with - | {pvb_pat; pvb_expr} when locHasCursor pvb_pat.ppat_loc -> ( - match - ( pvb_pat - |> CompletionPatterns.traversePattern ~patternPath:[] ~locHasCursor - ~firstCharBeforeCursorNoWhite ~posBeforeCursor, - exprToContextPath pvb_expr ) - with - | Some (prefix, nestedPattern), Some ctxPath -> - setResult - (Completable.Cpattern - { - contextPath = ctxPath; - prefix; - nested = List.rev nestedPattern; - fallback = None; - }) - | _ -> ()) - | _ -> () + scopePattern vb.pvb_pat in let scopeTypeKind (tk : Parsetree.type_kind) = match tk with @@ -490,6 +470,58 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (value_binding : Parsetree.value_binding) = let oldInJsxContext = !inJsxContext in if Utils.isReactComponent value_binding then inJsxContext := true; + (match value_binding with + | {pvb_pat = {ppat_desc = Ppat_constraint (_pat, coreType)}; pvb_expr} + when locHasCursor pvb_expr.pexp_loc -> ( + (* Expression with derivable type annotation. + E.g: let x: someRecord = {} *) + match + ( TypeUtils.contextPathFromCoreType coreType, + pvb_expr + |> CompletionExpressions.traverseExpr ~exprPath:[] + ~pos:posBeforeCursor ~firstCharBeforeCursorNoWhite ) + with + | Some ctxPath, Some (prefix, nested) -> + setResult + (Completable.Cexpression + {contextPath = ctxPath; prefix; nested = List.rev nested}) + | _ -> ()) + | { + pvb_pat = {ppat_desc = Ppat_constraint (_pat, coreType); ppat_loc}; + pvb_expr; + } + when locHasCursor value_binding.pvb_loc + && locHasCursor ppat_loc = false + && locHasCursor pvb_expr.pexp_loc = false + && CompletionExpressions.isExprHole pvb_expr -> ( + (* Expression with derivable type annotation, when the expression is empty (expr hole). + E.g: let x: someRecord = *) + match TypeUtils.contextPathFromCoreType coreType with + | Some ctxPath -> + setResult + (Completable.Cexpression + {contextPath = ctxPath; prefix = ""; nested = []}) + | _ -> ()) + | {pvb_pat; pvb_expr} when locHasCursor pvb_pat.ppat_loc -> ( + (* Completing a destructuring. + E.g: let {} = someVar *) + match + ( pvb_pat + |> CompletionPatterns.traversePattern ~patternPath:[] ~locHasCursor + ~firstCharBeforeCursorNoWhite ~posBeforeCursor, + exprToContextPath pvb_expr ) + with + | Some (prefix, nested), Some ctxPath -> + setResult + (Completable.Cpattern + { + contextPath = ctxPath; + prefix; + nested = List.rev nested; + fallback = None; + }) + | _ -> ()) + | _ -> ()); Ast_iterator.default_iterator.value_binding iterator value_binding; inJsxContext := oldInJsxContext in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 638246fb4..1579b2ad3 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -34,8 +34,6 @@ type field = { docstring: string list; } -type completionType = TypeExpr of Types.type_expr | InlineRecord of field list - type constructorArgs = | InlineRecord of field list | Args of (Types.type_expr * Location.t) list @@ -142,6 +140,11 @@ module Declared = struct } end +type completionType = + | TypeExpr of Types.type_expr + | InlineRecord of field list + | ResolvedType of Type.t + module Stamps : sig type t diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 3535efccc..0a0924359 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -242,21 +242,33 @@ let rec resolveTypeForPipeCompletion ~env ~package ~lhsLoc ~full in digToRelevantType ~env ~package t) +let extractTypeFromCompletionType (t : completionType) ~env ~full = + match t with + | TypeExpr t -> t |> extractType ~env ~package:full.package + | InlineRecord fields -> Some (TinlineRecord {env; fields}) + | ResolvedType typ -> ( + match typ.kind with + | Tuple items -> Some (Tuple (env, items, Ctype.newty (Ttuple items))) + | Record fields -> Some (TinlineRecord {env; fields}) + | Variant constructors -> + Some + (Tvariant {env; constructors; variantName = ""; variantDecl = typ.decl}) + | Abstract _ | Open -> ( + match typ.decl.type_manifest with + | None -> None + | Some t -> t |> extractType ~env ~package:full.package)) + (** This moves through a nested path via a set of instructions, trying to resolve the type at the end of the path. *) -let rec resolveNested (typ : completionType) ~env ~package ~nested = +let rec resolveNested (typ : completionType) ~env ~full ~nested = match nested with | [] -> Some (typ, env, None) | patternPath :: nested -> ( - let extractedType = - match typ with - | TypeExpr typ -> typ |> extractType ~env ~package - | InlineRecord fields -> Some (TinlineRecord {env; fields}) - in + let extractedType = typ |> extractTypeFromCompletionType ~env ~full in match (patternPath, extractedType) with | Completable.NTupleItem {itemNum}, Some (Tuple (env, tupleItems, _)) -> ( match List.nth_opt tupleItems itemNum with | None -> None - | Some typ -> TypeExpr typ |> resolveNested ~env ~package ~nested) + | Some typ -> TypeExpr typ |> resolveNested ~env ~full ~nested) | ( NFollowRecordField {fieldName}, Some (TinlineRecord {env; fields} | Trecord {env; fields}) ) -> ( match @@ -266,7 +278,7 @@ let rec resolveNested (typ : completionType) ~env ~package ~nested = | None -> None | Some {typ; optional} -> let typ = if optional then Utils.unwrapIfOption typ else typ in - TypeExpr typ |> resolveNested ~env ~package ~nested) + TypeExpr typ |> resolveNested ~env ~full ~nested) | NRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> Some (TypeExpr typeExpr, env, Some (Completable.RecordField {seenFields})) | NRecordBody {seenFields}, Some (TinlineRecord {env; fields}) -> @@ -274,7 +286,7 @@ let rec resolveNested (typ : completionType) ~env ~package ~nested = (InlineRecord fields, env, Some (Completable.RecordField {seenFields})) | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, Some (Toption (env, typ)) ) -> - TypeExpr typ |> resolveNested ~env ~package ~nested + TypeExpr typ |> resolveNested ~env ~full ~nested | ( NVariantPayload {constructorName; itemNum}, Some (Tvariant {env; constructors}) ) -> ( match @@ -285,9 +297,9 @@ let rec resolveNested (typ : completionType) ~env ~package ~nested = | Some {args = Args args} -> ( match List.nth_opt args itemNum with | None -> None - | Some (typ, _) -> TypeExpr typ |> resolveNested ~env ~package ~nested) + | Some (typ, _) -> TypeExpr typ |> resolveNested ~env ~full ~nested) | Some {args = InlineRecord fields} when itemNum = 0 -> - InlineRecord fields |> resolveNested ~env ~package ~nested + InlineRecord fields |> resolveNested ~env ~full ~nested | _ -> None) | ( NPolyvariantPayload {constructorName; itemNum}, Some (Tpolyvariant {env; constructors}) ) -> ( @@ -300,9 +312,9 @@ let rec resolveNested (typ : completionType) ~env ~package ~nested = | Some constructor -> ( match List.nth_opt constructor.args itemNum with | None -> None - | Some typ -> TypeExpr typ |> resolveNested ~env ~package ~nested)) + | Some typ -> TypeExpr typ |> resolveNested ~env ~full ~nested)) | NArray, Some (Tarray (env, typ)) -> - TypeExpr typ |> resolveNested ~env ~package ~nested + TypeExpr typ |> resolveNested ~env ~full ~nested | _ -> None) let getArgs ~env (t : Types.type_expr) ~full = @@ -344,3 +356,9 @@ let typeIsUnit (typ : Types.type_expr) = when Ident.name id = "unit" -> true | _ -> false + +let contextPathFromCoreType (coreType : Parsetree.core_type) = + match coreType.ptyp_desc with + | Ptyp_constr (loc, []) -> + Some (Completable.CPId (loc.txt |> Utils.flattenLongIdent, Type)) + | _ -> None diff --git a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt index 88a6a0ae0..4328452b3 100644 --- a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt +++ b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt @@ -1,25 +1,67 @@ Complete src/CompletionTypeAnnotation.res 9:22 XXX Not found! -[] +Completable: Cexpression Type[someRecord] +[{ + "label": "{}", + "kind": 4, + "tags": [], + "detail": "Inline record", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] Complete src/CompletionTypeAnnotation.res 12:24 -posCursor:[12:24] posNoWhite:[12:23] Found expr:[12:23->12:25] -[] +XXX Not found! +Completable: Cexpression Type[someRecord]->recordBody +[{ + "label": "age", + "kind": 4, + "tags": [], + "detail": "Inline record", + "documentation": null + }, { + "label": "name", + "kind": 4, + "tags": [], + "detail": "Inline record", + "documentation": null + }] Complete src/CompletionTypeAnnotation.res 15:23 XXX Not found! -[] +Completable: Cexpression Type[someVariant] +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\ntype = One | Two(bool)", + "documentation": null, + "insertText": "One", + "insertTextFormat": 2 + }, { + "label": "Two(_)", + "kind": 4, + "tags": [], + "detail": "Two(bool)\n\ntype = One | Two(bool)", + "documentation": null, + "insertText": "Two(${1:_})", + "insertTextFormat": 2 + }] Complete src/CompletionTypeAnnotation.res 18:25 -posCursor:[18:25] posNoWhite:[18:24] Found expr:[18:24->18:25] -Pexp_construct O:[18:24->18:25] None -Completable: Cpath Value[O] +XXX Not found! +Completable: Cexpression Type[someVariant]=O [{ "label": "One", "kind": 4, "tags": [], - "detail": "One\n\ntype someVariant = One | Two(bool)", - "documentation": null + "detail": "One\n\ntype = One | Two(bool)", + "documentation": null, + "sortText": "A One", + "insertText": "One", + "insertTextFormat": 2 }, { "label": "Obj", "kind": 9, @@ -36,9 +78,35 @@ Completable: Cpath Value[O] Complete src/CompletionTypeAnnotation.res 21:27 XXX Not found! -[] +Completable: Cexpression Type[somePolyVariant] +[{ + "label": "#one", + "kind": 4, + "tags": [], + "detail": "#one\n\n[#one | #two(bool)]", + "documentation": null, + "insertText": "#one", + "insertTextFormat": 2 + }, { + "label": "#two(_)", + "kind": 4, + "tags": [], + "detail": "#two(bool)\n\n[#one | #two(bool)]", + "documentation": null, + "insertText": "#two(${1:_})", + "insertTextFormat": 2 + }] Complete src/CompletionTypeAnnotation.res 24:30 -posCursor:[24:30] posNoWhite:[24:29] Found expr:[24:28->24:30] -[] +XXX Not found! +Completable: Cexpression Type[somePolyVariant]=#o +[{ + "label": "#one", + "kind": 4, + "tags": [], + "detail": "#one\n\n[#one | #two(bool)]", + "documentation": null, + "insertText": "one", + "insertTextFormat": 2 + }] From dd6b4fc66b8f0dea2689888045e48da95eb93954 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 21 Jan 2023 18:50:53 +0100 Subject: [PATCH 03/14] add more tests --- .../tests/src/CompletionTypeAnnotation.res | 13 +++++ .../expected/CompletionTypeAnnotation.res.txt | 58 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/analysis/tests/src/CompletionTypeAnnotation.res b/analysis/tests/src/CompletionTypeAnnotation.res index 4a2df9661..1662db78f 100644 --- a/analysis/tests/src/CompletionTypeAnnotation.res +++ b/analysis/tests/src/CompletionTypeAnnotation.res @@ -24,3 +24,16 @@ type somePolyVariant = [#one | #two(bool)] // let x: somePolyVariant = #o // ^com + +type someFunc = (int, string) => bool + +// let x: someFunc = +// ^com + +type someTuple = (bool, option) + +// let x: someTuple = +// ^com + +// let x: someTuple = (true, ) +// ^com diff --git a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt index 4328452b3..26c0f918b 100644 --- a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt +++ b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt @@ -110,3 +110,61 @@ Completable: Cexpression Type[somePolyVariant]=#o "insertTextFormat": 2 }] +Complete src/CompletionTypeAnnotation.res 29:20 +XXX Not found! +Completable: Cexpression Type[someFunc] +[{ + "label": "(v1, v2) => {}", + "kind": 12, + "tags": [], + "detail": "(int, string) => bool", + "documentation": null, + "sortText": "A", + "insertText": "(${1:v1}, ${2:v2}) => {$0}", + "insertTextFormat": 2 + }] + +Complete src/CompletionTypeAnnotation.res 34:21 +XXX Not found! +Completable: Cexpression Type[someTuple] +[{ + "label": "(_, _)", + "kind": 12, + "tags": [], + "detail": "(bool, option)", + "documentation": null, + "insertText": "(${1:_}, ${2:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionTypeAnnotation.res 37:28 +XXX Not found! +Completable: Cexpression Type[someTuple]->tuple($1) +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }, { + "label": "Some(true)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(false)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + From ee039639f4e5e4d5cd741ad303dd6064d23e2c0e Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 22 Jan 2023 13:37:48 +0100 Subject: [PATCH 04/14] pick up all props needed to print types both from type_expr and declarations --- analysis/src/CompletionBackEnd.ml | 10 ++- analysis/src/ProcessCmt.ml | 2 + analysis/src/SharedTypes.ml | 72 +++++++++---------- analysis/src/TypeUtils.ml | 44 ++++++++++-- .../expected/CompletionExpressions.res.txt | 6 +- .../src/expected/CompletionPattern.res.txt | 4 +- .../expected/CompletionTypeAnnotation.res.txt | 18 ++--- 7 files changed, 97 insertions(+), 59 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 479e0892b..c8c91830f 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -233,6 +233,7 @@ let detail name (kind : Completion.kind) = ^ ")") ^ "\n\n" ^ s | Snippet s -> s + | ExtractedType extractedType -> TypeUtils.extractedTypeToString extractedType let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed ~(completionContext : Completable.completionContext) = @@ -587,6 +588,8 @@ let completionsGetCompletionType = function | {Completion.kind = ObjLabel typ; env} :: _ -> Some (TypeExpr typ, env) | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (TypeExpr typ, env) | {Completion.kind = Type typ; env} :: _ -> Some (ResolvedType typ, env) + | {Completion.kind = ExtractedType typ; env} :: _ -> + Some (ExtractedType typ, env) | _ -> None let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env @@ -1083,7 +1086,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix ~insertText:(printConstructorArgs numExprs ~asSnippet:true) ~kind:(Value typ) ~env (); ] - | Some (Trecord {env; fields; typeExpr}) -> ( + | Some (Trecord {env; fields} as extractedType) -> ( (* As we're completing for a record, we'll need a hint (completionContext) here to figure out whether we should complete for a record field, or the record body itself. *) @@ -1094,7 +1097,8 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix List.mem field.fname.txt seenFields = false) |> List.map (fun (field : field) -> Completion.create field.fname.txt - ~kind:(Field (field, typeExpr |> Shared.typeToString)) + ~kind: + (Field (field, TypeUtils.extractedTypeToString extractedType)) ~env) |> filterItems ~prefix | None -> @@ -1102,7 +1106,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix [ Completion.createWithSnippet ~name:"{}" ~insertText:(if !Cfg.supportsSnippets then "{$0}" else "{}") - ~sortText:"A" ~kind:(Value typeExpr) ~env (); + ~sortText:"A" ~kind:(ExtractedType extractedType) ~env (); ] else []) | Some (TinlineRecord {env; fields}) -> ( diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index 2865dc02d..a578c1dd1 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -64,6 +64,7 @@ let rec forTypeSignatureItem ~(env : SharedTypes.Env.t) ~(exported : Exported.t) ~item: { Type.decl; + name = ident.name; kind = (match type_kind with | Type_abstract -> ( @@ -167,6 +168,7 @@ let forTypeDeclaration ~env ~(exported : Exported.t) ~item: { Type.decl = typ_type; + name = name.txt; kind = (match typ_kind with | Ttype_abstract -> ( diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 1579b2ad3..89ef9ce34 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -57,7 +57,7 @@ module Type = struct | Record of field list | Variant of Constructor.t list - type t = {kind: kind; decl: Types.type_declaration} + type t = {kind: kind; decl: Types.type_declaration; name: string} end module Exported = struct @@ -140,11 +140,6 @@ module Declared = struct } end -type completionType = - | TypeExpr of Types.type_expr - | InlineRecord of field list - | ResolvedType of Type.t - module Stamps : sig type t @@ -299,6 +294,38 @@ end type polyVariantConstructor = {name: string; args: Types.type_expr list} +(** An extracted type from a type expr *) +type extractedType = + | Tuple of QueryEnv.t * Types.type_expr list * Types.type_expr + | Toption of QueryEnv.t * Types.type_expr + | Tbool of QueryEnv.t + | Tarray of QueryEnv.t * Types.type_expr + | Tstring of QueryEnv.t + | Tvariant of { + env: QueryEnv.t; + constructors: Constructor.t list; + variantDecl: Types.type_declaration; + variantName: string; + } + | Tpolyvariant of { + env: QueryEnv.t; + constructors: polyVariantConstructor list; + typeExpr: Types.type_expr; + } + | Trecord of { + env: QueryEnv.t; + fields: field list; + name: [`Str of string | `TypeExpr of Types.type_expr]; + } + | TinlineRecord of {env: QueryEnv.t; fields: field list} + | Tfunction of {env: QueryEnv.t; args: typedFnArg list; typ: Types.type_expr} + +type completionType = + | TypeExpr of Types.type_expr + | InlineRecord of field list + | ResolvedType of Type.t + | ExtractedType of extractedType + module Completion = struct type kind = | Module of Module.t @@ -311,6 +338,7 @@ module Completion = struct | Field of field * string | FileModule of string | Snippet of string + | ExtractedType of extractedType type t = { name: string; @@ -361,7 +389,7 @@ module Completion = struct | ObjLabel _ -> 4 | Label _ -> 4 | Field (_, _) -> 5 - | Type _ -> 22 + | Type _ | ExtractedType _ -> 22 | Value _ -> 12 | Snippet _ -> 15 end @@ -625,36 +653,6 @@ module Completable = struct } | CexhaustiveSwitch of {contextPath: contextPath; exprLoc: Location.t} - (** An extracted type from a type expr *) - type extractedType = - | Tuple of QueryEnv.t * Types.type_expr list * Types.type_expr - | Toption of QueryEnv.t * Types.type_expr - | Tbool of QueryEnv.t - | Tarray of QueryEnv.t * Types.type_expr - | Tstring of QueryEnv.t - | Tvariant of { - env: QueryEnv.t; - constructors: Constructor.t list; - variantDecl: Types.type_declaration; - variantName: string; - } - | Tpolyvariant of { - env: QueryEnv.t; - constructors: polyVariantConstructor list; - typeExpr: Types.type_expr; - } - | Trecord of { - env: QueryEnv.t; - fields: field list; - typeExpr: Types.type_expr; - } - | TinlineRecord of {env: QueryEnv.t; fields: field list} - | Tfunction of { - env: QueryEnv.t; - args: typedFnArg list; - typ: Types.type_expr; - } - let toString = let completionContextToString = function | Value -> "Value" diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 0a0924359..8e8a79267 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -112,7 +112,7 @@ let rec extractType ~env ~package (t : Types.type_expr) = match t.desc with | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractType ~env ~package t1 | Tconstr (Path.Pident {name = "option"}, [payloadTypeExpr], _) -> - Some (Completable.Toption (env, payloadTypeExpr)) + Some (Toption (env, payloadTypeExpr)) | Tconstr (Path.Pident {name = "array"}, [payloadTypeExpr], _) -> Some (Tarray (env, payloadTypeExpr)) | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) @@ -126,7 +126,7 @@ let rec extractType ~env ~package (t : Types.type_expr) = (Tvariant {env; constructors; variantName = name.txt; variantDecl = decl}) | Some (env, {item = {kind = Record fields}}) -> - Some (Trecord {env; fields; typeExpr = t}) + Some (Trecord {env; fields; name = `TypeExpr t}) | _ -> None) | Ttuple expressions -> Some (Tuple (env, expressions, t)) | Tvariant {row_fields} -> @@ -244,15 +244,17 @@ let rec resolveTypeForPipeCompletion ~env ~package ~lhsLoc ~full let extractTypeFromCompletionType (t : completionType) ~env ~full = match t with + | ExtractedType extractedType -> Some extractedType | TypeExpr t -> t |> extractType ~env ~package:full.package | InlineRecord fields -> Some (TinlineRecord {env; fields}) | ResolvedType typ -> ( match typ.kind with | Tuple items -> Some (Tuple (env, items, Ctype.newty (Ttuple items))) - | Record fields -> Some (TinlineRecord {env; fields}) + | Record fields -> Some (Trecord {env; fields; name = `Str typ.name}) | Variant constructors -> Some - (Tvariant {env; constructors; variantName = ""; variantDecl = typ.decl}) + (Tvariant + {env; constructors; variantName = typ.name; variantDecl = typ.decl}) | Abstract _ | Open -> ( match typ.decl.type_manifest with | None -> None @@ -279,8 +281,15 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = | Some {typ; optional} -> let typ = if optional then Utils.unwrapIfOption typ else typ in TypeExpr typ |> resolveNested ~env ~full ~nested) - | NRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> + | NRecordBody {seenFields}, Some (Trecord {env; name = `TypeExpr typeExpr}) + -> Some (TypeExpr typeExpr, env, Some (Completable.RecordField {seenFields})) + | ( NRecordBody {seenFields}, + Some (Trecord {env; name = `Str _} as extractedType) ) -> + Some + ( ExtractedType extractedType, + env, + Some (Completable.RecordField {seenFields}) ) | NRecordBody {seenFields}, Some (TinlineRecord {env; fields}) -> Some (InlineRecord fields, env, Some (Completable.RecordField {seenFields})) @@ -362,3 +371,28 @@ let contextPathFromCoreType (coreType : Parsetree.core_type) = | Ptyp_constr (loc, []) -> Some (Completable.CPId (loc.txt |> Utils.flattenLongIdent, Type)) | _ -> None + +let printRecordFromFields ?name (fields : field list) = + (match name with + | None -> "" + | Some name -> "type " ^ name ^ " = ") + ^ "{" + ^ (fields + |> List.map (fun f -> f.fname.txt ^ ": " ^ Shared.typeToString f.typ) + |> String.concat ", ") + ^ "}" + +let extractedTypeToString = function + | Tuple (_, _, typ) + | Toption (_, typ) + | Tpolyvariant {typeExpr = typ} + | Tfunction {typ} + | Trecord {name = `TypeExpr typ} -> + Shared.typeToString typ + | Tbool _ -> "bool" + | Tstring _ -> "string" + | Tarray (_, innerTyp) -> "array<" ^ Shared.typeToString innerTyp ^ ">" + | Tvariant {variantDecl; variantName} -> + Shared.declToString variantName variantDecl + | Trecord {name = `Str name; fields} -> printRecordFromFields ~name fields + | TinlineRecord {fields} -> printRecordFromFields fields \ No newline at end of file diff --git a/analysis/tests/src/expected/CompletionExpressions.res.txt b/analysis/tests/src/expected/CompletionExpressions.res.txt index e0a1cec63..ee05c6265 100644 --- a/analysis/tests/src/expected/CompletionExpressions.res.txt +++ b/analysis/tests/src/expected/CompletionExpressions.res.txt @@ -169,7 +169,7 @@ Completable: Cexpression CArgument Value[fnTakingRecord]($0)->recordField(nested "insertTextFormat": 2 }, { "label": "Some({})", - "kind": 12, + "kind": 22, "tags": [], "detail": "otherRecord", "documentation": null, @@ -264,7 +264,7 @@ Pexp_apply ...[56:11->56:25] (...[56:26->56:60]) Completable: Cexpression CArgument Value[fnTakingRecord]($0)->recordField(polyvariant), polyvariantPayload::three($0) [{ "label": "{}", - "kind": 12, + "kind": 22, "tags": [], "detail": "someRecord", "documentation": null, @@ -590,7 +590,7 @@ Pexp_apply ...[135:11->135:31] (...[135:32->135:66]) Completable: Cexpression CArgument Value[fnTakingInlineRecord]($0)->variantPayload::WithInlineRecord($0), recordField(nestedRecord) [{ "label": "{}", - "kind": 12, + "kind": 22, "tags": [], "detail": "otherRecord", "documentation": null, diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index ff00ec0a6..16d5f5818 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -76,7 +76,7 @@ XXX Not found! Completable: Cpattern Value[f] [{ "label": "{}", - "kind": 12, + "kind": 22, "tags": [], "detail": "someRecord", "documentation": null, @@ -166,7 +166,7 @@ posCursor:[61:22] posNoWhite:[61:21] Found pattern:[61:16->61:25] Completable: Cpattern Value[f]->recordField(nest) [{ "label": "{}", - "kind": 12, + "kind": 22, "tags": [], "detail": "nestedRecord", "documentation": null, diff --git a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt index 26c0f918b..1aabb6bfe 100644 --- a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt +++ b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt @@ -3,9 +3,9 @@ XXX Not found! Completable: Cexpression Type[someRecord] [{ "label": "{}", - "kind": 4, + "kind": 22, "tags": [], - "detail": "Inline record", + "detail": "type someRecord = {age: int, name: string}", "documentation": null, "sortText": "A", "insertText": "{$0}", @@ -17,15 +17,15 @@ XXX Not found! Completable: Cexpression Type[someRecord]->recordBody [{ "label": "age", - "kind": 4, + "kind": 5, "tags": [], - "detail": "Inline record", + "detail": "age: int\n\ntype someRecord = {age: int, name: string}", "documentation": null }, { "label": "name", - "kind": 4, + "kind": 5, "tags": [], - "detail": "Inline record", + "detail": "name: string\n\ntype someRecord = {age: int, name: string}", "documentation": null }] @@ -36,7 +36,7 @@ Completable: Cexpression Type[someVariant] "label": "One", "kind": 4, "tags": [], - "detail": "One\n\ntype = One | Two(bool)", + "detail": "One\n\ntype someVariant = One | Two(bool)", "documentation": null, "insertText": "One", "insertTextFormat": 2 @@ -44,7 +44,7 @@ Completable: Cexpression Type[someVariant] "label": "Two(_)", "kind": 4, "tags": [], - "detail": "Two(bool)\n\ntype = One | Two(bool)", + "detail": "Two(bool)\n\ntype someVariant = One | Two(bool)", "documentation": null, "insertText": "Two(${1:_})", "insertTextFormat": 2 @@ -57,7 +57,7 @@ Completable: Cexpression Type[someVariant]=O "label": "One", "kind": 4, "tags": [], - "detail": "One\n\ntype = One | Two(bool)", + "detail": "One\n\ntype someVariant = One | Two(bool)", "documentation": null, "sortText": "A One", "insertText": "One", From 34b52b86d3b237d07610a3a9667a7d663a1e966a Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 09:20:46 +0100 Subject: [PATCH 05/14] get rid off two redundant variant members for completionType --- analysis/src/CompletionBackEnd.ml | 9 ++++++--- analysis/src/SharedTypes.ml | 2 -- analysis/src/TypeUtils.ml | 33 +++++++++++++++++-------------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index c8c91830f..54fbb4928 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -583,11 +583,14 @@ let completionsGetTypeEnv = function | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) | _ -> None -let completionsGetCompletionType = function +let completionsGetCompletionType ~full = function | {Completion.kind = Value typ; env} :: _ -> Some (TypeExpr typ, env) | {Completion.kind = ObjLabel typ; env} :: _ -> Some (TypeExpr typ, env) | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (TypeExpr typ, env) - | {Completion.kind = Type typ; env} :: _ -> Some (ResolvedType typ, env) + | {Completion.kind = Type typ; env} :: _ -> ( + match TypeUtils.extractTypeFromResolvedType typ ~env ~full with + | None -> None + | Some extractedType -> Some (ExtractedType extractedType, env)) | {Completion.kind = ExtractedType typ; env} :: _ -> Some (ExtractedType typ, env) | _ -> None @@ -1330,7 +1333,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover contextPath |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env ~exact:true ~scope - |> completionsGetCompletionType + |> completionsGetCompletionType ~full with | None -> [] | Some (typ, env) -> ( diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 89ef9ce34..e330bc351 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -322,8 +322,6 @@ type extractedType = type completionType = | TypeExpr of Types.type_expr - | InlineRecord of field list - | ResolvedType of Type.t | ExtractedType of extractedType module Completion = struct diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 8e8a79267..5ee675146 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -242,23 +242,23 @@ let rec resolveTypeForPipeCompletion ~env ~package ~lhsLoc ~full in digToRelevantType ~env ~package t) +let extractTypeFromResolvedType (typ : Type.t) ~env ~full = + match typ.kind with + | Tuple items -> Some (Tuple (env, items, Ctype.newty (Ttuple items))) + | Record fields -> Some (Trecord {env; fields; name = `Str typ.name}) + | Variant constructors -> + Some + (Tvariant + {env; constructors; variantName = typ.name; variantDecl = typ.decl}) + | Abstract _ | Open -> ( + match typ.decl.type_manifest with + | None -> None + | Some t -> t |> extractType ~env ~package:full.package) + let extractTypeFromCompletionType (t : completionType) ~env ~full = match t with | ExtractedType extractedType -> Some extractedType | TypeExpr t -> t |> extractType ~env ~package:full.package - | InlineRecord fields -> Some (TinlineRecord {env; fields}) - | ResolvedType typ -> ( - match typ.kind with - | Tuple items -> Some (Tuple (env, items, Ctype.newty (Ttuple items))) - | Record fields -> Some (Trecord {env; fields; name = `Str typ.name}) - | Variant constructors -> - Some - (Tvariant - {env; constructors; variantName = typ.name; variantDecl = typ.decl}) - | Abstract _ | Open -> ( - match typ.decl.type_manifest with - | None -> None - | Some t -> t |> extractType ~env ~package:full.package)) (** This moves through a nested path via a set of instructions, trying to resolve the type at the end of the path. *) let rec resolveNested (typ : completionType) ~env ~full ~nested = @@ -292,7 +292,9 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = Some (Completable.RecordField {seenFields}) ) | NRecordBody {seenFields}, Some (TinlineRecord {env; fields}) -> Some - (InlineRecord fields, env, Some (Completable.RecordField {seenFields})) + ( ExtractedType (TinlineRecord {fields; env}), + env, + Some (Completable.RecordField {seenFields}) ) | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, Some (Toption (env, typ)) ) -> TypeExpr typ |> resolveNested ~env ~full ~nested @@ -308,7 +310,8 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = | None -> None | Some (typ, _) -> TypeExpr typ |> resolveNested ~env ~full ~nested) | Some {args = InlineRecord fields} when itemNum = 0 -> - InlineRecord fields |> resolveNested ~env ~full ~nested + ExtractedType (TinlineRecord {env; fields}) + |> resolveNested ~env ~full ~nested | _ -> None) | ( NPolyvariantPayload {constructorName; itemNum}, Some (Tpolyvariant {env; constructors}) ) -> ( From 5adfa08a27ca4c56db47ff694323e235e6035f9c Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 09:41:44 +0100 Subject: [PATCH 06/14] more unwinding of unecessary variant members for completion type --- analysis/src/CompletionBackEnd.ml | 100 +++++++++++++++++------------- analysis/src/SharedTypes.ml | 10 +-- analysis/src/TypeUtils.ml | 79 +++++++++++++---------- analysis/src/Utils.ml | 7 +++ 4 files changed, 112 insertions(+), 84 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 54fbb4928..e05d1b1bb 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -584,15 +584,17 @@ let completionsGetTypeEnv = function | _ -> None let completionsGetCompletionType ~full = function - | {Completion.kind = Value typ; env} :: _ -> Some (TypeExpr typ, env) - | {Completion.kind = ObjLabel typ; env} :: _ -> Some (TypeExpr typ, env) - | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (TypeExpr typ, env) + | {Completion.kind = Value typ; env} :: _ + | {Completion.kind = ObjLabel typ; env} :: _ + | {Completion.kind = Field ({typ}, _); env} :: _ -> + typ + |> TypeUtils.extractType ~env ~package:full.package + |> Option.map (fun typ -> (typ, env)) | {Completion.kind = Type typ; env} :: _ -> ( match TypeUtils.extractTypeFromResolvedType typ ~env ~full with | None -> None - | Some extractedType -> Some (ExtractedType extractedType, env)) - | {Completion.kind = ExtractedType typ; env} :: _ -> - Some (ExtractedType typ, env) + | Some extractedType -> Some (extractedType, env)) + | {Completion.kind = ExtractedType typ; env} :: _ -> Some (typ, env) | _ -> None let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env @@ -1007,17 +1009,16 @@ let printConstructorArgs argsLen ~asSnippet = type completionMode = Pattern | Expression -let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix +let rec completeTypedValue (t : SharedTypes.completionType) ~full ~prefix ~completionContext ~mode = - let extractedType = t |> TypeUtils.extractTypeFromCompletionType ~env ~full in - match extractedType with - | Some (Tbool env) -> + match t with + | Tbool env -> [ Completion.create "true" ~kind:(Label "bool") ~env; Completion.create "false" ~kind:(Label "bool") ~env; ] |> filterItems ~prefix - | Some (Tvariant {env; constructors; variantDecl; variantName}) -> + | Tvariant {env; constructors; variantDecl; variantName} -> constructors |> List.map (fun (constructor : Constructor.t) -> let numArgs = @@ -1037,7 +1038,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix (constructor, variantDecl |> Shared.declToString variantName)) ~env ()) |> filterItems ~prefix - | Some (Tpolyvariant {env; constructors; typeExpr}) -> + | Tpolyvariant {env; constructors; typeExpr} -> constructors |> List.map (fun (constructor : polyVariantConstructor) -> Completion.createWithSnippet @@ -1057,31 +1058,38 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix (constructor, typeExpr |> Shared.typeToString)) ~env ()) |> filterItems ~prefix - | Some (Toption (env, t)) -> - let innerType = Utils.unwrapIfOption t in + | Toption (env, t) -> + let innerType = + Utils.unwrapIfOption t |> TypeUtils.extractType ~env ~package:full.package + in let expandedCompletions = - TypeExpr innerType - |> completeTypedValue ~env ~full ~prefix ~completionContext ~mode - |> List.map (fun (c : Completion.t) -> - { - c with - name = "Some(" ^ c.name ^ ")"; - sortText = None; - insertText = - (match c.insertText with - | None -> None - | Some insertText -> Some ("Some(" ^ insertText ^ ")")); - }) + innerType + |> Option.map (fun innerType -> + innerType + |> completeTypedValue ~full ~prefix ~completionContext ~mode + |> List.map (fun (c : Completion.t) -> + { + c with + name = "Some(" ^ c.name ^ ")"; + sortText = None; + insertText = + (match c.insertText with + | None -> None + | Some insertText -> Some ("Some(" ^ insertText ^ ")")); + })) in - [ - Completion.create "None" ~kind:(Label (t |> Shared.typeToString)) ~env; - Completion.createWithSnippet ~name:"Some(_)" - ~kind:(Label (t |> Shared.typeToString)) - ~env ~insertText:"Some(${1:_})" (); - ] - @ expandedCompletions + ([ + Completion.create "None" ~kind:(Label (t |> Shared.typeToString)) ~env; + Completion.createWithSnippet ~name:"Some(_)" + ~kind:(Label (t |> Shared.typeToString)) + ~env ~insertText:"Some(${1:_})" (); + ] + @ + match expandedCompletions with + | None -> [] + | Some expandedCompletions -> expandedCompletions) |> filterItems ~prefix - | Some (Tuple (env, exprs, typ)) -> + | Tuple (env, exprs, typ) -> let numExprs = List.length exprs in [ Completion.createWithSnippet @@ -1089,7 +1097,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix ~insertText:(printConstructorArgs numExprs ~asSnippet:true) ~kind:(Value typ) ~env (); ] - | Some (Trecord {env; fields} as extractedType) -> ( + | Trecord {env; fields} as extractedType -> ( (* As we're completing for a record, we'll need a hint (completionContext) here to figure out whether we should complete for a record field, or the record body itself. *) @@ -1112,7 +1120,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix ~sortText:"A" ~kind:(ExtractedType extractedType) ~env (); ] else []) - | Some (TinlineRecord {env; fields}) -> ( + | TinlineRecord {env; fields} -> ( match completionContext with | Some (Completable.RecordField {seenFields}) -> fields @@ -1130,7 +1138,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix ~sortText:"A" ~kind:(Label "Inline record") ~env (); ] else []) - | Some (Tarray (env, typeExpr)) -> + | Tarray (env, typeExpr) -> if prefix = "" then [ Completion.createWithSnippet ~name:"[]" @@ -1138,7 +1146,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix ~sortText:"A" ~kind:(Value typeExpr) ~env (); ] else [] - | Some (Tstring env) -> + | Tstring env -> if prefix = "" then [ Completion.createWithSnippet ~name:"\"\"" @@ -1149,7 +1157,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix ~env (); ] else [] - | Some (Tfunction {env; typ; args}) when prefix = "" && mode = Expression -> + | Tfunction {env; typ; args} when prefix = "" && mode = Expression -> let prettyPrintArgTyp ?currentIndex (argTyp : Types.type_expr) = let indexText = match currentIndex with @@ -1318,13 +1326,17 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover |> completionsGetTypeEnv with | Some (typ, env) -> ( - match TypeExpr typ |> TypeUtils.resolveNested ~env ~full ~nested with + match + typ + |> TypeUtils.extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun typ -> + typ |> TypeUtils.resolveNested ~env ~full ~nested) + with | None -> fallbackOrEmpty () - | Some (typ, env, completionContext) -> + | Some (typ, _env, completionContext) -> let items = typ - |> completeTypedValue ~mode:Pattern ~env ~full ~prefix - ~completionContext + |> completeTypedValue ~mode:Pattern ~full ~prefix ~completionContext in fallbackOrEmpty ~items ()) | None -> fallbackOrEmpty ()) @@ -1347,7 +1359,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover in let items = typ - |> completeTypedValue ~mode:Expression ~env ~full ~prefix + |> completeTypedValue ~mode:Expression ~full ~prefix ~completionContext |> List.map (fun (c : Completion.t) -> if isJsx then diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index e330bc351..9d80f4caa 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -294,8 +294,8 @@ end type polyVariantConstructor = {name: string; args: Types.type_expr list} -(** An extracted type from a type expr *) -type extractedType = +(** An type that can be used to drive completion *) +type completionType = | Tuple of QueryEnv.t * Types.type_expr list * Types.type_expr | Toption of QueryEnv.t * Types.type_expr | Tbool of QueryEnv.t @@ -320,10 +320,6 @@ type extractedType = | TinlineRecord of {env: QueryEnv.t; fields: field list} | Tfunction of {env: QueryEnv.t; args: typedFnArg list; typ: Types.type_expr} -type completionType = - | TypeExpr of Types.type_expr - | ExtractedType of extractedType - module Completion = struct type kind = | Module of Module.t @@ -336,7 +332,7 @@ module Completion = struct | Field of field * string | FileModule of string | Snippet of string - | ExtractedType of extractedType + | ExtractedType of completionType type t = { name: string; diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 5ee675146..597e3b58f 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -255,24 +255,22 @@ let extractTypeFromResolvedType (typ : Type.t) ~env ~full = | None -> None | Some t -> t |> extractType ~env ~package:full.package) -let extractTypeFromCompletionType (t : completionType) ~env ~full = - match t with - | ExtractedType extractedType -> Some extractedType - | TypeExpr t -> t |> extractType ~env ~package:full.package - (** This moves through a nested path via a set of instructions, trying to resolve the type at the end of the path. *) let rec resolveNested (typ : completionType) ~env ~full ~nested = match nested with | [] -> Some (typ, env, None) | patternPath :: nested -> ( - let extractedType = typ |> extractTypeFromCompletionType ~env ~full in - match (patternPath, extractedType) with - | Completable.NTupleItem {itemNum}, Some (Tuple (env, tupleItems, _)) -> ( + match (patternPath, typ) with + | Completable.NTupleItem {itemNum}, Tuple (env, tupleItems, _) -> ( match List.nth_opt tupleItems itemNum with | None -> None - | Some typ -> TypeExpr typ |> resolveNested ~env ~full ~nested) + | Some typ -> + typ + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun typ -> + typ |> resolveNested ~env ~full ~nested)) | ( NFollowRecordField {fieldName}, - Some (TinlineRecord {env; fields} | Trecord {env; fields}) ) -> ( + (TinlineRecord {env; fields} | Trecord {env; fields}) ) -> ( match fields |> List.find_opt (fun (field : field) -> field.fname.txt = fieldName) @@ -280,26 +278,31 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = | None -> None | Some {typ; optional} -> let typ = if optional then Utils.unwrapIfOption typ else typ in - TypeExpr typ |> resolveNested ~env ~full ~nested) - | NRecordBody {seenFields}, Some (Trecord {env; name = `TypeExpr typeExpr}) + typ + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun typ -> + typ |> resolveNested ~env ~full ~nested)) + | NRecordBody {seenFields}, Trecord {env; name = `TypeExpr typeExpr} -> + typeExpr + |> extractType ~env ~package:full.package + |> Option.map (fun typ -> + (typ, env, Some (Completable.RecordField {seenFields}))) + | NRecordBody {seenFields}, (Trecord {env; name = `Str _} as extractedType) -> - Some (TypeExpr typeExpr, env, Some (Completable.RecordField {seenFields})) - | ( NRecordBody {seenFields}, - Some (Trecord {env; name = `Str _} as extractedType) ) -> + Some (extractedType, env, Some (Completable.RecordField {seenFields})) + | NRecordBody {seenFields}, TinlineRecord {env; fields} -> Some - ( ExtractedType extractedType, + ( TinlineRecord {fields; env}, env, Some (Completable.RecordField {seenFields}) ) - | NRecordBody {seenFields}, Some (TinlineRecord {env; fields}) -> - Some - ( ExtractedType (TinlineRecord {fields; env}), - env, - Some (Completable.RecordField {seenFields}) ) - | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, - Some (Toption (env, typ)) ) -> - TypeExpr typ |> resolveNested ~env ~full ~nested - | ( NVariantPayload {constructorName; itemNum}, - Some (Tvariant {env; constructors}) ) -> ( + | NVariantPayload {constructorName = "Some"; itemNum = 0}, Toption (env, typ) + -> + typ + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun typ -> + typ |> resolveNested ~env ~full ~nested) + | NVariantPayload {constructorName; itemNum}, Tvariant {env; constructors} + -> ( match constructors |> List.find_opt (fun (c : Constructor.t) -> @@ -308,13 +311,16 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = | Some {args = Args args} -> ( match List.nth_opt args itemNum with | None -> None - | Some (typ, _) -> TypeExpr typ |> resolveNested ~env ~full ~nested) + | Some (typ, _) -> + typ + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun typ -> + typ |> resolveNested ~env ~full ~nested)) | Some {args = InlineRecord fields} when itemNum = 0 -> - ExtractedType (TinlineRecord {env; fields}) - |> resolveNested ~env ~full ~nested + TinlineRecord {env; fields} |> resolveNested ~env ~full ~nested | _ -> None) | ( NPolyvariantPayload {constructorName; itemNum}, - Some (Tpolyvariant {env; constructors}) ) -> ( + Tpolyvariant {env; constructors} ) -> ( match constructors |> List.find_opt (fun (c : polyVariantConstructor) -> @@ -324,9 +330,16 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = | Some constructor -> ( match List.nth_opt constructor.args itemNum with | None -> None - | Some typ -> TypeExpr typ |> resolveNested ~env ~full ~nested)) - | NArray, Some (Tarray (env, typ)) -> - TypeExpr typ |> resolveNested ~env ~full ~nested + | Some typ -> + typ + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun typ -> + typ |> resolveNested ~env ~full ~nested))) + | NArray, Tarray (env, typ) -> + typ + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun typ -> + typ |> resolveNested ~env ~full ~nested) | _ -> None) let getArgs ~env (t : Types.type_expr) ~full = diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index 00468a339..0cb3d1ff7 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -212,3 +212,10 @@ let rec expandPath (path : Path.t) = | Pident id -> [Ident.name id] | Pdot (p, s, _) -> s :: expandPath p | Papply _ -> [] + +module Option = struct + let flatMap f o = + match o with + | None -> None + | Some v -> f v +end From 339d30f2d2390972f8c9520e6f14e258efa86314 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 09:43:57 +0100 Subject: [PATCH 07/14] clarify record name field a bit --- analysis/src/SharedTypes.ml | 6 +++++- analysis/src/TypeUtils.ml | 17 ++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 9d80f4caa..43f943095 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -315,7 +315,11 @@ type completionType = | Trecord of { env: QueryEnv.t; fields: field list; - name: [`Str of string | `TypeExpr of Types.type_expr]; + definition: + [ `NameOnly of string + (** When we only have the name, like when pulling the record from a declared type. *) + | `TypeExpr of Types.type_expr + (** When we have the full type expr from the compiler. *) ]; } | TinlineRecord of {env: QueryEnv.t; fields: field list} | Tfunction of {env: QueryEnv.t; args: typedFnArg list; typ: Types.type_expr} diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 597e3b58f..511ca9386 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -126,7 +126,7 @@ let rec extractType ~env ~package (t : Types.type_expr) = (Tvariant {env; constructors; variantName = name.txt; variantDecl = decl}) | Some (env, {item = {kind = Record fields}}) -> - Some (Trecord {env; fields; name = `TypeExpr t}) + Some (Trecord {env; fields; definition = `TypeExpr t}) | _ -> None) | Ttuple expressions -> Some (Tuple (env, expressions, t)) | Tvariant {row_fields} -> @@ -245,7 +245,8 @@ let rec resolveTypeForPipeCompletion ~env ~package ~lhsLoc ~full let extractTypeFromResolvedType (typ : Type.t) ~env ~full = match typ.kind with | Tuple items -> Some (Tuple (env, items, Ctype.newty (Ttuple items))) - | Record fields -> Some (Trecord {env; fields; name = `Str typ.name}) + | Record fields -> + Some (Trecord {env; fields; definition = `NameOnly typ.name}) | Variant constructors -> Some (Tvariant @@ -282,13 +283,14 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = |> extractType ~env ~package:full.package |> Utils.Option.flatMap (fun typ -> typ |> resolveNested ~env ~full ~nested)) - | NRecordBody {seenFields}, Trecord {env; name = `TypeExpr typeExpr} -> + | NRecordBody {seenFields}, Trecord {env; definition = `TypeExpr typeExpr} + -> typeExpr |> extractType ~env ~package:full.package |> Option.map (fun typ -> (typ, env, Some (Completable.RecordField {seenFields}))) - | NRecordBody {seenFields}, (Trecord {env; name = `Str _} as extractedType) - -> + | ( NRecordBody {seenFields}, + (Trecord {env; definition = `NameOnly _} as extractedType) ) -> Some (extractedType, env, Some (Completable.RecordField {seenFields})) | NRecordBody {seenFields}, TinlineRecord {env; fields} -> Some @@ -403,12 +405,13 @@ let extractedTypeToString = function | Toption (_, typ) | Tpolyvariant {typeExpr = typ} | Tfunction {typ} - | Trecord {name = `TypeExpr typ} -> + | Trecord {definition = `TypeExpr typ} -> Shared.typeToString typ | Tbool _ -> "bool" | Tstring _ -> "string" | Tarray (_, innerTyp) -> "array<" ^ Shared.typeToString innerTyp ^ ">" | Tvariant {variantDecl; variantName} -> Shared.declToString variantName variantDecl - | Trecord {name = `Str name; fields} -> printRecordFromFields ~name fields + | Trecord {definition = `NameOnly name; fields} -> + printRecordFromFields ~name fields | TinlineRecord {fields} -> printRecordFromFields fields \ No newline at end of file From 4c71789837d04e6ee5785569524ab87beb55391c Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 13:22:29 +0100 Subject: [PATCH 08/14] move Toption to use completion type as well --- analysis/src/CompletionBackEnd.ml | 47 +++++++++---------- analysis/src/SharedTypes.ml | 2 +- analysis/src/TypeUtils.ml | 19 ++++---- analysis/src/Utils.ml | 1 + .../expected/CompletionExpressions.res.txt | 4 +- .../CompletionFunctionArguments.res.txt | 2 +- 6 files changed, 37 insertions(+), 38 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index e05d1b1bb..4b95a5452 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1059,35 +1059,30 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~full ~prefix ~env ()) |> filterItems ~prefix | Toption (env, t) -> - let innerType = - Utils.unwrapIfOption t |> TypeUtils.extractType ~env ~package:full.package - in + let innerType = TypeUtils.unwrapCompletionTypeIfOption t in let expandedCompletions = innerType - |> Option.map (fun innerType -> - innerType - |> completeTypedValue ~full ~prefix ~completionContext ~mode - |> List.map (fun (c : Completion.t) -> - { - c with - name = "Some(" ^ c.name ^ ")"; - sortText = None; - insertText = - (match c.insertText with - | None -> None - | Some insertText -> Some ("Some(" ^ insertText ^ ")")); - })) + |> completeTypedValue ~full ~prefix ~completionContext ~mode + |> List.map (fun (c : Completion.t) -> + { + c with + name = "Some(" ^ c.name ^ ")"; + sortText = None; + insertText = + (match c.insertText with + | None -> None + | Some insertText -> Some ("Some(" ^ insertText ^ ")")); + }) in - ([ - Completion.create "None" ~kind:(Label (t |> Shared.typeToString)) ~env; - Completion.createWithSnippet ~name:"Some(_)" - ~kind:(Label (t |> Shared.typeToString)) - ~env ~insertText:"Some(${1:_})" (); - ] - @ - match expandedCompletions with - | None -> [] - | Some expandedCompletions -> expandedCompletions) + [ + Completion.create "None" + ~kind:(Label (t |> TypeUtils.extractedTypeToString)) + ~env; + Completion.createWithSnippet ~name:"Some(_)" + ~kind:(Label (t |> TypeUtils.extractedTypeToString)) + ~env ~insertText:"Some(${1:_})" (); + ] + @ expandedCompletions |> filterItems ~prefix | Tuple (env, exprs, typ) -> let numExprs = List.length exprs in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 43f943095..c7fab0433 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -297,7 +297,7 @@ type polyVariantConstructor = {name: string; args: Types.type_expr list} (** An type that can be used to drive completion *) type completionType = | Tuple of QueryEnv.t * Types.type_expr list * Types.type_expr - | Toption of QueryEnv.t * Types.type_expr + | Toption of QueryEnv.t * completionType | Tbool of QueryEnv.t | Tarray of QueryEnv.t * Types.type_expr | Tstring of QueryEnv.t diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 511ca9386..bcb2dc1a6 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -112,7 +112,8 @@ let rec extractType ~env ~package (t : Types.type_expr) = match t.desc with | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractType ~env ~package t1 | Tconstr (Path.Pident {name = "option"}, [payloadTypeExpr], _) -> - Some (Toption (env, payloadTypeExpr)) + payloadTypeExpr |> extractType ~env ~package + |> Option.map (fun payloadTyp -> Toption (env, payloadTyp)) | Tconstr (Path.Pident {name = "array"}, [payloadTypeExpr], _) -> Some (Tarray (env, payloadTypeExpr)) | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) @@ -299,10 +300,7 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = Some (Completable.RecordField {seenFields}) ) | NVariantPayload {constructorName = "Some"; itemNum = 0}, Toption (env, typ) -> - typ - |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~env ~full ~nested) + typ |> resolveNested ~env ~full ~nested | NVariantPayload {constructorName; itemNum}, Tvariant {env; constructors} -> ( match @@ -400,9 +398,8 @@ let printRecordFromFields ?name (fields : field list) = |> String.concat ", ") ^ "}" -let extractedTypeToString = function +let rec extractedTypeToString = function | Tuple (_, _, typ) - | Toption (_, typ) | Tpolyvariant {typeExpr = typ} | Tfunction {typ} | Trecord {definition = `TypeExpr typ} -> @@ -410,8 +407,14 @@ let extractedTypeToString = function | Tbool _ -> "bool" | Tstring _ -> "string" | Tarray (_, innerTyp) -> "array<" ^ Shared.typeToString innerTyp ^ ">" + | Toption (_, typ) -> "option<" ^ extractedTypeToString typ ^ ">" | Tvariant {variantDecl; variantName} -> Shared.declToString variantName variantDecl | Trecord {definition = `NameOnly name; fields} -> printRecordFromFields ~name fields - | TinlineRecord {fields} -> printRecordFromFields fields \ No newline at end of file + | TinlineRecord {fields} -> printRecordFromFields fields + +let unwrapCompletionTypeIfOption (t : SharedTypes.completionType) = + match t with + | Toption (_, unwrapped) -> unwrapped + | _ -> t diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index 0cb3d1ff7..c7f66b66c 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -168,6 +168,7 @@ let rec unwrapIfOption (t : Types.type_expr) = | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> unwrapIfOption t1 | Tconstr (Path.Pident {name = "option"}, [unwrappedType], _) -> unwrappedType | _ -> t + let isReactComponent (vb : Parsetree.value_binding) = vb.pvb_attributes |> List.exists (function diff --git a/analysis/tests/src/expected/CompletionExpressions.res.txt b/analysis/tests/src/expected/CompletionExpressions.res.txt index ee05c6265..6a4a73a9c 100644 --- a/analysis/tests/src/expected/CompletionExpressions.res.txt +++ b/analysis/tests/src/expected/CompletionExpressions.res.txt @@ -497,13 +497,13 @@ Completable: Cexpression CArgument Value[fnTakingRecordWithOptVariant]($0)->reco "label": "None", "kind": 4, "tags": [], - "detail": "someVariant", + "detail": "type someVariant = One | Two | Three(int, string)", "documentation": null }, { "label": "Some(_)", "kind": 4, "tags": [], - "detail": "someVariant", + "detail": "type someVariant = One | Two | Three(int, string)", "documentation": null, "insertText": "Some(${1:_})", "insertTextFormat": 2 diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 2f25a539a..a2b6236f6 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -158,7 +158,7 @@ Completable: Cexpression CArgument Value[someFnTakingVariant]($0)=So "label": "Some(_)", "kind": 4, "tags": [], - "detail": "someVariant", + "detail": "type someVariant = One | Two | Three(int, string)", "documentation": null, "sortText": "A Some(_)", "insertText": "Some(${1:_})", From b856a6d28e0fb647c1edc9711c75594667a94db5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 13:34:15 +0100 Subject: [PATCH 09/14] move Tarray to use completion type as well, plus clean up completion item kind output --- analysis/src/CompletionBackEnd.ml | 25 +++++++++++++++---- analysis/src/SharedTypes.ml | 8 +++--- analysis/src/TypeUtils.ml | 13 ++++------ .../expected/CompletionExpressions.res.txt | 6 ++--- .../src/expected/CompletionPattern.res.txt | 6 ++--- .../expected/CompletionTypeAnnotation.res.txt | 2 +- 6 files changed, 36 insertions(+), 24 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 4b95a5452..8e30449ee 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -233,7 +233,8 @@ let detail name (kind : Completion.kind) = ^ ")") ^ "\n\n" ^ s | Snippet s -> s - | ExtractedType extractedType -> TypeUtils.extractedTypeToString extractedType + | ExtractedType (extractedType, _) -> + TypeUtils.extractedTypeToString extractedType let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed ~(completionContext : Completable.completionContext) = @@ -594,7 +595,7 @@ let completionsGetCompletionType ~full = function match TypeUtils.extractTypeFromResolvedType typ ~env ~full with | None -> None | Some extractedType -> Some (extractedType, env)) - | {Completion.kind = ExtractedType typ; env} :: _ -> Some (typ, env) + | {Completion.kind = ExtractedType (typ, _); env} :: _ -> Some (typ, env) | _ -> None let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env @@ -1112,7 +1113,14 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~full ~prefix [ Completion.createWithSnippet ~name:"{}" ~insertText:(if !Cfg.supportsSnippets then "{$0}" else "{}") - ~sortText:"A" ~kind:(ExtractedType extractedType) ~env (); + ~sortText:"A" + ~kind: + (ExtractedType + ( extractedType, + match mode with + | Pattern -> `Type + | Expression -> `Value )) + ~env (); ] else []) | TinlineRecord {env; fields} -> ( @@ -1133,12 +1141,19 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~full ~prefix ~sortText:"A" ~kind:(Label "Inline record") ~env (); ] else []) - | Tarray (env, typeExpr) -> + | Tarray (env, typ) -> if prefix = "" then [ Completion.createWithSnippet ~name:"[]" ~insertText:(if !Cfg.supportsSnippets then "[$0]" else "[]") - ~sortText:"A" ~kind:(Value typeExpr) ~env (); + ~sortText:"A" + ~kind: + (ExtractedType + ( typ, + match mode with + | Pattern -> `Type + | Expression -> `Value )) + ~env (); ] else [] | Tstring env -> diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index c7fab0433..733cdbdb8 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -299,7 +299,7 @@ type completionType = | Tuple of QueryEnv.t * Types.type_expr list * Types.type_expr | Toption of QueryEnv.t * completionType | Tbool of QueryEnv.t - | Tarray of QueryEnv.t * Types.type_expr + | Tarray of QueryEnv.t * completionType | Tstring of QueryEnv.t | Tvariant of { env: QueryEnv.t; @@ -336,7 +336,7 @@ module Completion = struct | Field of field * string | FileModule of string | Snippet of string - | ExtractedType of completionType + | ExtractedType of completionType * [`Value | `Type] type t = { name: string; @@ -387,8 +387,8 @@ module Completion = struct | ObjLabel _ -> 4 | Label _ -> 4 | Field (_, _) -> 5 - | Type _ | ExtractedType _ -> 22 - | Value _ -> 12 + | Type _ | ExtractedType (_, `Type) -> 22 + | Value _ | ExtractedType (_, `Value) -> 12 | Snippet _ -> 15 end diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index bcb2dc1a6..219a03e5c 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -115,7 +115,8 @@ let rec extractType ~env ~package (t : Types.type_expr) = payloadTypeExpr |> extractType ~env ~package |> Option.map (fun payloadTyp -> Toption (env, payloadTyp)) | Tconstr (Path.Pident {name = "array"}, [payloadTypeExpr], _) -> - Some (Tarray (env, payloadTypeExpr)) + payloadTypeExpr |> extractType ~env ~package + |> Option.map (fun payloadTyp -> Tarray (env, payloadTyp)) | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) | Tconstr (Path.Pident {name = "string"}, [], _) -> Some (Tstring env) | Tconstr (path, _, _) -> ( @@ -335,11 +336,7 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = |> extractType ~env ~package:full.package |> Utils.Option.flatMap (fun typ -> typ |> resolveNested ~env ~full ~nested))) - | NArray, Tarray (env, typ) -> - typ - |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~env ~full ~nested) + | NArray, Tarray (env, typ) -> typ |> resolveNested ~env ~full ~nested | _ -> None) let getArgs ~env (t : Types.type_expr) ~full = @@ -406,8 +403,8 @@ let rec extractedTypeToString = function Shared.typeToString typ | Tbool _ -> "bool" | Tstring _ -> "string" - | Tarray (_, innerTyp) -> "array<" ^ Shared.typeToString innerTyp ^ ">" - | Toption (_, typ) -> "option<" ^ extractedTypeToString typ ^ ">" + | Tarray (_, innerTyp) -> "array<" ^ extractedTypeToString innerTyp ^ ">" + | Toption (_, innerTyp) -> "option<" ^ extractedTypeToString innerTyp ^ ">" | Tvariant {variantDecl; variantName} -> Shared.declToString variantName variantDecl | Trecord {definition = `NameOnly name; fields} -> diff --git a/analysis/tests/src/expected/CompletionExpressions.res.txt b/analysis/tests/src/expected/CompletionExpressions.res.txt index 6a4a73a9c..ae108ccd3 100644 --- a/analysis/tests/src/expected/CompletionExpressions.res.txt +++ b/analysis/tests/src/expected/CompletionExpressions.res.txt @@ -169,7 +169,7 @@ Completable: Cexpression CArgument Value[fnTakingRecord]($0)->recordField(nested "insertTextFormat": 2 }, { "label": "Some({})", - "kind": 22, + "kind": 12, "tags": [], "detail": "otherRecord", "documentation": null, @@ -264,7 +264,7 @@ Pexp_apply ...[56:11->56:25] (...[56:26->56:60]) Completable: Cexpression CArgument Value[fnTakingRecord]($0)->recordField(polyvariant), polyvariantPayload::three($0) [{ "label": "{}", - "kind": 22, + "kind": 12, "tags": [], "detail": "someRecord", "documentation": null, @@ -590,7 +590,7 @@ Pexp_apply ...[135:11->135:31] (...[135:32->135:66]) Completable: Cexpression CArgument Value[fnTakingInlineRecord]($0)->variantPayload::WithInlineRecord($0), recordField(nestedRecord) [{ "label": "{}", - "kind": 22, + "kind": 12, "tags": [], "detail": "otherRecord", "documentation": null, diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 16d5f5818..3a603eb50 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -410,7 +410,7 @@ XXX Not found! Completable: Cpattern Value[c] [{ "label": "[]", - "kind": 12, + "kind": 22, "tags": [], "detail": "bool", "documentation": null, @@ -539,7 +539,7 @@ posCursor:[143:35] posNoWhite:[143:34] Found pattern:[143:20->143:38] Completable: Cpattern Value[p]->variantPayload::Test($3) [{ "label": "[]", - "kind": 12, + "kind": 22, "tags": [], "detail": "bool", "documentation": null, @@ -625,7 +625,7 @@ posCursor:[159:36] posNoWhite:[159:35] Found pattern:[159:21->159:38] Completable: Cpattern Value[v]->polyvariantPayload::test($3) [{ "label": "[]", - "kind": 12, + "kind": 22, "tags": [], "detail": "bool", "documentation": null, diff --git a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt index 1aabb6bfe..715c493ae 100644 --- a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt +++ b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt @@ -3,7 +3,7 @@ XXX Not found! Completable: Cexpression Type[someRecord] [{ "label": "{}", - "kind": 22, + "kind": 12, "tags": [], "detail": "type someRecord = {age: int, name: string}", "documentation": null, From 9656b62551709ad9dd3e8f5cd7a6f5dbdc180ec1 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 13:41:18 +0100 Subject: [PATCH 10/14] cleanup --- analysis/src/DumpAst.ml | 6 +++--- analysis/src/ProcessCmt.ml | 2 +- analysis/src/TypeUtils.ml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/analysis/src/DumpAst.ml b/analysis/src/DumpAst.ml index cb651dbce..dc841b41a 100644 --- a/analysis/src/DumpAst.ml +++ b/analysis/src/DumpAst.ml @@ -59,10 +59,10 @@ let printCoreType typ ~pos = match typ.ptyp_desc with | Ptyp_any -> "Ptyp_any" | Ptyp_var name -> "Ptyp_var(" ^ str name ^ ")" - | Ptyp_constr (loc, _types) -> + | Ptyp_constr (lid, _types) -> "Ptyp_constr(" - ^ (loc |> printLocDenominatorLoc ~pos) - ^ (Utils.flattenLongIdent loc.txt |> ident |> str) + ^ (lid |> printLocDenominatorLoc ~pos) + ^ (Utils.flattenLongIdent lid.txt |> ident |> str) ^ ")" | Ptyp_variant _ -> "Ptyp_variant()" | _ -> "" diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index a578c1dd1..67b910830 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -64,7 +64,7 @@ let rec forTypeSignatureItem ~(env : SharedTypes.Env.t) ~(exported : Exported.t) ~item: { Type.decl; - name = ident.name; + name = name.txt; kind = (match type_kind with | Type_abstract -> ( diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 219a03e5c..9bf0abe54 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -381,8 +381,8 @@ let typeIsUnit (typ : Types.type_expr) = let contextPathFromCoreType (coreType : Parsetree.core_type) = match coreType.ptyp_desc with - | Ptyp_constr (loc, []) -> - Some (Completable.CPId (loc.txt |> Utils.flattenLongIdent, Type)) + | Ptyp_constr (lid, []) -> + Some (Completable.CPId (lid.txt |> Utils.flattenLongIdent, Type)) | _ -> None let printRecordFromFields ?name (fields : field list) = From 45c32a0b1d88dcfa2c0b807bf711d3a35a9cf0d9 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 17:57:53 +0100 Subject: [PATCH 11/14] handle options with annotations --- analysis/src/CompletionBackEnd.ml | 13 +++++++ analysis/src/SharedTypes.ml | 2 ++ analysis/src/TypeUtils.ml | 9 +++-- .../tests/src/CompletionTypeAnnotation.res | 3 ++ .../expected/CompletionTypeAnnotation.res.txt | 35 +++++++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 8e30449ee..f70890055 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -630,6 +630,19 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env (Completion.Value (Ctype.newconstr (Path.Pident (Ident.create "array")) [])); ] + | CPOption cp -> ( + match + cp + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:true ~scope + |> completionsGetCompletionType ~full + with + | None -> [] + | Some (typ, env) -> + [ + Completion.create "dummy" ~env + ~kind:(Completion.ExtractedType (Toption (env, typ), `Type)); + ]) | CPId (path, completionContext) -> path |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 733cdbdb8..1baf150d1 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -589,6 +589,7 @@ module Completable = struct | CPArray | CPInt | CPFloat + | CPOption of contextPath | CPApply of contextPath * Asttypes.arg_label list | CPId of string list * completionContext | CPField of contextPath * string @@ -662,6 +663,7 @@ module Completable = struct | CPString -> "string" | CPInt -> "int" | CPFloat -> "float" + | CPOption ctxPath -> "option<" ^ contextPathToString ctxPath ^ ">" | CPApply (cp, labels) -> contextPathToString cp ^ "(" ^ (labels diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 9bf0abe54..76b4c9177 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -379,10 +379,13 @@ let typeIsUnit (typ : Types.type_expr) = true | _ -> false -let contextPathFromCoreType (coreType : Parsetree.core_type) = +let rec contextPathFromCoreType (coreType : Parsetree.core_type) = match coreType.ptyp_desc with - | Ptyp_constr (lid, []) -> - Some (Completable.CPId (lid.txt |> Utils.flattenLongIdent, Type)) + | Ptyp_constr ({txt = Lident "option"}, [innerTyp]) -> + innerTyp |> contextPathFromCoreType + |> Option.map (fun innerTyp -> Completable.CPOption innerTyp) + | Ptyp_constr (lid, _) -> + Some (CPId (lid.txt |> Utils.flattenLongIdent, Type)) | _ -> None let printRecordFromFields ?name (fields : field list) = diff --git a/analysis/tests/src/CompletionTypeAnnotation.res b/analysis/tests/src/CompletionTypeAnnotation.res index 1662db78f..c4fbd2585 100644 --- a/analysis/tests/src/CompletionTypeAnnotation.res +++ b/analysis/tests/src/CompletionTypeAnnotation.res @@ -37,3 +37,6 @@ type someTuple = (bool, option) // let x: someTuple = (true, ) // ^com + +// let x: option = +// ^com diff --git a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt index 715c493ae..d01a25663 100644 --- a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt +++ b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt @@ -168,3 +168,38 @@ Completable: Cexpression Type[someTuple]->tuple($1) "documentation": null }] +Complete src/CompletionTypeAnnotation.res 40:31 +XXX Not found! +Completable: Cexpression option +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "type someVariant = One | Two(bool)", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "type someVariant = One | Two(bool)", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }, { + "label": "Some(One)", + "kind": 4, + "tags": [], + "detail": "One\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "Some(One)", + "insertTextFormat": 2 + }, { + "label": "Some(Two(_))", + "kind": 4, + "tags": [], + "detail": "Two(bool)\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "Some(Two(${1:_}))", + "insertTextFormat": 2 + }] + From 1eb870c942478dbfd562d2035caea2cd656056e7 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 20:09:26 +0100 Subject: [PATCH 12/14] handle arrays --- analysis/src/CompletionBackEnd.ml | 30 +++++- analysis/src/CompletionFrontEnd.ml | 7 +- analysis/src/SharedTypes.ml | 5 +- analysis/src/TypeUtils.ml | 20 ++-- .../tests/src/CompletionTypeAnnotation.res | 15 +++ .../tests/src/expected/Completion.res.txt | 2 +- .../expected/CompletionTypeAnnotation.res.txt | 91 +++++++++++++++++++ 7 files changed, 157 insertions(+), 13 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index f70890055..43572f6e1 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -599,7 +599,7 @@ let completionsGetCompletionType ~full = function | _ -> None let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env - ~exact ~scope (contextPath : Completable.contextPath) = + ~exact ~scope ?(mode = `Regular) (contextPath : Completable.contextPath) = let package = full.package in match contextPath with | CPString -> @@ -623,13 +623,37 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env (Completion.Value (Ctype.newconstr (Path.Pident (Ident.create "float")) [])); ] - | CPArray -> + | CPArray None -> [ Completion.create "array" ~env ~kind: (Completion.Value (Ctype.newconstr (Path.Pident (Ident.create "array")) [])); ] + | CPArray (Some cp) -> ( + match mode with + | `Regular -> ( + match + cp + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos + ~env ~exact:true ~scope + |> completionsGetCompletionType ~full + with + | None -> [] + | Some (typ, env) -> + [ + Completion.create "dummy" ~env + ~kind:(Completion.ExtractedType (Tarray (env, typ), `Type)); + ]) + | `Pipe -> + (* Pipe completion with array just needs to know that it's an array, not + what inner type it has. *) + [ + Completion.create "array" ~env + ~kind: + (Completion.Value + (Ctype.newconstr (Path.Pident (Ident.create "array")) [])); + ]) | CPOption cp -> ( match cp @@ -748,7 +772,7 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env match cp |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env - ~exact:true ~scope + ~exact:true ~scope ~mode:`Pipe |> completionsGetTypeEnv with | None -> [] diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c54e44a07..9fe9c5369 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -141,7 +141,12 @@ let rec exprToContextPath (e : Parsetree.expression) = | Pexp_constant (Pconst_string _) -> Some Completable.CPString | Pexp_constant (Pconst_integer _) -> Some CPInt | Pexp_constant (Pconst_float _) -> Some CPFloat - | Pexp_array _ -> Some CPArray + | Pexp_array exprs -> + Some + (CPArray + (match exprs with + | [] -> None + | exp :: _ -> exprToContextPath exp)) | Pexp_ident {txt} -> Some (CPId (Utils.flattenLongIdent txt, Value)) | Pexp_field (e1, {txt = Lident name}) -> ( match exprToContextPath e1 with diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 1baf150d1..b8f811fad 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -586,7 +586,7 @@ module Completable = struct type contextPath = | CPString - | CPArray + | CPArray of contextPath option | CPInt | CPFloat | CPOption of contextPath @@ -673,7 +673,8 @@ module Completable = struct | Optional s -> "?" ^ s) |> String.concat ", ") ^ ")" - | CPArray -> "array" + | CPArray (Some ctxPath) -> "array<" ^ contextPathToString ctxPath ^ ">" + | CPArray None -> "array" | CPId (sl, completionContext) -> completionContextToString completionContext ^ list sl | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 76b4c9177..a07490b8c 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -384,6 +384,8 @@ let rec contextPathFromCoreType (coreType : Parsetree.core_type) = | Ptyp_constr ({txt = Lident "option"}, [innerTyp]) -> innerTyp |> contextPathFromCoreType |> Option.map (fun innerTyp -> Completable.CPOption innerTyp) + | Ptyp_constr ({txt = Lident "array"}, [innerTyp]) -> + Some (Completable.CPArray (innerTyp |> contextPathFromCoreType)) | Ptyp_constr (lid, _) -> Some (CPId (lid.txt |> Utils.flattenLongIdent, Type)) | _ -> None @@ -398,20 +400,26 @@ let printRecordFromFields ?name (fields : field list) = |> String.concat ", ") ^ "}" -let rec extractedTypeToString = function +let rec extractedTypeToString ?(inner = false) = function | Tuple (_, _, typ) | Tpolyvariant {typeExpr = typ} | Tfunction {typ} | Trecord {definition = `TypeExpr typ} -> - Shared.typeToString typ + if inner then + match pathFromTypeExpr typ with + | None -> "record" (* Won't happen *) + | Some p -> p |> SharedTypes.pathIdentToString + else Shared.typeToString typ | Tbool _ -> "bool" | Tstring _ -> "string" - | Tarray (_, innerTyp) -> "array<" ^ extractedTypeToString innerTyp ^ ">" - | Toption (_, innerTyp) -> "option<" ^ extractedTypeToString innerTyp ^ ">" + | Tarray (_, innerTyp) -> + "array<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" + | Toption (_, innerTyp) -> + "option<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" | Tvariant {variantDecl; variantName} -> - Shared.declToString variantName variantDecl + if inner then variantName else Shared.declToString variantName variantDecl | Trecord {definition = `NameOnly name; fields} -> - printRecordFromFields ~name fields + if inner then name else printRecordFromFields ~name fields | TinlineRecord {fields} -> printRecordFromFields fields let unwrapCompletionTypeIfOption (t : SharedTypes.completionType) = diff --git a/analysis/tests/src/CompletionTypeAnnotation.res b/analysis/tests/src/CompletionTypeAnnotation.res index c4fbd2585..bfe480ea6 100644 --- a/analysis/tests/src/CompletionTypeAnnotation.res +++ b/analysis/tests/src/CompletionTypeAnnotation.res @@ -40,3 +40,18 @@ type someTuple = (bool, option) // let x: option = // ^com + +// let x: option = Some() +// ^com + +// let x: array = +// ^com + +// let x: array = [] +// ^com + +// let x: array> = +// ^com + +// let x: option> = Some([]) +// ^com diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 7907834ab..b36ab0940 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -379,7 +379,7 @@ Found type for function (~age: int, ~name: string) => string Complete src/Completion.res 26:13 posCursor:[26:13] posNoWhite:[26:12] Found expr:[26:3->26:13] -Completable: Cpath array->m +Completable: Cpath array->m [{ "label": "Js.Array2.mapi", "kind": 12, diff --git a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt index d01a25663..6d7db71a7 100644 --- a/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt +++ b/analysis/tests/src/expected/CompletionTypeAnnotation.res.txt @@ -203,3 +203,94 @@ Completable: Cexpression option "insertTextFormat": 2 }] +Complete src/CompletionTypeAnnotation.res 43:37 +XXX Not found! +Completable: Cexpression option->variantPayload::Some($0) +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "One", + "insertTextFormat": 2 + }, { + "label": "Two(_)", + "kind": 4, + "tags": [], + "detail": "Two(bool)\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "Two(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionTypeAnnotation.res 46:30 +XXX Not found! +Completable: Cexpression array +[{ + "label": "[]", + "kind": 12, + "tags": [], + "detail": "type someVariant = One | Two(bool)", + "documentation": null, + "sortText": "A", + "insertText": "[$0]", + "insertTextFormat": 2 + }] + +Complete src/CompletionTypeAnnotation.res 49:32 +XXX Not found! +Completable: Cexpression array->array +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "One", + "insertTextFormat": 2 + }, { + "label": "Two(_)", + "kind": 4, + "tags": [], + "detail": "Two(bool)\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "Two(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionTypeAnnotation.res 52:38 +XXX Not found! +Completable: Cexpression array> +[{ + "label": "[]", + "kind": 12, + "tags": [], + "detail": "option", + "documentation": null, + "sortText": "A", + "insertText": "[$0]", + "insertTextFormat": 2 + }] + +Complete src/CompletionTypeAnnotation.res 55:45 +XXX Not found! +Completable: Cexpression option>->variantPayload::Some($0), array +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "One", + "insertTextFormat": 2 + }, { + "label": "Two(_)", + "kind": 4, + "tags": [], + "detail": "Two(bool)\n\ntype someVariant = One | Two(bool)", + "documentation": null, + "insertText": "Two(${1:_})", + "insertTextFormat": 2 + }] + From c595d2056d6ce0572581727f81659f2c4651d5d4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 23 Jan 2023 20:25:17 +0100 Subject: [PATCH 13/14] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a9ea2a50..2bcd18904 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Support inline records in completion. https://github.com/rescript-lang/rescript-vscode/pull/695 - Add way to autocomplete an exhaustive switch statement for identifiers. Example: an identifier that's a variant can have a switch autoinserted matching all variant cases. https://github.com/rescript-lang/rescript-vscode/pull/699 - Support typed expression completion for lowercase (builtin) JSX tags. https://github.com/rescript-lang/rescript-vscode/pull/702 +- Support typed expression completion driven by type annotations. https://github.com/rescript-lang/rescript-vscode/pull/711 #### :nail_care: Polish From 20af7f073bd572931feb638b16cb6d01c9815386 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 24 Jan 2023 09:45:33 +0100 Subject: [PATCH 14/14] poly -> real variant --- analysis/src/CompletionBackEnd.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 43572f6e1..75b25c138 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -598,8 +598,10 @@ let completionsGetCompletionType ~full = function | {Completion.kind = ExtractedType (typ, _); env} :: _ -> Some (typ, env) | _ -> None +type getCompletionsForContextPathMode = Regular | Pipe + let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env - ~exact ~scope ?(mode = `Regular) (contextPath : Completable.contextPath) = + ~exact ~scope ?(mode = Regular) (contextPath : Completable.contextPath) = let package = full.package in match contextPath with | CPString -> @@ -632,7 +634,7 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env ] | CPArray (Some cp) -> ( match mode with - | `Regular -> ( + | Regular -> ( match cp |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos @@ -645,7 +647,7 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env Completion.create "dummy" ~env ~kind:(Completion.ExtractedType (Tarray (env, typ), `Type)); ]) - | `Pipe -> + | Pipe -> (* Pipe completion with array just needs to know that it's an array, not what inner type it has. *) [ @@ -772,7 +774,7 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env match cp |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env - ~exact:true ~scope ~mode:`Pipe + ~exact:true ~scope ~mode:Pipe |> completionsGetTypeEnv with | None -> []