diff --git a/CHANGELOG.md b/CHANGELOG.md index 2528a1e55..2840e47b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Add autocomplete for function argument values (booleans, variants and options. More values coming), both labelled and unlabelled. https://github.com/rescript-lang/rescript-vscode/pull/665 - Add autocomplete for JSX prop values. https://github.com/rescript-lang/rescript-vscode/pull/667 - Add snippet support in completion items. https://github.com/rescript-lang/rescript-vscode/pull/668 +- Add support from completing polyvariants as values. https://github.com/rescript-lang/rescript-vscode/pull/669 #### :nail_care: Polish diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index daaa056f6..3568d3581 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -711,6 +711,17 @@ let detail name (kind : Completion.kind) = | FileModule _ -> "file module" | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s + | PolyvariantConstructor ({name; args}, s) -> + "#" ^ name + ^ (match args with + | [] -> "" + | typeExprs -> + "(" + ^ (typeExprs + |> List.map (fun typeExpr -> typeExpr |> Shared.typeToString) + |> String.concat ", ") + ^ ")") + ^ "\n\n" ^ s let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed ~(completionContext : Completable.completionContext) = @@ -1519,6 +1530,23 @@ let rec extractType ~env ~package (t : Types.type_expr) = {env; constructors; variantName = name.txt; variantDecl = decl}) | _ -> None) | Ttuple expressions -> Some (Tuple (env, expressions, t)) + | Tvariant {row_fields} -> + let constructors = + row_fields + |> List.map (fun (label, field) -> + { + name = label; + args = + (* Multiple arguments are represented as a Ttuple, while a single argument is just the type expression itself. *) + (match field with + | Types.Rpresent (Some typeExpr) -> ( + match typeExpr.desc with + | Ttuple args -> args + | _ -> [typeExpr]) + | _ -> []); + }) + in + Some (Tpolyvariant {env; constructors; typeExpr = t}) | _ -> None let filterItems items ~prefix = @@ -1575,6 +1603,26 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix variantDecl |> Shared.declToString variantName )) ~env ()) |> filterItems ~prefix + | Some (Tpolyvariant {env; constructors; typeExpr}) -> + constructors + |> List.map (fun (constructor : polyVariantConstructor) -> + Completion.createWithSnippet + ~name: + ("#" ^ constructor.name + ^ printConstructorArgs + (List.length constructor.args) + ~asSnippet:false) + ~insertText: + ((if Utils.startsWith prefix "#" then "" else "#") + ^ constructor.name + ^ printConstructorArgs + (List.length constructor.args) + ~asSnippet:true) + ~kind: + (PolyvariantConstructor + (constructor, typeExpr |> Shared.typeToString)) + ~env ()) + |> filterItems ~prefix | Some (Toption (env, t)) -> [ Completion.create ~name:"None" diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 5203c1572..aaeee4dd1 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -11,7 +11,8 @@ let extractCompletableArgValueInfo exp = match exp.Parsetree.pexp_desc with | Pexp_ident {txt = Lident txt} -> Some txt | Pexp_construct ({txt = Lident "()"}, _) -> Some "" - | Pexp_construct ({txt = Lident txt}, _) -> Some txt + | Pexp_construct ({txt = Lident txt}, None) -> Some txt + | Pexp_variant (label, None) -> Some ("#" ^ label) | _ -> None let isExprHole exp = diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index b68eb57ee..ef2808427 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -279,6 +279,8 @@ end = struct {env with exported = structure.exported; pathRev; parent = Some env} end +type polyVariantConstructor = {name: string; args: Types.type_expr list} + module Completion = struct type kind = | Module of Module.t @@ -287,6 +289,7 @@ module Completion = struct | Label of string | Type of Type.t | Constructor of Constructor.t * string + | PolyvariantConstructor of polyVariantConstructor * string | Field of field * string | FileModule of string @@ -331,7 +334,7 @@ module Completion = struct match kind with | Module _ -> 9 | FileModule _ -> 9 - | Constructor (_, _) -> 4 + | Constructor (_, _) | PolyvariantConstructor (_, _) -> 4 | ObjLabel _ -> 4 | Label _ -> 4 | Field (_, _) -> 5 @@ -578,6 +581,11 @@ module Completable = struct variantDecl: Types.type_declaration; variantName: string; } + | Tpolyvariant of { + env: QueryEnv.t; + constructors: polyVariantConstructor list; + typeExpr: Types.type_expr; + } let toString = let completionContextToString = function diff --git a/analysis/tests/src/CompletionJsxProps.res b/analysis/tests/src/CompletionJsxProps.res index 65f5fa7e1..41dcfe57f 100644 --- a/analysis/tests/src/CompletionJsxProps.res +++ b/analysis/tests/src/CompletionJsxProps.res @@ -7,3 +7,9 @@ // let _ = { + let make = ( + ~on: bool, + ~test: testVariant, + ~polyArg: option<[#one | #two | #two2 | #three(int, bool)]>=?, + ) => { ignore(on) ignore(test) + ignore(polyArg) React.null } } diff --git a/analysis/tests/src/expected/CompletionJsxProps.res.txt b/analysis/tests/src/expected/CompletionJsxProps.res.txt index 7a6dec0df..7656ead99 100644 --- a/analysis/tests/src/expected/CompletionJsxProps.res.txt +++ b/analysis/tests/src/expected/CompletionJsxProps.res.txt @@ -50,3 +50,71 @@ Completable: CjsxPropValue [CompletionSupport, TestComponent] test=T "insertTextFormat": 2 }] +Complete src/CompletionJsxProps.res 9:52 +posCursor:[9:52] posNoWhite:[9:51] Found expr:[9:12->9:52] +JSX 9:43] polyArg[9:44->9:51]=...__ghost__[0:-1->0:-1]> _children:None +Completable: CjsxPropValue [CompletionSupport, TestComponent] polyArg= +[{ + "label": "#one", + "kind": 4, + "tags": [], + "detail": "#one\n\n[#one | #three(int, bool) | #two | #two2]", + "documentation": null, + "insertText": "#one", + "insertTextFormat": 2 + }, { + "label": "#three(_, _)", + "kind": 4, + "tags": [], + "detail": "#three(int, bool)\n\n[#one | #three(int, bool) | #two | #two2]", + "documentation": null, + "insertText": "#three(${1:_}, ${2:_})", + "insertTextFormat": 2 + }, { + "label": "#two", + "kind": 4, + "tags": [], + "detail": "#two\n\n[#one | #three(int, bool) | #two | #two2]", + "documentation": null, + "insertText": "#two", + "insertTextFormat": 2 + }, { + "label": "#two2", + "kind": 4, + "tags": [], + "detail": "#two2\n\n[#one | #three(int, bool) | #two | #two2]", + "documentation": null, + "insertText": "#two2", + "insertTextFormat": 2 + }] + +Complete src/CompletionJsxProps.res 12:54 +posCursor:[12:54] posNoWhite:[12:53] Found expr:[12:12->12:54] +JSX 12:43] polyArg[12:44->12:51]=...[12:52->12:54]> _children:None +Completable: CjsxPropValue [CompletionSupport, TestComponent] polyArg=#t +[{ + "label": "#three(_, _)", + "kind": 4, + "tags": [], + "detail": "#three(int, bool)\n\n[#one | #three(int, bool) | #two | #two2]", + "documentation": null, + "insertText": "three(${1:_}, ${2:_})", + "insertTextFormat": 2 + }, { + "label": "#two", + "kind": 4, + "tags": [], + "detail": "#two\n\n[#one | #three(int, bool) | #two | #two2]", + "documentation": null, + "insertText": "two", + "insertTextFormat": 2 + }, { + "label": "#two2", + "kind": 4, + "tags": [], + "detail": "#two2\n\n[#one | #three(int, bool) | #two | #two2]", + "documentation": null, + "insertText": "two2", + "insertTextFormat": 2 + }] +