From a1c059504c58c63f1f6f548628be96e4d9543e39 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 22 Dec 2022 21:33:15 +0100 Subject: [PATCH 01/29] add base test for what we're looking to complete --- .../tests/src/CompletionFunctionArguments.res | 13 +++++++++++++ .../expected/CompletionFunctionArguments.res.txt | 15 +++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 analysis/tests/src/CompletionFunctionArguments.res create mode 100644 analysis/tests/src/expected/CompletionFunctionArguments.res.txt diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res new file mode 100644 index 000000000..2515408a0 --- /dev/null +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -0,0 +1,13 @@ +let someFn = (~isOn) => { + if isOn { + "on" + } else { + "off" + } +} + +// let _ = someFn(~isOn=) +// ^com + +// let _ = someFn(~isOn=t) +// ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt new file mode 100644 index 000000000..094cca6d2 --- /dev/null +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -0,0 +1,15 @@ +Complete src/CompletionFunctionArguments.res 8:24 +posCursor:[8:24] posNoWhite:[8:23] Found expr:[8:11->8:25] +Pexp_apply ...[8:11->8:17] (~isOn8:19->8:23=...__ghost__[0:-1->0:-1]) +Completable: CnamedArg(Value[someFn], "", [isOn]) +Found type for function (~isOn: bool) => string +[] + +Complete src/CompletionFunctionArguments.res 11:25 +posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:11->11:26] +Pexp_apply ...[11:11->11:17] (~isOn11:19->11:23=...[11:24->11:25]) +posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:24->11:25] +Pexp_ident t:[11:24->11:25] +Completable: Cpath Value[t] +[] + From f3dbd6885fb8c64958e13cebbef117cfaadd7b08 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 22 Dec 2022 22:14:06 +0100 Subject: [PATCH 02/29] add test cases and identify arguments for completion --- analysis/src/CompletionBackEnd.ml | 1 + analysis/src/CompletionFrontEnd.ml | 61 ++++++++++++++++--- analysis/src/SharedTypes.ml | 17 ++++++ .../tests/src/CompletionFunctionArguments.res | 15 +++++ .../CompletionFunctionArguments.res.txt | 23 +++++-- 5 files changed, 105 insertions(+), 12 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index e1ace9947..25c23fee4 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1817,6 +1817,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i in (dec2, doc)) |> List.map mkDecorator + | Cargument _ -> [] | CnamedArg (cp, prefix, identsSeen) -> let labels = match diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 953163b60..8fb4ac408 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -106,7 +106,17 @@ let extractJsxProps ~(compName : Longident.t Location.loc) ~args = in args |> processProps ~acc:[] -let findNamedArgCompletable ~(args : arg list) ~endPos ~posBeforeCursor +let extractCompletableArgValueInfo exp = + match exp.Parsetree.pexp_desc with + | Pexp_ident {txt} -> Some (Utils.flattenLongIdent txt |> List.hd) + | _ -> None + +let isExprHole exp = + match exp.Parsetree.pexp_desc with + | Pexp_extension ({txt = "rescript.exprhole"}, _) -> true + | _ -> false + +let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor ~(contextPath : Completable.contextPath) ~posAfterFunExpr = let allNames = List.fold_right @@ -116,6 +126,7 @@ let findNamedArgCompletable ~(args : arg list) ~endPos ~posBeforeCursor | {label = None} -> allLabels) args [] in + let unlabelledCount = ref 0 in let rec loop args = match args with | {label = Some labelled; exp} :: rest -> @@ -123,11 +134,47 @@ let findNamedArgCompletable ~(args : arg list) ~endPos ~posBeforeCursor labelled.posStart <= posBeforeCursor && posBeforeCursor < labelled.posEnd then Some (Completable.CnamedArg (contextPath, labelled.name, allNames)) - else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None + else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then + (* Completing in the assignment of labelled argument *) + match extractCompletableArgValueInfo exp with + | None -> None + | Some prefix -> + Some + (Cargument + {contextPath; argumentLabel = Labelled labelled.name; prefix}) + else if isExprHole exp then + Some + (Cargument + {contextPath; argumentLabel = Labelled labelled.name; prefix = ""}) else loop rest | {label = None; exp} :: rest -> - if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None - else loop rest + (* TODO: Better guard for this... This is so completion does not trigger + inside of template string calls, which are regular calls *) + if Res_parsetree_viewer.isTemplateLiteral exp then None + else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then + (* Completing in an unlabelled argument *) + match extractCompletableArgValueInfo exp with + | None -> None + | Some prefix -> + Some + (Cargument + { + contextPath; + argumentLabel = + Unlabelled {argumentPosition = !unlabelledCount}; + prefix; + }) + else if isExprHole exp then + Some + (Cargument + { + contextPath; + argumentLabel = Unlabelled {argumentPosition = !unlabelledCount}; + prefix = ""; + }) + else ( + unlabelledCount := !unlabelledCount + 1; + loop rest) | [] -> if posAfterFunExpr <= posBeforeCursor && posBeforeCursor < endPos then Some (CnamedArg (contextPath, "", allNames)) @@ -578,16 +625,16 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (Loc.toString exp.pexp_loc)) |> String.concat ", "); - let namedArgCompletable = + let argCompletable = match exprToContextPath funExpr with | Some contextPath -> - findNamedArgCompletable ~contextPath ~args + findArgCompletables ~contextPath ~args ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor ~posAfterFunExpr:(Loc.end_ funExpr.pexp_loc) | None -> None in - setResultOpt namedArgCompletable + setResultOpt argCompletable | Pexp_send (lhs, {txt; loc}) -> ( (* e["txt"] If the string for txt is not closed, it could go over several lines. diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 358e25856..89686c352 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -502,6 +502,10 @@ module Completable = struct (* Completion context *) type completionContext = Type | Value | Module | Field + type argumentLabel = + | Unlabelled of {argumentPosition: int} + | Labelled of string + type contextPath = | CPString | CPArray @@ -526,6 +530,11 @@ module Completable = struct | Cpath of contextPath | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for "Cnone" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" + | Cargument {contextPath; argumentLabel; prefix} -> + contextPathToString contextPath + ^ "(" + ^ (match argumentLabel with + | Unlabelled {argumentPosition} -> "$" ^ string_of_int argumentPosition + | Labelled name -> "~" ^ name) + ^ (if prefix <> "" then "=" ^ prefix else "") + ^ ")" end module CursorPosition = struct diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index 2515408a0..ee84e3691 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -11,3 +11,18 @@ let someFn = (~isOn) => { // let _ = someFn(~isOn=t) // ^com + +let _ = someFn( + ~isOn={ + // switch someFn(~isOn=) + // ^com + true + }, +) + +let someOtherFn = (includeName, age) => { + "Hello" ++ (includeName ? " Some Name" : "") ++ ", you are age " ++ Belt.Int.toString(age) +} + +// let _ = someOtherFn(t) +// ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 094cca6d2..4ca60c5f2 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -1,15 +1,28 @@ Complete src/CompletionFunctionArguments.res 8:24 posCursor:[8:24] posNoWhite:[8:23] Found expr:[8:11->8:25] Pexp_apply ...[8:11->8:17] (~isOn8:19->8:23=...__ghost__[0:-1->0:-1]) -Completable: CnamedArg(Value[someFn], "", [isOn]) -Found type for function (~isOn: bool) => string +Completable: Value[someFn](~isOn) [] Complete src/CompletionFunctionArguments.res 11:25 posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:11->11:26] Pexp_apply ...[11:11->11:17] (~isOn11:19->11:23=...[11:24->11:25]) -posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:24->11:25] -Pexp_ident t:[11:24->11:25] -Completable: Cpath Value[t] +Completable: Value[someFn](~isOn=t) +[] + +Complete src/CompletionFunctionArguments.res 16:27 +posCursor:[16:27] posNoWhite:[16:26] Found expr:[14:8->20:1] +Pexp_apply ...[14:8->14:14] (~isOn15:3->15:7=...[16:7->18:8]) +posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:7->18:8] +posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:7->16:28] +posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:14->16:28] +Pexp_apply ...[16:14->16:20] (~isOn16:22->16:26=...__ghost__[0:-1->0:-1]) +Completable: Value[someFn](~isOn) +[] + +Complete src/CompletionFunctionArguments.res 26:24 +posCursor:[26:24] posNoWhite:[26:23] Found expr:[26:11->26:25] +Pexp_apply ...[26:11->26:22] (...[26:23->26:24]) +Completable: Value[someOtherFn]($0=t) [] From 1bbb35f15f168425bda582df4deef32815d0c951 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 22 Dec 2022 22:47:45 +0100 Subject: [PATCH 03/29] refactor in prep for reuse --- analysis/src/CompletionBackEnd.ml | 56 ++++++++++++++++++------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 25c23fee4..93fc193b2 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1455,6 +1455,34 @@ let getOpens ~debug ~rawOpens ~package ~env = (* Last open takes priority *) List.rev resolvedOpens +let getArgs ~env (t : Types.type_expr) ~full = + let rec getArgsLoop ~env (t : Types.type_expr) ~full ~currentArgumentPosition + = + match t.desc with + | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> + getArgsLoop ~full ~env ~currentArgumentPosition t1 + | Tarrow ((Labelled l | Optional l), tArg, tRet, _) -> + (SharedTypes.Completable.Labelled l, tArg) + :: getArgsLoop ~full ~env ~currentArgumentPosition tRet + | Tarrow (Nolabel, tArg, tRet, _) -> + (Unlabelled {argumentPosition = currentArgumentPosition}, tArg) + :: getArgsLoop ~full ~env + ~currentArgumentPosition:(currentArgumentPosition + 1) + tRet + | Tconstr (path, typeArgs, _) -> ( + match References.digConstructor ~env ~package:full.package path with + | Some + ( env, + { + item = {decl = {type_manifest = Some t1; type_params = typeParams}}; + } ) -> + let t1 = t1 |> instantiateType ~typeParams ~typeArgs in + getArgsLoop ~full ~env ~currentArgumentPosition t1 + | _ -> []) + | _ -> [] + in + t |> getArgsLoop ~env ~full ~currentArgumentPosition:0 + let processCompletable ~debug ~full ~scope ~env ~pos ~forHover (completable : Completable.t) = let package = full.package in @@ -1830,29 +1858,11 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i if debug then Printf.printf "Found type for function %s\n" (typ |> Shared.typeToString); - let rec getLabels ~env (t : Types.type_expr) = - match t.desc with - | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> getLabels ~env t1 - | Tarrow ((Labelled l | Optional l), tArg, tRet, _) -> - (l, tArg) :: getLabels ~env tRet - | Tarrow (Nolabel, _, tRet, _) -> getLabels ~env tRet - | Tconstr (path, typeArgs, _) -> ( - match References.digConstructor ~env ~package path with - | Some - ( env, - { - item = - { - decl = - {type_manifest = Some t1; type_params = typeParams}; - }; - } ) -> - let t1 = t1 |> instantiateType ~typeParams ~typeArgs in - getLabels ~env t1 - | _ -> []) - | _ -> [] - in - typ |> getLabels ~env + typ |> getArgs ~full ~env + |> List.filter_map (fun arg -> + match arg with + | SharedTypes.Completable.Labelled name, a -> Some (name, a) + | _ -> None) | None -> [] in let mkLabel (name, typ) = From 3d1a03ee1ee572f2a43edb65d3998e5af8c31c07 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 22 Dec 2022 22:49:03 +0100 Subject: [PATCH 04/29] add utils for extracting relevant completion information from type expr --- analysis/src/CompletionBackEnd.ml | 48 +++++++++++++++++++++++++++++++ analysis/src/SharedTypes.ml | 6 ++++ 2 files changed, 54 insertions(+) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 93fc193b2..f9cd54daa 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1483,6 +1483,54 @@ let getArgs ~env (t : Types.type_expr) ~full = in t |> getArgsLoop ~env ~full ~currentArgumentPosition:0 +type extractedType = + | Declared of QueryEnv.t * Type.t Declared.t + | Polyvariant of QueryEnv.t * SharedTypes.polyVariantConstructor list + | Tuple of QueryEnv.t * Types.type_expr list + | Toption of QueryEnv.t * Types.type_expr + | Tbool of QueryEnv.t + +(* This is a more general extraction function for pulling out the type of a type_expr. We already have other similar functions, but they are all specialized on something (variants, records, etc). *) +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], _) -> + (* Handle option. TODO: Look up how the compiler does this and copy that behavior. *) + Some (Toption (env, payloadTypeExpr)) + | Tconstr (Path.Pident {name = "bool"}, [], _) -> + (* Handle bool. TODO: Look up how the compiler does this and copy that behavior. *) + Some (Tbool env) + | Tconstr (path, _, _) -> ( + match References.digConstructor ~env ~package path with + | Some (env, {item = {decl = {type_manifest = Some t1}}}) -> + extractType ~env ~package t1 + | Some (env, typ) -> Some (Declared (env, typ)) + | None -> None) + | Tvariant {row_fields} -> + (* Since polyvariants are strutural, they're "inlined". So, we extract just + what we need for completion from that definition here. *) + let constructors = + row_fields + |> List.map (fun (label, field) -> + { + name = label; + payload = + (match field with + | Types.Rpresent maybeTypeExpr -> maybeTypeExpr + | _ -> None); + 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 (Polyvariant (env, constructors)) + | Ttuple expressions -> Some (Tuple (env, expressions)) + | _ -> None let processCompletable ~debug ~full ~scope ~env ~pos ~forHover (completable : Completable.t) = let package = full.package in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 89686c352..4d15e47f0 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -498,6 +498,12 @@ let locItemToString {loc = {Location.loc_start; loc_end}; locType} = (* needed for debugging *) let _ = locItemToString +type polyVariantConstructor = { + name: string; + payload: Types.type_expr option; + args: Types.type_expr list; +} + module Completable = struct (* Completion context *) type completionContext = Type | Value | Module | Field From 06e25c7fdca1db3e1228df3b9abee1ecf0d7ed9d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 22 Dec 2022 22:49:41 +0100 Subject: [PATCH 05/29] complete bools as arguments --- analysis/src/CompletionBackEnd.ml | 40 +++++++++++++++- .../tests/src/CompletionFunctionArguments.res | 2 +- .../CompletionFunctionArguments.res.txt | 46 +++++++++++++++++-- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index f9cd54daa..d0ce547cf 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1531,6 +1531,27 @@ let rec extractType ~env ~package (t : Types.type_expr) = Some (Polyvariant (env, constructors)) | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None + +let completeTypedValue t ~env ~full ~prefix = + match t |> extractType ~env ~package:full.package with + | Some (Tbool env) -> + let items = + [ + Completion.create ~name:"true" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + Completion.create ~name:"false" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + ] + in + if prefix = "" then items + else + items + |> List.filter (fun (item : Completion.t) -> + Utils.startsWith item.name prefix) + | _ -> [] + let processCompletable ~debug ~full ~scope ~env ~pos ~forHover (completable : Completable.t) = let package = full.package in @@ -1893,7 +1914,23 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i in (dec2, doc)) |> List.map mkDecorator - | Cargument _ -> [] + | Cargument {contextPath; argumentLabel; prefix} -> ( + let labels = + match + contextPath + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos + ~env ~exact:true ~scope + |> completionsGetTypeEnv + with + | Some (typ, _env) -> typ |> getArgs ~full ~env + | None -> [] + in + let targetLabel = + labels |> List.find_opt (fun (label, _) -> label = argumentLabel) + in + match targetLabel with + | None -> [] + | Some (_, typ) -> typ |> completeTypedValue ~env ~full ~prefix) | CnamedArg (cp, prefix, identsSeen) -> let labels = match @@ -1906,6 +1943,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i if debug then Printf.printf "Found type for function %s\n" (typ |> Shared.typeToString); + typ |> getArgs ~full ~env |> List.filter_map (fun arg -> match arg with diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index ee84e3691..904555f44 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -24,5 +24,5 @@ let someOtherFn = (includeName, age) => { "Hello" ++ (includeName ? " Some Name" : "") ++ ", you are age " ++ Belt.Int.toString(age) } -// let _ = someOtherFn(t) +// let _ = someOtherFn(f) // ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 4ca60c5f2..012fc4512 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -2,13 +2,31 @@ Complete src/CompletionFunctionArguments.res 8:24 posCursor:[8:24] posNoWhite:[8:23] Found expr:[8:11->8:25] Pexp_apply ...[8:11->8:17] (~isOn8:19->8:23=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) -[] +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] Complete src/CompletionFunctionArguments.res 11:25 posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:11->11:26] Pexp_apply ...[11:11->11:17] (~isOn11:19->11:23=...[11:24->11:25]) Completable: Value[someFn](~isOn=t) -[] +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] Complete src/CompletionFunctionArguments.res 16:27 posCursor:[16:27] posNoWhite:[16:26] Found expr:[14:8->20:1] @@ -18,11 +36,29 @@ posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:7->16:28] posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:14->16:28] Pexp_apply ...[16:14->16:20] (~isOn16:22->16:26=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) -[] +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] Complete src/CompletionFunctionArguments.res 26:24 posCursor:[26:24] posNoWhite:[26:23] Found expr:[26:11->26:25] Pexp_apply ...[26:11->26:22] (...[26:23->26:24]) -Completable: Value[someOtherFn]($0=t) -[] +Completable: Value[someOtherFn]($0=f) +[{ + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] From 284ecb4b3e488f126177946bbf010c84047d3ccc Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 09:11:51 +0100 Subject: [PATCH 06/29] remove things from type extraction that will be added in separate PRs --- analysis/src/CompletionBackEnd.ml | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index d0ce547cf..86be114c7 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1484,8 +1484,6 @@ let getArgs ~env (t : Types.type_expr) ~full = t |> getArgsLoop ~env ~full ~currentArgumentPosition:0 type extractedType = - | Declared of QueryEnv.t * Type.t Declared.t - | Polyvariant of QueryEnv.t * SharedTypes.polyVariantConstructor list | Tuple of QueryEnv.t * Types.type_expr list | Toption of QueryEnv.t * Types.type_expr | Tbool of QueryEnv.t @@ -1500,35 +1498,6 @@ let rec extractType ~env ~package (t : Types.type_expr) = | Tconstr (Path.Pident {name = "bool"}, [], _) -> (* Handle bool. TODO: Look up how the compiler does this and copy that behavior. *) Some (Tbool env) - | Tconstr (path, _, _) -> ( - match References.digConstructor ~env ~package path with - | Some (env, {item = {decl = {type_manifest = Some t1}}}) -> - extractType ~env ~package t1 - | Some (env, typ) -> Some (Declared (env, typ)) - | None -> None) - | Tvariant {row_fields} -> - (* Since polyvariants are strutural, they're "inlined". So, we extract just - what we need for completion from that definition here. *) - let constructors = - row_fields - |> List.map (fun (label, field) -> - { - name = label; - payload = - (match field with - | Types.Rpresent maybeTypeExpr -> maybeTypeExpr - | _ -> None); - 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 (Polyvariant (env, constructors)) | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None From ecc1693fb011745eea28020d3959c9f100aedc77 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 10:29:50 +0100 Subject: [PATCH 07/29] complete regular variants --- analysis/src/CompletionBackEnd.ml | 23 +++++++ analysis/src/CompletionFrontEnd.ml | 1 + .../tests/src/CompletionFunctionArguments.res | 12 ++++ .../CompletionFunctionArguments.res.txt | 68 ++++++++++++++----- 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 86be114c7..da95c7f39 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1487,6 +1487,7 @@ type extractedType = | Tuple of QueryEnv.t * Types.type_expr list | Toption of QueryEnv.t * Types.type_expr | Tbool of QueryEnv.t + | Tvariant of {env: QueryEnv.t; constructors: Constructor.t list} (* This is a more general extraction function for pulling out the type of a type_expr. We already have other similar functions, but they are all specialized on something (variants, records, etc). *) let rec extractType ~env ~package (t : Types.type_expr) = @@ -1498,6 +1499,13 @@ let rec extractType ~env ~package (t : Types.type_expr) = | Tconstr (Path.Pident {name = "bool"}, [], _) -> (* Handle bool. TODO: Look up how the compiler does this and copy that behavior. *) Some (Tbool env) + | Tconstr (path, _, _) -> ( + match References.digConstructor ~env ~package path with + | Some (env, {item = {decl = {type_manifest = Some t1}}}) -> + extractType ~env ~package t1 + | Some (env, {item = {kind = Type.Variant constructors}}) -> + Some (Tvariant {env; constructors}) + | _ -> None) | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None @@ -1519,6 +1527,21 @@ let completeTypedValue t ~env ~full ~prefix = items |> List.filter (fun (item : Completion.t) -> Utils.startsWith item.name prefix) + | Some (Tvariant {env; constructors}) -> + let items = + constructors + |> List.filter_map (fun (constructor : Constructor.t) -> + if + prefix <> "" + && not (Utils.startsWith constructor.cname.txt prefix) + then None + else + Some + (Completion.create ~name:constructor.cname.txt + ~kind:(Constructor (constructor, "" (* TODO *))) + ~env)) + in + items | _ -> [] let processCompletable ~debug ~full ~scope ~env ~pos ~forHover diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 8fb4ac408..1451048f1 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -109,6 +109,7 @@ let extractJsxProps ~(compName : Longident.t Location.loc) ~args = let extractCompletableArgValueInfo exp = match exp.Parsetree.pexp_desc with | Pexp_ident {txt} -> Some (Utils.flattenLongIdent txt |> List.hd) + | Pexp_construct ({txt}, _) -> Some (Utils.flattenLongIdent txt |> List.hd) | _ -> None let isExprHole exp = diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index 904555f44..41a29a539 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -26,3 +26,15 @@ let someOtherFn = (includeName, age) => { // let _ = someOtherFn(f) // ^com + +type someVariant = One | Two | Three(int) + +let someFnTakingVariant = (~config: someVariant) => { + ignore(config) +} + +// let _ = someFnTakingVariant(~config=) +// ^com + +// let _ = someFnTakingVariant(~config=O) +// ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 012fc4512..d9a93d21a 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -1,6 +1,6 @@ -Complete src/CompletionFunctionArguments.res 8:24 -posCursor:[8:24] posNoWhite:[8:23] Found expr:[8:11->8:25] -Pexp_apply ...[8:11->8:17] (~isOn8:19->8:23=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 10:24 +posCursor:[10:24] posNoWhite:[10:23] Found expr:[10:11->10:25] +Pexp_apply ...[10:11->10:17] (~isOn10:19->10:23=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) [{ "label": "true", @@ -16,9 +16,9 @@ Completable: Value[someFn](~isOn) "documentation": null }] -Complete src/CompletionFunctionArguments.res 11:25 -posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:11->11:26] -Pexp_apply ...[11:11->11:17] (~isOn11:19->11:23=...[11:24->11:25]) +Complete src/CompletionFunctionArguments.res 13:25 +posCursor:[13:25] posNoWhite:[13:24] Found expr:[13:11->13:26] +Pexp_apply ...[13:11->13:17] (~isOn13:19->13:23=...[13:24->13:25]) Completable: Value[someFn](~isOn=t) [{ "label": "true", @@ -28,13 +28,13 @@ Completable: Value[someFn](~isOn=t) "documentation": null }] -Complete src/CompletionFunctionArguments.res 16:27 -posCursor:[16:27] posNoWhite:[16:26] Found expr:[14:8->20:1] -Pexp_apply ...[14:8->14:14] (~isOn15:3->15:7=...[16:7->18:8]) -posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:7->18:8] -posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:7->16:28] -posCursor:[16:27] posNoWhite:[16:26] Found expr:[16:14->16:28] -Pexp_apply ...[16:14->16:20] (~isOn16:22->16:26=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 18:27 +posCursor:[18:27] posNoWhite:[18:26] Found expr:[16:8->22:1] +Pexp_apply ...[16:8->16:14] (~isOn17:3->17:7=...[18:7->20:8]) +posCursor:[18:27] posNoWhite:[18:26] Found expr:[18:7->20:8] +posCursor:[18:27] posNoWhite:[18:26] Found expr:[18:7->18:28] +posCursor:[18:27] posNoWhite:[18:26] Found expr:[18:14->18:28] +Pexp_apply ...[18:14->18:20] (~isOn18:22->18:26=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) [{ "label": "true", @@ -50,9 +50,9 @@ Completable: Value[someFn](~isOn) "documentation": null }] -Complete src/CompletionFunctionArguments.res 26:24 -posCursor:[26:24] posNoWhite:[26:23] Found expr:[26:11->26:25] -Pexp_apply ...[26:11->26:22] (...[26:23->26:24]) +Complete src/CompletionFunctionArguments.res 28:24 +posCursor:[28:24] posNoWhite:[28:23] Found expr:[28:11->28:25] +Pexp_apply ...[28:11->28:22] (...[28:23->28:24]) Completable: Value[someOtherFn]($0=f) [{ "label": "false", @@ -62,3 +62,39 @@ Completable: Value[someOtherFn]($0=f) "documentation": null }] +Complete src/CompletionFunctionArguments.res 37:39 +posCursor:[37:39] posNoWhite:[37:38] Found expr:[37:11->37:40] +Pexp_apply ...[37:11->37:30] (~config37:32->37:38=...__ghost__[0:-1->0:-1]) +Completable: Value[someFnTakingVariant](~config) +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\n", + "documentation": null + }, { + "label": "Two", + "kind": 4, + "tags": [], + "detail": "Two\n\n", + "documentation": null + }, { + "label": "Three", + "kind": 4, + "tags": [], + "detail": "Three(int)\n\n", + "documentation": null + }] + +Complete src/CompletionFunctionArguments.res 40:40 +posCursor:[40:40] posNoWhite:[40:39] Found expr:[40:11->40:41] +Pexp_apply ...[40:11->40:30] (~config40:32->40:38=...[40:39->40:40]) +Completable: Value[someFnTakingVariant](~config=O) +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\n", + "documentation": null + }] + From 8fb62d3703aabfdbe2b4741e3e0102de33051614 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 10:56:11 +0100 Subject: [PATCH 08/29] add failing tests for optionals --- .../tests/src/CompletionFunctionArguments.res | 24 ++++++- .../CompletionFunctionArguments.res.txt | 68 +++++++++++++------ 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index 41a29a539..1732d4f80 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -1,5 +1,5 @@ -let someFn = (~isOn) => { - if isOn { +let someFn = (~isOn, ~isOff=false, ()) => { + if isOn && !isOff { "on" } else { "off" @@ -12,6 +12,9 @@ let someFn = (~isOn) => { // let _ = someFn(~isOn=t) // ^com +// let _ = someFn(~isOff=) +// ^com + let _ = someFn( ~isOn={ // switch someFn(~isOn=) @@ -29,8 +32,14 @@ let someOtherFn = (includeName, age) => { type someVariant = One | Two | Three(int) -let someFnTakingVariant = (~config: someVariant) => { +let someFnTakingVariant = ( + configOpt: option, + ~configOpt2=One, + ~config: someVariant, +) => { ignore(config) + ignore(configOpt) + ignore(configOpt2) } // let _ = someFnTakingVariant(~config=) @@ -38,3 +47,12 @@ let someFnTakingVariant = (~config: someVariant) => { // let _ = someFnTakingVariant(~config=O) // ^com + +// let _ = someFnTakingVariant(S) +// ^com + +// let _ = someFnTakingVariant(~configOpt=O) +// ^com + +// let _ = someFnTakingVariant(~configOpt2=O) +// ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index d9a93d21a..52c708ee7 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -1,6 +1,6 @@ -Complete src/CompletionFunctionArguments.res 10:24 -posCursor:[10:24] posNoWhite:[10:23] Found expr:[10:11->10:25] -Pexp_apply ...[10:11->10:17] (~isOn10:19->10:23=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 8:24 +posCursor:[8:24] posNoWhite:[8:23] Found expr:[8:11->8:25] +Pexp_apply ...[8:11->8:17] (~isOn8:19->8:23=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) [{ "label": "true", @@ -16,9 +16,9 @@ Completable: Value[someFn](~isOn) "documentation": null }] -Complete src/CompletionFunctionArguments.res 13:25 -posCursor:[13:25] posNoWhite:[13:24] Found expr:[13:11->13:26] -Pexp_apply ...[13:11->13:17] (~isOn13:19->13:23=...[13:24->13:25]) +Complete src/CompletionFunctionArguments.res 11:25 +posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:11->11:26] +Pexp_apply ...[11:11->11:17] (~isOn11:19->11:23=...[11:24->11:25]) Completable: Value[someFn](~isOn=t) [{ "label": "true", @@ -28,13 +28,19 @@ Completable: Value[someFn](~isOn=t) "documentation": null }] -Complete src/CompletionFunctionArguments.res 18:27 -posCursor:[18:27] posNoWhite:[18:26] Found expr:[16:8->22:1] -Pexp_apply ...[16:8->16:14] (~isOn17:3->17:7=...[18:7->20:8]) -posCursor:[18:27] posNoWhite:[18:26] Found expr:[18:7->20:8] -posCursor:[18:27] posNoWhite:[18:26] Found expr:[18:7->18:28] -posCursor:[18:27] posNoWhite:[18:26] Found expr:[18:14->18:28] -Pexp_apply ...[18:14->18:20] (~isOn18:22->18:26=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 14:25 +posCursor:[14:25] posNoWhite:[14:24] Found expr:[14:11->14:26] +Pexp_apply ...[14:11->14:17] (~isOff14:19->14:24=...__ghost__[0:-1->0:-1]) +Completable: Value[someFn](~isOff) +[] + +Complete src/CompletionFunctionArguments.res 19:27 +posCursor:[19:27] posNoWhite:[19:26] Found expr:[17:8->23:1] +Pexp_apply ...[17:8->17:14] (~isOn18:3->18:7=...[19:7->21:8]) +posCursor:[19:27] posNoWhite:[19:26] Found expr:[19:7->21:8] +posCursor:[19:27] posNoWhite:[19:26] Found expr:[19:7->19:28] +posCursor:[19:27] posNoWhite:[19:26] Found expr:[19:14->19:28] +Pexp_apply ...[19:14->19:20] (~isOn19:22->19:26=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) [{ "label": "true", @@ -50,9 +56,9 @@ Completable: Value[someFn](~isOn) "documentation": null }] -Complete src/CompletionFunctionArguments.res 28:24 -posCursor:[28:24] posNoWhite:[28:23] Found expr:[28:11->28:25] -Pexp_apply ...[28:11->28:22] (...[28:23->28:24]) +Complete src/CompletionFunctionArguments.res 29:24 +posCursor:[29:24] posNoWhite:[29:23] Found expr:[29:11->29:25] +Pexp_apply ...[29:11->29:22] (...[29:23->29:24]) Completable: Value[someOtherFn]($0=f) [{ "label": "false", @@ -62,9 +68,9 @@ Completable: Value[someOtherFn]($0=f) "documentation": null }] -Complete src/CompletionFunctionArguments.res 37:39 -posCursor:[37:39] posNoWhite:[37:38] Found expr:[37:11->37:40] -Pexp_apply ...[37:11->37:30] (~config37:32->37:38=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 44:39 +posCursor:[44:39] posNoWhite:[44:38] Found expr:[44:11->44:40] +Pexp_apply ...[44:11->44:30] (~config44:32->44:38=...__ghost__[0:-1->0:-1]) Completable: Value[someFnTakingVariant](~config) [{ "label": "One", @@ -86,9 +92,9 @@ Completable: Value[someFnTakingVariant](~config) "documentation": null }] -Complete src/CompletionFunctionArguments.res 40:40 -posCursor:[40:40] posNoWhite:[40:39] Found expr:[40:11->40:41] -Pexp_apply ...[40:11->40:30] (~config40:32->40:38=...[40:39->40:40]) +Complete src/CompletionFunctionArguments.res 47:40 +posCursor:[47:40] posNoWhite:[47:39] Found expr:[47:11->47:41] +Pexp_apply ...[47:11->47:30] (~config47:32->47:38=...[47:39->47:40]) Completable: Value[someFnTakingVariant](~config=O) [{ "label": "One", @@ -98,3 +104,21 @@ Completable: Value[someFnTakingVariant](~config=O) "documentation": null }] +Complete src/CompletionFunctionArguments.res 50:32 +posCursor:[50:32] posNoWhite:[50:31] Found expr:[50:11->50:33] +Pexp_apply ...[50:11->50:30] (...[50:31->50:32]) +Completable: Value[someFnTakingVariant]($0=S) +[] + +Complete src/CompletionFunctionArguments.res 53:43 +posCursor:[53:43] posNoWhite:[53:42] Found expr:[53:11->53:44] +Pexp_apply ...[53:11->53:30] (~configOpt53:32->53:41=...[53:42->53:43]) +Completable: Value[someFnTakingVariant](~configOpt=O) +[] + +Complete src/CompletionFunctionArguments.res 56:44 +posCursor:[56:44] posNoWhite:[56:43] Found expr:[56:11->56:45] +Pexp_apply ...[56:11->56:30] (~configOpt256:32->56:42=...[56:43->56:44]) +Completable: Value[someFnTakingVariant](~configOpt2=O) +[] + From 0546ce453d5db9096431333b83496b6282cd135c Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 10:58:22 +0100 Subject: [PATCH 09/29] basic completion for opts --- analysis/src/CompletionBackEnd.ml | 16 ++++++++++++++ .../CompletionFunctionArguments.res.txt | 22 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index da95c7f39..8f6dfe3f7 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1542,6 +1542,22 @@ let completeTypedValue t ~env ~full ~prefix = ~env)) in items + | Some (Toption (env, t)) -> + let items = + [ + Completion.create ~name:"None" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + Completion.create ~name:"Some(_)" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + ] + in + if prefix = "" then items + else + items + |> List.filter (fun (item : Completion.t) -> + Utils.startsWith item.name prefix) | _ -> [] let processCompletable ~debug ~full ~scope ~env ~pos ~forHover diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 52c708ee7..b729ba523 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -32,7 +32,19 @@ Complete src/CompletionFunctionArguments.res 14:25 posCursor:[14:25] posNoWhite:[14:24] Found expr:[14:11->14:26] Pexp_apply ...[14:11->14:17] (~isOff14:19->14:24=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOff) -[] +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] Complete src/CompletionFunctionArguments.res 19:27 posCursor:[19:27] posNoWhite:[19:26] Found expr:[17:8->23:1] @@ -108,7 +120,13 @@ Complete src/CompletionFunctionArguments.res 50:32 posCursor:[50:32] posNoWhite:[50:31] Found expr:[50:11->50:33] Pexp_apply ...[50:11->50:30] (...[50:31->50:32]) Completable: Value[someFnTakingVariant]($0=S) -[] +[{ + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "someVariant", + "documentation": null + }] Complete src/CompletionFunctionArguments.res 53:43 posCursor:[53:43] posNoWhite:[53:42] Found expr:[53:11->53:44] From ac348bb2f2834c4f9258c11e0c7bc39934654a1e Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 12:51:01 +0100 Subject: [PATCH 10/29] expand options where it makes sense --- analysis/src/CompletionBackEnd.ml | 9 +++++++-- .../src/expected/CompletionFunctionArguments.res.txt | 12 +++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 8f6dfe3f7..86bee3a2e 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1509,8 +1509,10 @@ let rec extractType ~env ~package (t : Types.type_expr) = | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None -let completeTypedValue t ~env ~full ~prefix = +let rec completeTypedValue t ~env ~full ~prefix ~expandOption = match t |> extractType ~env ~package:full.package with + | Some (Toption (env, typ)) when expandOption -> + typ |> completeTypedValue ~env ~full ~prefix ~expandOption:false | Some (Tbool env) -> let items = [ @@ -1938,7 +1940,10 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i in match targetLabel with | None -> [] - | Some (_, typ) -> typ |> completeTypedValue ~env ~full ~prefix) + | Some (Labelled _, typ) -> + typ |> completeTypedValue ~env ~full ~prefix ~expandOption:true + | Some (Unlabelled _, typ) -> + typ |> completeTypedValue ~env ~full ~prefix ~expandOption:false) | CnamedArg (cp, prefix, identsSeen) -> let labels = match diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index b729ba523..ea4e07a5c 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -33,13 +33,13 @@ posCursor:[14:25] posNoWhite:[14:24] Found expr:[14:11->14:26] Pexp_apply ...[14:11->14:17] (~isOff14:19->14:24=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOff) [{ - "label": "None", + "label": "true", "kind": 4, "tags": [], "detail": "bool", "documentation": null }, { - "label": "Some(_)", + "label": "false", "kind": 4, "tags": [], "detail": "bool", @@ -138,5 +138,11 @@ Complete src/CompletionFunctionArguments.res 56:44 posCursor:[56:44] posNoWhite:[56:43] Found expr:[56:11->56:45] Pexp_apply ...[56:11->56:30] (~configOpt256:32->56:42=...[56:43->56:44]) Completable: Value[someFnTakingVariant](~configOpt2=O) -[] +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\n", + "documentation": null + }] From 379dd1a9d43f4bf7e8a71abc559a9a34a53ffb1a Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 12:52:28 +0100 Subject: [PATCH 11/29] remove unused test case --- analysis/tests/src/CompletionFunctionArguments.res | 3 --- .../src/expected/CompletionFunctionArguments.res.txt | 12 +++--------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index 1732d4f80..d6a16ac45 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -51,8 +51,5 @@ let someFnTakingVariant = ( // let _ = someFnTakingVariant(S) // ^com -// let _ = someFnTakingVariant(~configOpt=O) -// ^com - // let _ = someFnTakingVariant(~configOpt2=O) // ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index ea4e07a5c..d510c75b8 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -128,15 +128,9 @@ Completable: Value[someFnTakingVariant]($0=S) "documentation": null }] -Complete src/CompletionFunctionArguments.res 53:43 -posCursor:[53:43] posNoWhite:[53:42] Found expr:[53:11->53:44] -Pexp_apply ...[53:11->53:30] (~configOpt53:32->53:41=...[53:42->53:43]) -Completable: Value[someFnTakingVariant](~configOpt=O) -[] - -Complete src/CompletionFunctionArguments.res 56:44 -posCursor:[56:44] posNoWhite:[56:43] Found expr:[56:11->56:45] -Pexp_apply ...[56:11->56:30] (~configOpt256:32->56:42=...[56:43->56:44]) +Complete src/CompletionFunctionArguments.res 53:44 +posCursor:[53:44] posNoWhite:[53:43] Found expr:[53:11->53:45] +Pexp_apply ...[53:11->53:30] (~configOpt253:32->53:42=...[53:43->53:44]) Completable: Value[someFnTakingVariant](~configOpt2=O) [{ "label": "One", From 7b66a3d6ab8684a7e88dd2d0871d2f50e3a16b1d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 12:56:50 +0100 Subject: [PATCH 12/29] make sure payloads (with any as placeholder) are printed for each constructor argument --- analysis/src/CompletionBackEnd.ml | 12 +++++++++++- analysis/tests/src/CompletionFunctionArguments.res | 2 +- .../src/expected/CompletionFunctionArguments.res.txt | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 86bee3a2e..f1fbc4606 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1539,7 +1539,17 @@ let rec completeTypedValue t ~env ~full ~prefix ~expandOption = then None else Some - (Completion.create ~name:constructor.cname.txt + (Completion.create + ~name: + (constructor.cname.txt + ^ + if constructor.args |> List.length > 0 then + "(" + ^ (constructor.args + |> List.map (fun _ -> "_") + |> String.concat ", ") + ^ ")" + else "") ~kind:(Constructor (constructor, "" (* TODO *))) ~env)) in diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index d6a16ac45..2c38e94c2 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -30,7 +30,7 @@ let someOtherFn = (includeName, age) => { // let _ = someOtherFn(f) // ^com -type someVariant = One | Two | Three(int) +type someVariant = One | Two | Three(int, string) let someFnTakingVariant = ( configOpt: option, diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index d510c75b8..949cb6b59 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -97,10 +97,10 @@ Completable: Value[someFnTakingVariant](~config) "detail": "Two\n\n", "documentation": null }, { - "label": "Three", + "label": "Three(_, _)", "kind": 4, "tags": [], - "detail": "Three(int)\n\n", + "detail": "Three(int, string)\n\n", "documentation": null }] From 840cdb5ee2bf118c242a72d2c200f0c15f542057 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 13:10:41 +0100 Subject: [PATCH 13/29] add failing test demonstrating issue with parser --- analysis/tests/src/CompletionFunctionArguments.res | 4 ++++ .../expected/CompletionFunctionArguments.res.txt | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index 2c38e94c2..da5636824 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -53,3 +53,7 @@ let someFnTakingVariant = ( // let _ = someFnTakingVariant(~configOpt2=O) // ^com + +// This below demonstrates an issue when what you're completing is the _last_ labelled argument, and there's a unit application after it. The parser wrongly merges the unit argument as the expression of the labelled argument assignment, where is should really let the trailing unit argument be, and set a %rescript.exprhole as the expression of the assignment, just like it normally does. +// let _ = someFn(~isOff=, ()) +// ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 949cb6b59..136412f13 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -140,3 +140,16 @@ Completable: Value[someFnTakingVariant](~configOpt2=O) "documentation": null }] +Complete src/CompletionFunctionArguments.res 57:25 +posCursor:[57:25] posNoWhite:[57:24] Found expr:[57:11->57:30] +Pexp_apply ...[57:11->57:17] (~isOff57:19->57:24=...[57:27->57:29]) +Completable: CnamedArg(Value[someFn], "", [isOff]) +Found type for function (~isOn: bool, ~isOff: bool=?, unit) => string +[{ + "label": "isOn", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + From 8ec981ac230c415ea883086dba5c01976a4cdfb5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 23 Dec 2022 13:25:17 +0100 Subject: [PATCH 14/29] include local values and modules in type based completions when there is a prefix to filter on --- analysis/src/CompletionBackEnd.ml | 126 ++++++++++-------- .../tests/src/CompletionFunctionArguments.res | 4 + .../CompletionFunctionArguments.res.txt | 86 +++++++----- 3 files changed, 125 insertions(+), 91 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index f1fbc4606..88401eb2a 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1509,68 +1509,80 @@ let rec extractType ~env ~package (t : Types.type_expr) = | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None -let rec completeTypedValue t ~env ~full ~prefix ~expandOption = - match t |> extractType ~env ~package:full.package with - | Some (Toption (env, typ)) when expandOption -> - typ |> completeTypedValue ~env ~full ~prefix ~expandOption:false - | Some (Tbool env) -> +let completeTypedValue ~env ~full ~prefix ~expandOption = + let namesUsed = Hashtbl.create 10 in + let rec completeTypedValueInner t ~env ~full ~prefix ~expandOption = let items = - [ - Completion.create ~name:"true" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - Completion.create ~name:"false" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - ] - in - if prefix = "" then items - else - items - |> List.filter (fun (item : Completion.t) -> - Utils.startsWith item.name prefix) - | Some (Tvariant {env; constructors}) -> - let items = - constructors - |> List.filter_map (fun (constructor : Constructor.t) -> - if - prefix <> "" - && not (Utils.startsWith constructor.cname.txt prefix) - then None - else - Some - (Completion.create - ~name: - (constructor.cname.txt - ^ - if constructor.args |> List.length > 0 then - "(" - ^ (constructor.args - |> List.map (fun _ -> "_") - |> String.concat ", ") - ^ ")" - else "") - ~kind:(Constructor (constructor, "" (* TODO *))) - ~env)) - in - items - | Some (Toption (env, t)) -> - let items = - [ - Completion.create ~name:"None" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - Completion.create ~name:"Some(_)" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - ] + match t |> extractType ~env ~package:full.package with + | Some (Toption (env, typ)) when expandOption -> + typ |> completeTypedValueInner ~env ~full ~prefix ~expandOption:false + | Some (Tbool env) -> + let items = + [ + Completion.create ~name:"true" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + Completion.create ~name:"false" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + ] + in + if prefix = "" then items + else + items + |> List.filter (fun (item : Completion.t) -> + Utils.startsWith item.name prefix) + | Some (Tvariant {env; constructors}) -> + let items = + constructors + |> List.filter_map (fun (constructor : Constructor.t) -> + if + prefix <> "" + && not (Utils.startsWith constructor.cname.txt prefix) + then None + else + Some + (Completion.create + ~name: + (constructor.cname.txt + ^ + if constructor.args |> List.length > 0 then + "(" + ^ (constructor.args + |> List.map (fun _ -> "_") + |> String.concat ", ") + ^ ")" + else "") + ~kind:(Constructor (constructor, "" (* TODO *))) + ~env)) + in + items + | Some (Toption (env, t)) -> + let items = + [ + Completion.create ~name:"None" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + Completion.create ~name:"Some(_)" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + ] + in + if prefix = "" then items + else + items + |> List.filter (fun (item : Completion.t) -> + Utils.startsWith item.name prefix) + | _ -> [] in + (* Include all values and modules in completion if there's a prefix, not otherwise *) if prefix = "" then items else items - |> List.filter (fun (item : Completion.t) -> - Utils.startsWith item.name prefix) - | _ -> [] + @ completionForExportedValues ~env ~prefix ~exact:false ~namesUsed + @ completionForExportedModules ~env ~prefix ~exact:false ~namesUsed + in + completeTypedValueInner ~env ~full ~prefix ~expandOption let processCompletable ~debug ~full ~scope ~env ~pos ~forHover (completable : Completable.t) = diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index da5636824..481a4e963 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -6,6 +6,8 @@ let someFn = (~isOn, ~isOff=false, ()) => { } } +let tLocalVar = false + // let _ = someFn(~isOn=) // ^com @@ -30,6 +32,8 @@ let someOtherFn = (includeName, age) => { // let _ = someOtherFn(f) // ^com +module OIncludeMeInCompletions = {} + type someVariant = One | Two | Three(int, string) let someFnTakingVariant = ( diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 136412f13..902df3d3c 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -1,6 +1,6 @@ -Complete src/CompletionFunctionArguments.res 8:24 -posCursor:[8:24] posNoWhite:[8:23] Found expr:[8:11->8:25] -Pexp_apply ...[8:11->8:17] (~isOn8:19->8:23=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 10:24 +posCursor:[10:24] posNoWhite:[10:23] Found expr:[10:11->10:25] +Pexp_apply ...[10:11->10:17] (~isOn10:19->10:23=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) [{ "label": "true", @@ -16,9 +16,9 @@ Completable: Value[someFn](~isOn) "documentation": null }] -Complete src/CompletionFunctionArguments.res 11:25 -posCursor:[11:25] posNoWhite:[11:24] Found expr:[11:11->11:26] -Pexp_apply ...[11:11->11:17] (~isOn11:19->11:23=...[11:24->11:25]) +Complete src/CompletionFunctionArguments.res 13:25 +posCursor:[13:25] posNoWhite:[13:24] Found expr:[13:11->13:26] +Pexp_apply ...[13:11->13:17] (~isOn13:19->13:23=...[13:24->13:25]) Completable: Value[someFn](~isOn=t) [{ "label": "true", @@ -26,11 +26,17 @@ Completable: Value[someFn](~isOn=t) "tags": [], "detail": "bool", "documentation": null + }, { + "label": "tLocalVar", + "kind": 12, + "tags": [], + "detail": "bool", + "documentation": null }] -Complete src/CompletionFunctionArguments.res 14:25 -posCursor:[14:25] posNoWhite:[14:24] Found expr:[14:11->14:26] -Pexp_apply ...[14:11->14:17] (~isOff14:19->14:24=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 16:25 +posCursor:[16:25] posNoWhite:[16:24] Found expr:[16:11->16:26] +Pexp_apply ...[16:11->16:17] (~isOff16:19->16:24=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOff) [{ "label": "true", @@ -46,13 +52,13 @@ Completable: Value[someFn](~isOff) "documentation": null }] -Complete src/CompletionFunctionArguments.res 19:27 -posCursor:[19:27] posNoWhite:[19:26] Found expr:[17:8->23:1] -Pexp_apply ...[17:8->17:14] (~isOn18:3->18:7=...[19:7->21:8]) -posCursor:[19:27] posNoWhite:[19:26] Found expr:[19:7->21:8] -posCursor:[19:27] posNoWhite:[19:26] Found expr:[19:7->19:28] -posCursor:[19:27] posNoWhite:[19:26] Found expr:[19:14->19:28] -Pexp_apply ...[19:14->19:20] (~isOn19:22->19:26=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 21:27 +posCursor:[21:27] posNoWhite:[21:26] Found expr:[19:8->25:1] +Pexp_apply ...[19:8->19:14] (~isOn20:3->20:7=...[21:7->23:8]) +posCursor:[21:27] posNoWhite:[21:26] Found expr:[21:7->23:8] +posCursor:[21:27] posNoWhite:[21:26] Found expr:[21:7->21:28] +posCursor:[21:27] posNoWhite:[21:26] Found expr:[21:14->21:28] +Pexp_apply ...[21:14->21:20] (~isOn21:22->21:26=...__ghost__[0:-1->0:-1]) Completable: Value[someFn](~isOn) [{ "label": "true", @@ -68,9 +74,9 @@ Completable: Value[someFn](~isOn) "documentation": null }] -Complete src/CompletionFunctionArguments.res 29:24 -posCursor:[29:24] posNoWhite:[29:23] Found expr:[29:11->29:25] -Pexp_apply ...[29:11->29:22] (...[29:23->29:24]) +Complete src/CompletionFunctionArguments.res 31:24 +posCursor:[31:24] posNoWhite:[31:23] Found expr:[31:11->31:25] +Pexp_apply ...[31:11->31:22] (...[31:23->31:24]) Completable: Value[someOtherFn]($0=f) [{ "label": "false", @@ -80,9 +86,9 @@ Completable: Value[someOtherFn]($0=f) "documentation": null }] -Complete src/CompletionFunctionArguments.res 44:39 -posCursor:[44:39] posNoWhite:[44:38] Found expr:[44:11->44:40] -Pexp_apply ...[44:11->44:30] (~config44:32->44:38=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 48:39 +posCursor:[48:39] posNoWhite:[48:38] Found expr:[48:11->48:40] +Pexp_apply ...[48:11->48:30] (~config48:32->48:38=...__ghost__[0:-1->0:-1]) Completable: Value[someFnTakingVariant](~config) [{ "label": "One", @@ -104,9 +110,9 @@ Completable: Value[someFnTakingVariant](~config) "documentation": null }] -Complete src/CompletionFunctionArguments.res 47:40 -posCursor:[47:40] posNoWhite:[47:39] Found expr:[47:11->47:41] -Pexp_apply ...[47:11->47:30] (~config47:32->47:38=...[47:39->47:40]) +Complete src/CompletionFunctionArguments.res 51:40 +posCursor:[51:40] posNoWhite:[51:39] Found expr:[51:11->51:41] +Pexp_apply ...[51:11->51:30] (~config51:32->51:38=...[51:39->51:40]) Completable: Value[someFnTakingVariant](~config=O) [{ "label": "One", @@ -114,11 +120,17 @@ Completable: Value[someFnTakingVariant](~config=O) "tags": [], "detail": "One\n\n", "documentation": null + }, { + "label": "OIncludeMeInCompletions", + "kind": 9, + "tags": [], + "detail": "module", + "documentation": null }] -Complete src/CompletionFunctionArguments.res 50:32 -posCursor:[50:32] posNoWhite:[50:31] Found expr:[50:11->50:33] -Pexp_apply ...[50:11->50:30] (...[50:31->50:32]) +Complete src/CompletionFunctionArguments.res 54:32 +posCursor:[54:32] posNoWhite:[54:31] Found expr:[54:11->54:33] +Pexp_apply ...[54:11->54:30] (...[54:31->54:32]) Completable: Value[someFnTakingVariant]($0=S) [{ "label": "Some(_)", @@ -128,9 +140,9 @@ Completable: Value[someFnTakingVariant]($0=S) "documentation": null }] -Complete src/CompletionFunctionArguments.res 53:44 -posCursor:[53:44] posNoWhite:[53:43] Found expr:[53:11->53:45] -Pexp_apply ...[53:11->53:30] (~configOpt253:32->53:42=...[53:43->53:44]) +Complete src/CompletionFunctionArguments.res 57:44 +posCursor:[57:44] posNoWhite:[57:43] Found expr:[57:11->57:45] +Pexp_apply ...[57:11->57:30] (~configOpt257:32->57:42=...[57:43->57:44]) Completable: Value[someFnTakingVariant](~configOpt2=O) [{ "label": "One", @@ -138,11 +150,17 @@ Completable: Value[someFnTakingVariant](~configOpt2=O) "tags": [], "detail": "One\n\n", "documentation": null + }, { + "label": "OIncludeMeInCompletions", + "kind": 9, + "tags": [], + "detail": "module", + "documentation": null }] -Complete src/CompletionFunctionArguments.res 57:25 -posCursor:[57:25] posNoWhite:[57:24] Found expr:[57:11->57:30] -Pexp_apply ...[57:11->57:17] (~isOff57:19->57:24=...[57:27->57:29]) +Complete src/CompletionFunctionArguments.res 61:25 +posCursor:[61:25] posNoWhite:[61:24] Found expr:[61:11->61:30] +Pexp_apply ...[61:11->61:17] (~isOff61:19->61:24=...[61:27->61:29]) Completable: CnamedArg(Value[someFn], "", [isOff]) Found type for function (~isOn: bool, ~isOff: bool=?, unit) => string [{ From 8d261be24a4f958a89281ab6be8b4955140bcdc4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 15:08:43 +0100 Subject: [PATCH 15/29] fix full variant completion item text --- analysis/src/CompletionBackEnd.ml | 20 ++++++++++++++----- .../CompletionFunctionArguments.res.txt | 10 +++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 88401eb2a..4ec926dfe 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1487,7 +1487,12 @@ type extractedType = | Tuple of QueryEnv.t * Types.type_expr list | Toption of QueryEnv.t * Types.type_expr | Tbool of QueryEnv.t - | Tvariant of {env: QueryEnv.t; constructors: Constructor.t list} + | Tvariant of { + env: QueryEnv.t; + constructors: Constructor.t list; + variantDecl: Types.type_declaration; + variantName: string; + } (* This is a more general extraction function for pulling out the type of a type_expr. We already have other similar functions, but they are all specialized on something (variants, records, etc). *) let rec extractType ~env ~package (t : Types.type_expr) = @@ -1503,8 +1508,10 @@ let rec extractType ~env ~package (t : Types.type_expr) = match References.digConstructor ~env ~package path with | Some (env, {item = {decl = {type_manifest = Some t1}}}) -> extractType ~env ~package t1 - | Some (env, {item = {kind = Type.Variant constructors}}) -> - Some (Tvariant {env; constructors}) + | Some (env, {name; item = {decl; kind = Type.Variant constructors}}) -> + Some + (Tvariant + {env; constructors; variantName = name.txt; variantDecl = decl}) | _ -> None) | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None @@ -1532,7 +1539,7 @@ let completeTypedValue ~env ~full ~prefix ~expandOption = items |> List.filter (fun (item : Completion.t) -> Utils.startsWith item.name prefix) - | Some (Tvariant {env; constructors}) -> + | Some (Tvariant {env; constructors; variantDecl; variantName}) -> let items = constructors |> List.filter_map (fun (constructor : Constructor.t) -> @@ -1553,7 +1560,10 @@ let completeTypedValue ~env ~full ~prefix ~expandOption = |> String.concat ", ") ^ ")" else "") - ~kind:(Constructor (constructor, "" (* TODO *))) + ~kind: + (Constructor + ( constructor, + variantDecl |> Shared.declToString variantName )) ~env)) in items diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 902df3d3c..681c6be6b 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -94,19 +94,19 @@ Completable: Value[someFnTakingVariant](~config) "label": "One", "kind": 4, "tags": [], - "detail": "One\n\n", + "detail": "One\n\ntype someVariant = One | Two | Three(int, string)", "documentation": null }, { "label": "Two", "kind": 4, "tags": [], - "detail": "Two\n\n", + "detail": "Two\n\ntype someVariant = One | Two | Three(int, string)", "documentation": null }, { "label": "Three(_, _)", "kind": 4, "tags": [], - "detail": "Three(int, string)\n\n", + "detail": "Three(int, string)\n\ntype someVariant = One | Two | Three(int, string)", "documentation": null }] @@ -118,7 +118,7 @@ Completable: Value[someFnTakingVariant](~config=O) "label": "One", "kind": 4, "tags": [], - "detail": "One\n\n", + "detail": "One\n\ntype someVariant = One | Two | Three(int, string)", "documentation": null }, { "label": "OIncludeMeInCompletions", @@ -148,7 +148,7 @@ Completable: Value[someFnTakingVariant](~configOpt2=O) "label": "One", "kind": 4, "tags": [], - "detail": "One\n\n", + "detail": "One\n\ntype someVariant = One | Two | Three(int, string)", "documentation": null }, { "label": "OIncludeMeInCompletions", From 6d10c56fe2b5a7a3a7b8225fe8f4980164e8ddba Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 18:58:19 +0100 Subject: [PATCH 16/29] add more cases to tests --- .../tests/src/CompletionFunctionArguments.res | 15 +++++- .../CompletionFunctionArguments.res.txt | 50 ++++++++++++------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index 481a4e963..f70e033cd 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -25,8 +25,11 @@ let _ = someFn( }, ) -let someOtherFn = (includeName, age) => { - "Hello" ++ (includeName ? " Some Name" : "") ++ ", you are age " ++ Belt.Int.toString(age) +let someOtherFn = (includeName, age, includeAge) => { + "Hello" ++ + (includeName ? " Some Name" : "") ++ + ", you are age " ++ + Belt.Int.toString(includeAge ? age : 0) } // let _ = someOtherFn(f) @@ -58,6 +61,14 @@ let someFnTakingVariant = ( // let _ = someFnTakingVariant(~configOpt2=O) // ^com +// --- UNIMPLEMENTED CASES --- +// The following two cases does not complete to a values because of ambiguity - should it complete for a value, or for a named argument? We don't know whether the function has args or not when deciding, since we're just in the AST at that point. Can potentially be fixed in the future. +// let _ = someOtherFn() +// ^com +// let _ = someOtherFn(1, 2, ) +// ^com + +// --- BROKEN PARSER CASES --- // This below demonstrates an issue when what you're completing is the _last_ labelled argument, and there's a unit application after it. The parser wrongly merges the unit argument as the expression of the labelled argument assignment, where is should really let the trailing unit argument be, and set a %rescript.exprhole as the expression of the assignment, just like it normally does. // let _ = someFn(~isOff=, ()) // ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index 681c6be6b..a959c85e5 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -74,9 +74,9 @@ Completable: Value[someFn](~isOn) "documentation": null }] -Complete src/CompletionFunctionArguments.res 31:24 -posCursor:[31:24] posNoWhite:[31:23] Found expr:[31:11->31:25] -Pexp_apply ...[31:11->31:22] (...[31:23->31:24]) +Complete src/CompletionFunctionArguments.res 34:24 +posCursor:[34:24] posNoWhite:[34:23] Found expr:[34:11->34:25] +Pexp_apply ...[34:11->34:22] (...[34:23->34:24]) Completable: Value[someOtherFn]($0=f) [{ "label": "false", @@ -86,9 +86,9 @@ Completable: Value[someOtherFn]($0=f) "documentation": null }] -Complete src/CompletionFunctionArguments.res 48:39 -posCursor:[48:39] posNoWhite:[48:38] Found expr:[48:11->48:40] -Pexp_apply ...[48:11->48:30] (~config48:32->48:38=...__ghost__[0:-1->0:-1]) +Complete src/CompletionFunctionArguments.res 51:39 +posCursor:[51:39] posNoWhite:[51:38] Found expr:[51:11->51:40] +Pexp_apply ...[51:11->51:30] (~config51:32->51:38=...__ghost__[0:-1->0:-1]) Completable: Value[someFnTakingVariant](~config) [{ "label": "One", @@ -110,9 +110,9 @@ Completable: Value[someFnTakingVariant](~config) "documentation": null }] -Complete src/CompletionFunctionArguments.res 51:40 -posCursor:[51:40] posNoWhite:[51:39] Found expr:[51:11->51:41] -Pexp_apply ...[51:11->51:30] (~config51:32->51:38=...[51:39->51:40]) +Complete src/CompletionFunctionArguments.res 54:40 +posCursor:[54:40] posNoWhite:[54:39] Found expr:[54:11->54:41] +Pexp_apply ...[54:11->54:30] (~config54:32->54:38=...[54:39->54:40]) Completable: Value[someFnTakingVariant](~config=O) [{ "label": "One", @@ -128,9 +128,9 @@ Completable: Value[someFnTakingVariant](~config=O) "documentation": null }] -Complete src/CompletionFunctionArguments.res 54:32 -posCursor:[54:32] posNoWhite:[54:31] Found expr:[54:11->54:33] -Pexp_apply ...[54:11->54:30] (...[54:31->54:32]) +Complete src/CompletionFunctionArguments.res 57:32 +posCursor:[57:32] posNoWhite:[57:31] Found expr:[57:11->57:33] +Pexp_apply ...[57:11->57:30] (...[57:31->57:32]) Completable: Value[someFnTakingVariant]($0=S) [{ "label": "Some(_)", @@ -140,9 +140,9 @@ Completable: Value[someFnTakingVariant]($0=S) "documentation": null }] -Complete src/CompletionFunctionArguments.res 57:44 -posCursor:[57:44] posNoWhite:[57:43] Found expr:[57:11->57:45] -Pexp_apply ...[57:11->57:30] (~configOpt257:32->57:42=...[57:43->57:44]) +Complete src/CompletionFunctionArguments.res 60:44 +posCursor:[60:44] posNoWhite:[60:43] Found expr:[60:11->60:45] +Pexp_apply ...[60:11->60:30] (~configOpt260:32->60:42=...[60:43->60:44]) Completable: Value[someFnTakingVariant](~configOpt2=O) [{ "label": "One", @@ -158,9 +158,23 @@ Completable: Value[someFnTakingVariant](~configOpt2=O) "documentation": null }] -Complete src/CompletionFunctionArguments.res 61:25 -posCursor:[61:25] posNoWhite:[61:24] Found expr:[61:11->61:30] -Pexp_apply ...[61:11->61:17] (~isOff61:19->61:24=...[61:27->61:29]) +Complete src/CompletionFunctionArguments.res 65:23 +posCursor:[65:23] posNoWhite:[65:22] Found expr:[65:11->65:24] +Pexp_apply ...[65:11->65:22] (...[65:23->65:24]) +Completable: CnamedArg(Value[someOtherFn], "", []) +Found type for function (bool, int, bool) => string +[] + +Complete src/CompletionFunctionArguments.res 67:28 +posCursor:[67:28] posNoWhite:[67:27] Found expr:[67:11->67:30] +Pexp_apply ...[67:11->67:22] (...[67:23->67:24], ...[67:26->67:27]) +Completable: CnamedArg(Value[someOtherFn], "", []) +Found type for function (bool, int, bool) => string +[] + +Complete src/CompletionFunctionArguments.res 72:25 +posCursor:[72:25] posNoWhite:[72:24] Found expr:[72:11->72:30] +Pexp_apply ...[72:11->72:17] (~isOff72:19->72:24=...[72:27->72:29]) Completable: CnamedArg(Value[someFn], "", [isOff]) Found type for function (~isOn: bool, ~isOff: bool=?, unit) => string [{ From 17832b5c3d8f1a2eb002dbb837ab7bd5e6612f1b Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 18:59:22 +0100 Subject: [PATCH 17/29] add = as trigger character for completion --- server/src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/server.ts b/server/src/server.ts index 5b3be2505..e381851a2 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1111,7 +1111,7 @@ function onMessage(msg: p.Message) { codeActionProvider: true, renameProvider: { prepareProvider: true }, documentSymbolProvider: true, - completionProvider: { triggerCharacters: [".", ">", "@", "~", '"'] }, + completionProvider: { triggerCharacters: [".", ">", "@", "~", '"', "="] }, semanticTokensProvider: { legend: { tokenTypes: [ From f0e894edded446fb72cb4eee22fd5fffe20f1147 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 19:17:16 +0100 Subject: [PATCH 18/29] cleanup --- analysis/src/CompletionBackEnd.ml | 5 +---- analysis/src/CompletionFrontEnd.ml | 2 -- analysis/src/SharedTypes.ml | 6 ------ 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 4ec926dfe..6bbf9a5c5 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1499,11 +1499,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], _) -> - (* Handle option. TODO: Look up how the compiler does this and copy that behavior. *) Some (Toption (env, payloadTypeExpr)) - | Tconstr (Path.Pident {name = "bool"}, [], _) -> - (* Handle bool. TODO: Look up how the compiler does this and copy that behavior. *) - Some (Tbool env) + | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) | Tconstr (path, _, _) -> ( match References.digConstructor ~env ~package path with | Some (env, {item = {decl = {type_manifest = Some t1}}}) -> diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 1451048f1..250629edf 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -149,8 +149,6 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor {contextPath; argumentLabel = Labelled labelled.name; prefix = ""}) else loop rest | {label = None; exp} :: rest -> - (* TODO: Better guard for this... This is so completion does not trigger - inside of template string calls, which are regular calls *) if Res_parsetree_viewer.isTemplateLiteral exp then None else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then (* Completing in an unlabelled argument *) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 4d15e47f0..89686c352 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -498,12 +498,6 @@ let locItemToString {loc = {Location.loc_start; loc_end}; locType} = (* needed for debugging *) let _ = locItemToString -type polyVariantConstructor = { - name: string; - payload: Types.type_expr option; - args: Types.type_expr list; -} - module Completable = struct (* Completion context *) type completionContext = Type | Value | Module | Field From 41191a85fa113bf53696aa1a5921efd206dcf6c4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 19:23:29 +0100 Subject: [PATCH 19/29] cleanup --- analysis/src/CompletionBackEnd.ml | 15 ++------------- analysis/src/SharedTypes.ml | 12 ++++++++++++ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 6bbf9a5c5..8e1182269 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1483,23 +1483,12 @@ let getArgs ~env (t : Types.type_expr) ~full = in t |> getArgsLoop ~env ~full ~currentArgumentPosition:0 -type extractedType = - | Tuple of QueryEnv.t * Types.type_expr list - | Toption of QueryEnv.t * Types.type_expr - | Tbool of QueryEnv.t - | Tvariant of { - env: QueryEnv.t; - constructors: Constructor.t list; - variantDecl: Types.type_declaration; - variantName: string; - } - -(* This is a more general extraction function for pulling out the type of a type_expr. We already have other similar functions, but they are all specialized on something (variants, records, etc). *) +(** Pulls out a type we can complete from a type expr. *) 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)) + Some (Completable.Toption (env, payloadTypeExpr)) | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) | Tconstr (path, _, _) -> ( match References.digConstructor ~env ~package path with diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 89686c352..f70c48bb4 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -536,6 +536,18 @@ module Completable = struct prefix: string; } + (** An extracted type from a type expr *) + type extractedType = + | Tuple of QueryEnv.t * Types.type_expr list + | Toption of QueryEnv.t * Types.type_expr + | Tbool of QueryEnv.t + | Tvariant of { + env: QueryEnv.t; + constructors: Constructor.t list; + variantDecl: Types.type_declaration; + variantName: string; + } + let toString = let completionContextToString = function | Value -> "Value" From 8755a92c193092ec334cb41b00c3bc907668f096 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 19:39:31 +0100 Subject: [PATCH 20/29] comment + clarify prop name --- analysis/src/CompletionBackEnd.ml | 4 ++-- analysis/src/CompletionFrontEnd.ml | 16 ++++++++++++---- analysis/src/SharedTypes.ml | 7 ++++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 8e1182269..584188488 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1942,10 +1942,10 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i in (dec2, doc)) |> List.map mkDecorator - | Cargument {contextPath; argumentLabel; prefix} -> ( + | Cargument {functionContextPath; argumentLabel; prefix} -> ( let labels = match - contextPath + functionContextPath |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env ~exact:true ~scope |> completionsGetTypeEnv diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 250629edf..bcd33a38e 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -142,11 +142,19 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor | Some prefix -> Some (Cargument - {contextPath; argumentLabel = Labelled labelled.name; prefix}) + { + functionContextPath = contextPath; + argumentLabel = Labelled labelled.name; + prefix; + }) else if isExprHole exp then Some (Cargument - {contextPath; argumentLabel = Labelled labelled.name; prefix = ""}) + { + functionContextPath = contextPath; + argumentLabel = Labelled labelled.name; + prefix = ""; + }) else loop rest | {label = None; exp} :: rest -> if Res_parsetree_viewer.isTemplateLiteral exp then None @@ -158,7 +166,7 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor Some (Cargument { - contextPath; + functionContextPath = contextPath; argumentLabel = Unlabelled {argumentPosition = !unlabelledCount}; prefix; @@ -167,7 +175,7 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor Some (Cargument { - contextPath; + functionContextPath = contextPath; argumentLabel = Unlabelled {argumentPosition = !unlabelledCount}; prefix = ""; }) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index f70c48bb4..548d235a6 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -531,10 +531,11 @@ module Completable = struct | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for ), complete for the value of `someBoolArg` (true or false). *) (** An extracted type from a type expr *) type extractedType = @@ -585,8 +586,8 @@ module Completable = struct | Cnone -> "Cnone" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - | Cargument {contextPath; argumentLabel; prefix} -> - contextPathToString contextPath + | Cargument {functionContextPath; argumentLabel; prefix} -> + contextPathToString functionContextPath ^ "(" ^ (match argumentLabel with | Unlabelled {argumentPosition} -> "$" ^ string_of_int argumentPosition From aebffa446e7f195b4be8185316b0d7b2d9fcde3d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 19:42:48 +0100 Subject: [PATCH 21/29] polish test output a bit --- analysis/src/SharedTypes.ml | 3 ++- .../CompletionFunctionArguments.res.txt | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 548d235a6..205712f02 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -587,7 +587,8 @@ module Completable = struct | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cargument {functionContextPath; argumentLabel; prefix} -> - contextPathToString functionContextPath + "Cargument " + ^ contextPathToString functionContextPath ^ "(" ^ (match argumentLabel with | Unlabelled {argumentPosition} -> "$" ^ string_of_int argumentPosition diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index a959c85e5..cbabbf2c6 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -1,7 +1,7 @@ Complete src/CompletionFunctionArguments.res 10:24 posCursor:[10:24] posNoWhite:[10:23] Found expr:[10:11->10:25] Pexp_apply ...[10:11->10:17] (~isOn10:19->10:23=...__ghost__[0:-1->0:-1]) -Completable: Value[someFn](~isOn) +Completable: Cargument Value[someFn](~isOn) [{ "label": "true", "kind": 4, @@ -19,7 +19,7 @@ Completable: Value[someFn](~isOn) Complete src/CompletionFunctionArguments.res 13:25 posCursor:[13:25] posNoWhite:[13:24] Found expr:[13:11->13:26] Pexp_apply ...[13:11->13:17] (~isOn13:19->13:23=...[13:24->13:25]) -Completable: Value[someFn](~isOn=t) +Completable: Cargument Value[someFn](~isOn=t) [{ "label": "true", "kind": 4, @@ -37,7 +37,7 @@ Completable: Value[someFn](~isOn=t) Complete src/CompletionFunctionArguments.res 16:25 posCursor:[16:25] posNoWhite:[16:24] Found expr:[16:11->16:26] Pexp_apply ...[16:11->16:17] (~isOff16:19->16:24=...__ghost__[0:-1->0:-1]) -Completable: Value[someFn](~isOff) +Completable: Cargument Value[someFn](~isOff) [{ "label": "true", "kind": 4, @@ -59,7 +59,7 @@ posCursor:[21:27] posNoWhite:[21:26] Found expr:[21:7->23:8] posCursor:[21:27] posNoWhite:[21:26] Found expr:[21:7->21:28] posCursor:[21:27] posNoWhite:[21:26] Found expr:[21:14->21:28] Pexp_apply ...[21:14->21:20] (~isOn21:22->21:26=...__ghost__[0:-1->0:-1]) -Completable: Value[someFn](~isOn) +Completable: Cargument Value[someFn](~isOn) [{ "label": "true", "kind": 4, @@ -77,7 +77,7 @@ Completable: Value[someFn](~isOn) Complete src/CompletionFunctionArguments.res 34:24 posCursor:[34:24] posNoWhite:[34:23] Found expr:[34:11->34:25] Pexp_apply ...[34:11->34:22] (...[34:23->34:24]) -Completable: Value[someOtherFn]($0=f) +Completable: Cargument Value[someOtherFn]($0=f) [{ "label": "false", "kind": 4, @@ -89,7 +89,7 @@ Completable: Value[someOtherFn]($0=f) Complete src/CompletionFunctionArguments.res 51:39 posCursor:[51:39] posNoWhite:[51:38] Found expr:[51:11->51:40] Pexp_apply ...[51:11->51:30] (~config51:32->51:38=...__ghost__[0:-1->0:-1]) -Completable: Value[someFnTakingVariant](~config) +Completable: Cargument Value[someFnTakingVariant](~config) [{ "label": "One", "kind": 4, @@ -113,7 +113,7 @@ Completable: Value[someFnTakingVariant](~config) Complete src/CompletionFunctionArguments.res 54:40 posCursor:[54:40] posNoWhite:[54:39] Found expr:[54:11->54:41] Pexp_apply ...[54:11->54:30] (~config54:32->54:38=...[54:39->54:40]) -Completable: Value[someFnTakingVariant](~config=O) +Completable: Cargument Value[someFnTakingVariant](~config=O) [{ "label": "One", "kind": 4, @@ -131,7 +131,7 @@ Completable: Value[someFnTakingVariant](~config=O) Complete src/CompletionFunctionArguments.res 57:32 posCursor:[57:32] posNoWhite:[57:31] Found expr:[57:11->57:33] Pexp_apply ...[57:11->57:30] (...[57:31->57:32]) -Completable: Value[someFnTakingVariant]($0=S) +Completable: Cargument Value[someFnTakingVariant]($0=S) [{ "label": "Some(_)", "kind": 4, @@ -143,7 +143,7 @@ Completable: Value[someFnTakingVariant]($0=S) Complete src/CompletionFunctionArguments.res 60:44 posCursor:[60:44] posNoWhite:[60:43] Found expr:[60:11->60:45] Pexp_apply ...[60:11->60:30] (~configOpt260:32->60:42=...[60:43->60:44]) -Completable: Value[someFnTakingVariant](~configOpt2=O) +Completable: Cargument Value[someFnTakingVariant](~configOpt2=O) [{ "label": "One", "kind": 4, From e5a0950bbcdd766bf85b062fed67a0b2a79d3f30 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 25 Dec 2022 20:34:13 +0100 Subject: [PATCH 22/29] only pick up regular Lident --- analysis/src/CompletionFrontEnd.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index bcd33a38e..56ecbd7c5 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -108,8 +108,8 @@ let extractJsxProps ~(compName : Longident.t Location.loc) ~args = let extractCompletableArgValueInfo exp = match exp.Parsetree.pexp_desc with - | Pexp_ident {txt} -> Some (Utils.flattenLongIdent txt |> List.hd) - | Pexp_construct ({txt}, _) -> Some (Utils.flattenLongIdent txt |> List.hd) + | Pexp_ident {txt = Lident txt} -> Some txt + | Pexp_construct ({txt = Lident txt}, _) -> Some txt | _ -> None let isExprHole exp = From 8d81bc8fe01b57b7b46e50e5878a1ebcca9cabf6 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 27 Dec 2022 21:42:52 +0100 Subject: [PATCH 23/29] use env where completion started to populate values and module completions --- analysis/src/CompletionBackEnd.ml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 584188488..65d1350c3 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1502,7 +1502,8 @@ let rec extractType ~env ~package (t : Types.type_expr) = | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None -let completeTypedValue ~env ~full ~prefix ~expandOption = +let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix + ~expandOption = let namesUsed = Hashtbl.create 10 in let rec completeTypedValueInner t ~env ~full ~prefix ~expandOption = let items = @@ -1575,8 +1576,10 @@ let completeTypedValue ~env ~full ~prefix ~expandOption = if prefix = "" then items else items - @ completionForExportedValues ~env ~prefix ~exact:false ~namesUsed - @ completionForExportedModules ~env ~prefix ~exact:false ~namesUsed + @ completionForExportedValues ~env:envWhereCompletionStarted ~prefix + ~exact:false ~namesUsed + @ completionForExportedModules ~env:envWhereCompletionStarted ~prefix + ~exact:false ~namesUsed in completeTypedValueInner ~env ~full ~prefix ~expandOption @@ -1943,6 +1946,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i (dec2, doc)) |> List.map mkDecorator | Cargument {functionContextPath; argumentLabel; prefix} -> ( + let envWhereCompletionStarted = env in let labels = match functionContextPath @@ -1959,9 +1963,13 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i match targetLabel with | None -> [] | Some (Labelled _, typ) -> - typ |> completeTypedValue ~env ~full ~prefix ~expandOption:true + typ + |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix + ~expandOption:true | Some (Unlabelled _, typ) -> - typ |> completeTypedValue ~env ~full ~prefix ~expandOption:false) + typ + |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix + ~expandOption:false) | CnamedArg (cp, prefix, identsSeen) -> let labels = match From 9856c30689931cca0090699813c9a50e6d43cccb Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 27 Dec 2022 21:45:41 +0100 Subject: [PATCH 24/29] common filter --- analysis/src/CompletionBackEnd.ml | 101 +++++++++++++----------------- 1 file changed, 44 insertions(+), 57 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 65d1350c3..d50a3888f 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1502,6 +1502,13 @@ let rec extractType ~env ~package (t : Types.type_expr) = | Ttuple expressions -> Some (Tuple (env, expressions)) | _ -> None +let filterItems items ~prefix = + if prefix = "" then items + else + items + |> List.filter (fun (item : Completion.t) -> + Utils.startsWith item.name prefix) + let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~expandOption = let namesUsed = Hashtbl.create 10 in @@ -1511,65 +1518,45 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix | Some (Toption (env, typ)) when expandOption -> typ |> completeTypedValueInner ~env ~full ~prefix ~expandOption:false | Some (Tbool env) -> - let items = - [ - Completion.create ~name:"true" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - Completion.create ~name:"false" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - ] - in - if prefix = "" then items - else - items - |> List.filter (fun (item : Completion.t) -> - Utils.startsWith item.name prefix) + [ + Completion.create ~name:"true" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + Completion.create ~name:"false" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + ] + |> filterItems ~prefix | Some (Tvariant {env; constructors; variantDecl; variantName}) -> - let items = - constructors - |> List.filter_map (fun (constructor : Constructor.t) -> - if - prefix <> "" - && not (Utils.startsWith constructor.cname.txt prefix) - then None - else - Some - (Completion.create - ~name: - (constructor.cname.txt - ^ - if constructor.args |> List.length > 0 then - "(" - ^ (constructor.args - |> List.map (fun _ -> "_") - |> String.concat ", ") - ^ ")" - else "") - ~kind: - (Constructor - ( constructor, - variantDecl |> Shared.declToString variantName )) - ~env)) - in - items + constructors + |> List.map (fun (constructor : Constructor.t) -> + Completion.create + ~name: + (constructor.cname.txt + ^ + if constructor.args |> List.length > 0 then + "(" + ^ (constructor.args + |> List.map (fun _ -> "_") + |> String.concat ", ") + ^ ")" + else "") + ~kind: + (Constructor + ( constructor, + variantDecl |> Shared.declToString variantName )) + ~env) + |> filterItems ~prefix | Some (Toption (env, t)) -> - let items = - [ - Completion.create ~name:"None" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - Completion.create ~name:"Some(_)" - ~kind:(Label (t |> Shared.typeToString)) - ~env; - ] - in - if prefix = "" then items - else - items - |> List.filter (fun (item : Completion.t) -> - Utils.startsWith item.name prefix) + [ + Completion.create ~name:"None" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + Completion.create ~name:"Some(_)" + ~kind:(Label (t |> Shared.typeToString)) + ~env; + ] + |> filterItems ~prefix | _ -> [] in (* Include all values and modules in completion if there's a prefix, not otherwise *) From 22168565aeb3b474f2134ace07162f82cb98d9b5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 27 Dec 2022 22:30:26 +0100 Subject: [PATCH 25/29] fix triggering value completion vs named arg ambiguity --- analysis/src/CompletionFrontEnd.ml | 37 +++++++++++-- .../tests/src/CompletionFunctionArguments.res | 3 +- .../CompletionFunctionArguments.res.txt | 53 ++++++++++++------- 3 files changed, 68 insertions(+), 25 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 56ecbd7c5..198b0e6d5 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -109,6 +109,7 @@ let extractJsxProps ~(compName : Longident.t Location.loc) ~args = 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 | _ -> None @@ -118,7 +119,11 @@ let isExprHole exp = | _ -> false let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor - ~(contextPath : Completable.contextPath) ~posAfterFunExpr = + ~(contextPath : Completable.contextPath) ~posAfterFunExpr ~charBeforeCursor + = + let fnHasCursor = + posAfterFunExpr <= posBeforeCursor && posBeforeCursor < endPos + in let allNames = List.fold_right (fun arg allLabels -> @@ -183,11 +188,34 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor unlabelledCount := !unlabelledCount + 1; loop rest) | [] -> - if posAfterFunExpr <= posBeforeCursor && posBeforeCursor < endPos then - Some (CnamedArg (contextPath, "", allNames)) + if fnHasCursor then + match charBeforeCursor with + | Some '~' -> Some (Completable.CnamedArg (contextPath, "", allNames)) + | _ -> + Some + (Cargument + { + functionContextPath = contextPath; + argumentLabel = + Unlabelled {argumentPosition = !unlabelledCount}; + prefix = ""; + }) else None in - loop args + match args with + (* Special handling for empty fn calls, e.g. `let _ = someFn()` *) + | [ + {label = None; exp = {pexp_desc = Pexp_construct ({txt = Lident "()"}, _)}}; + ] + when fnHasCursor -> + Some + (Completable.Cargument + { + functionContextPath = contextPath; + argumentLabel = Unlabelled {argumentPosition = 0}; + prefix = ""; + }) + | _ -> loop args let rec exprToContextPath (e : Parsetree.expression) = match e.pexp_desc with @@ -638,6 +666,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = findArgCompletables ~contextPath ~args ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor ~posAfterFunExpr:(Loc.end_ funExpr.pexp_loc) + ~charBeforeCursor | None -> None in diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index f70e033cd..2915a4be3 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -61,10 +61,9 @@ let someFnTakingVariant = ( // let _ = someFnTakingVariant(~configOpt2=O) // ^com -// --- UNIMPLEMENTED CASES --- -// The following two cases does not complete to a values because of ambiguity - should it complete for a value, or for a named argument? We don't know whether the function has args or not when deciding, since we're just in the AST at that point. Can potentially be fixed in the future. // let _ = someOtherFn() // ^com + // let _ = someOtherFn(1, 2, ) // ^com diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index cbabbf2c6..f1c697a64 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -158,30 +158,45 @@ Completable: Cargument Value[someFnTakingVariant](~configOpt2=O) "documentation": null }] -Complete src/CompletionFunctionArguments.res 65:23 -posCursor:[65:23] posNoWhite:[65:22] Found expr:[65:11->65:24] -Pexp_apply ...[65:11->65:22] (...[65:23->65:24]) -Completable: CnamedArg(Value[someOtherFn], "", []) -Found type for function (bool, int, bool) => string -[] - -Complete src/CompletionFunctionArguments.res 67:28 -posCursor:[67:28] posNoWhite:[67:27] Found expr:[67:11->67:30] -Pexp_apply ...[67:11->67:22] (...[67:23->67:24], ...[67:26->67:27]) -Completable: CnamedArg(Value[someOtherFn], "", []) -Found type for function (bool, int, bool) => string -[] +Complete src/CompletionFunctionArguments.res 63:23 +posCursor:[63:23] posNoWhite:[63:22] Found expr:[63:11->63:24] +Pexp_apply ...[63:11->63:22] (...[63:23->63:24]) +Completable: Cargument Value[someOtherFn]($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] -Complete src/CompletionFunctionArguments.res 72:25 -posCursor:[72:25] posNoWhite:[72:24] Found expr:[72:11->72:30] -Pexp_apply ...[72:11->72:17] (~isOff72:19->72:24=...[72:27->72:29]) -Completable: CnamedArg(Value[someFn], "", [isOff]) -Found type for function (~isOn: bool, ~isOff: bool=?, unit) => string +Complete src/CompletionFunctionArguments.res 66:28 +posCursor:[66:28] posNoWhite:[66:27] Found expr:[66:11->66:30] +Pexp_apply ...[66:11->66:22] (...[66:23->66:24], ...[66:26->66:27]) +Completable: Cargument Value[someOtherFn]($2) [{ - "label": "isOn", + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", "kind": 4, "tags": [], "detail": "bool", "documentation": null }] +Complete src/CompletionFunctionArguments.res 71:25 +posCursor:[71:25] posNoWhite:[71:24] Found expr:[71:11->71:30] +Pexp_apply ...[71:11->71:17] (~isOff71:19->71:24=...[71:27->71:29]) +Completable: Cargument Value[someFn]($0) +[] + From 0774fc0a08df06e32e5493c00c6141de35eb8f1b Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 27 Dec 2022 23:00:28 +0100 Subject: [PATCH 26/29] handle piped fn calls properly --- analysis/src/CompletionFrontEnd.ml | 33 +++++++++++++++---- .../tests/src/CompletionFunctionArguments.res | 3 ++ .../CompletionFunctionArguments.res.txt | 23 +++++++++++-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 198b0e6d5..22b039103 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -120,7 +120,7 @@ let isExprHole exp = let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor ~(contextPath : Completable.contextPath) ~posAfterFunExpr ~charBeforeCursor - = + ~isPipedExpr = let fnHasCursor = posAfterFunExpr <= posBeforeCursor && posBeforeCursor < endPos in @@ -132,7 +132,7 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor | {label = None} -> allLabels) args [] in - let unlabelledCount = ref 0 in + let unlabelledCount = ref (if isPipedExpr then 1 else 0) in let rec loop args = match args with | {label = Some labelled; exp} :: rest -> @@ -635,15 +635,36 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = when Loc.end_ opLoc = posCursor -> (* Case foo-> *) setPipeResult ~lhs ~id:"" |> ignore - | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> - () - | Pexp_apply (funExpr, args) + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, + [_; (_, {pexp_desc = Pexp_apply (funExpr, args)})] ) when (* Normally named arg completion fires when the cursor is right after the expression. E.g in foo(~<---there But it should not fire in foo(~a)<---there *) not (Loc.end_ expr.pexp_loc = posCursor && charBeforeCursor = Some ')') -> + (* Complete fn argument values and named args when the fn call is piped. E.g. someVar->someFn(). *) + let args = extractExpApplyArgs ~args in + let argCompletable = + match exprToContextPath funExpr with + | Some contextPath -> + findArgCompletables ~contextPath ~args + ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor + ~posAfterFunExpr:(Loc.end_ funExpr.pexp_loc) + ~charBeforeCursor ~isPipedExpr:true + | None -> None + in + + setResultOpt argCompletable + | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> + (* Ignore any other pipe. *) + () + | Pexp_apply (funExpr, args) + when not + (Loc.end_ expr.pexp_loc = posCursor + && charBeforeCursor = Some ')') -> + (* Complete fn argument values and named args when the fn call is _not_ piped. E.g. someFn(). *) let args = extractExpApplyArgs ~args in if debug then Printf.printf "Pexp_apply ...%s (%s)\n" @@ -666,7 +687,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = findArgCompletables ~contextPath ~args ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor ~posAfterFunExpr:(Loc.end_ funExpr.pexp_loc) - ~charBeforeCursor + ~charBeforeCursor ~isPipedExpr:false | None -> None in diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index 2915a4be3..a65258bdd 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -67,6 +67,9 @@ let someFnTakingVariant = ( // let _ = someOtherFn(1, 2, ) // ^com +// let _ = 1->someOtherFn(1, t) +// ^com + // --- BROKEN PARSER CASES --- // This below demonstrates an issue when what you're completing is the _last_ labelled argument, and there's a unit application after it. The parser wrongly merges the unit argument as the expression of the labelled argument assignment, where is should really let the trailing unit argument be, and set a %rescript.exprhole as the expression of the assignment, just like it normally does. // let _ = someFn(~isOff=, ()) diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index f1c697a64..ea76de295 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -194,9 +194,26 @@ Completable: Cargument Value[someOtherFn]($2) "documentation": null }] -Complete src/CompletionFunctionArguments.res 71:25 -posCursor:[71:25] posNoWhite:[71:24] Found expr:[71:11->71:30] -Pexp_apply ...[71:11->71:17] (~isOff71:19->71:24=...[71:27->71:29]) +Complete src/CompletionFunctionArguments.res 69:30 +posCursor:[69:30] posNoWhite:[69:29] Found expr:[69:11->69:31] +Completable: Cargument Value[someOtherFn]($2=t) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "tLocalVar", + "kind": 12, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionFunctionArguments.res 74:25 +posCursor:[74:25] posNoWhite:[74:24] Found expr:[74:11->74:30] +Pexp_apply ...[74:11->74:17] (~isOff74:19->74:24=...[74:27->74:29]) Completable: Cargument Value[someFn]($0) [] From 619d7ecec1bfebabb1029ba2e5804a98f16fb330 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 28 Dec 2022 21:23:49 +0100 Subject: [PATCH 27/29] move broken parser cases to its own file --- analysis/tests/src/BrokenParserCases.res | 5 +++++ analysis/tests/src/CompletionFunctionArguments.res | 5 ----- analysis/tests/src/expected/BrokenParserCases.res.txt | 6 ++++++ .../tests/src/expected/CompletionFunctionArguments.res.txt | 6 ------ 4 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 analysis/tests/src/BrokenParserCases.res create mode 100644 analysis/tests/src/expected/BrokenParserCases.res.txt diff --git a/analysis/tests/src/BrokenParserCases.res b/analysis/tests/src/BrokenParserCases.res new file mode 100644 index 000000000..877b7b0e4 --- /dev/null +++ b/analysis/tests/src/BrokenParserCases.res @@ -0,0 +1,5 @@ +// --- BROKEN PARSER CASES --- +// This below demonstrates an issue when what you're completing is the _last_ labelled argument, and there's a unit application after it. The parser wrongly merges the unit argument as the expression of the labelled argument assignment, where is should really let the trailing unit argument be, and set a %rescript.exprhole as the expression of the assignment, just like it normally does. +// let _ = someFn(~isOff=, ()) +// ^com + diff --git a/analysis/tests/src/CompletionFunctionArguments.res b/analysis/tests/src/CompletionFunctionArguments.res index a65258bdd..f002a444d 100644 --- a/analysis/tests/src/CompletionFunctionArguments.res +++ b/analysis/tests/src/CompletionFunctionArguments.res @@ -69,8 +69,3 @@ let someFnTakingVariant = ( // let _ = 1->someOtherFn(1, t) // ^com - -// --- BROKEN PARSER CASES --- -// This below demonstrates an issue when what you're completing is the _last_ labelled argument, and there's a unit application after it. The parser wrongly merges the unit argument as the expression of the labelled argument assignment, where is should really let the trailing unit argument be, and set a %rescript.exprhole as the expression of the assignment, just like it normally does. -// let _ = someFn(~isOff=, ()) -// ^com diff --git a/analysis/tests/src/expected/BrokenParserCases.res.txt b/analysis/tests/src/expected/BrokenParserCases.res.txt new file mode 100644 index 000000000..7e4f89500 --- /dev/null +++ b/analysis/tests/src/expected/BrokenParserCases.res.txt @@ -0,0 +1,6 @@ +Complete src/BrokenParserCases.res 2:25 +posCursor:[2:25] posNoWhite:[2:24] Found expr:[2:11->2:30] +Pexp_apply ...[2:11->2:17] (~isOff2:19->2:24=...[2:27->2:29]) +Completable: Cargument Value[someFn]($0) +[] + diff --git a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt index ea76de295..07b8b0604 100644 --- a/analysis/tests/src/expected/CompletionFunctionArguments.res.txt +++ b/analysis/tests/src/expected/CompletionFunctionArguments.res.txt @@ -211,9 +211,3 @@ Completable: Cargument Value[someOtherFn]($2=t) "documentation": null }] -Complete src/CompletionFunctionArguments.res 74:25 -posCursor:[74:25] posNoWhite:[74:24] Found expr:[74:11->74:30] -Pexp_apply ...[74:11->74:17] (~isOff74:19->74:24=...[74:27->74:29]) -Completable: Cargument Value[someFn]($0) -[] - From 5995f457d02061eb5b03e9c0843684a5dc72c55f Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 28 Dec 2022 21:36:16 +0100 Subject: [PATCH 28/29] only expand options on optional arguments, not all labelled arguments --- analysis/src/CompletionBackEnd.ml | 18 ++++++++++++++---- analysis/src/SharedTypes.ml | 4 +++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index d50a3888f..22b0e25a0 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1461,9 +1461,11 @@ let getArgs ~env (t : Types.type_expr) ~full = match t.desc with | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> getArgsLoop ~full ~env ~currentArgumentPosition t1 - | Tarrow ((Labelled l | Optional l), tArg, tRet, _) -> + | Tarrow (Labelled l, tArg, tRet, _) -> (SharedTypes.Completable.Labelled l, tArg) :: getArgsLoop ~full ~env ~currentArgumentPosition tRet + | Tarrow (Optional l, tArg, tRet, _) -> + (Optional l, tArg) :: getArgsLoop ~full ~env ~currentArgumentPosition tRet | Tarrow (Nolabel, tArg, tRet, _) -> (Unlabelled {argumentPosition = currentArgumentPosition}, tArg) :: getArgsLoop ~full ~env @@ -1945,15 +1947,22 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | None -> [] in let targetLabel = - labels |> List.find_opt (fun (label, _) -> label = argumentLabel) + labels + |> List.find_opt (fun (label, _) -> + match argumentLabel with + | Unlabelled _ -> label = argumentLabel + | Labelled name | Optional name -> ( + match label with + | (Labelled n | Optional n) when name = n -> true + | _ -> false)) in match targetLabel with | None -> [] - | Some (Labelled _, typ) -> + | Some (Optional _, typ) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~expandOption:true - | Some (Unlabelled _, typ) -> + | Some ((Unlabelled _ | Labelled _), typ) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~expandOption:false) @@ -1974,6 +1983,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i |> List.filter_map (fun arg -> match arg with | SharedTypes.Completable.Labelled name, a -> Some (name, a) + | Optional name, a -> Some (name, a) | _ -> None) | None -> [] in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 205712f02..c90a93c4c 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -505,6 +505,7 @@ module Completable = struct type argumentLabel = | Unlabelled of {argumentPosition: int} | Labelled of string + | Optional of string type contextPath = | CPString @@ -592,7 +593,8 @@ module Completable = struct ^ "(" ^ (match argumentLabel with | Unlabelled {argumentPosition} -> "$" ^ string_of_int argumentPosition - | Labelled name -> "~" ^ name) + | Labelled name -> "~" ^ name + | Optional name -> "~" ^ name ^ "=?") ^ (if prefix <> "" then "=" ^ prefix else "") ^ ")" end From 0f626115966d93dc8f50884356b48b30e509995c Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 29 Dec 2022 11:30:17 +0100 Subject: [PATCH 29/29] add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7c3e09ee..2ba76946c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ ## v1.10.0 +#### :rocket: New Feature + +- 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 + #### :nail_care: Polish - Remove spacing between type definition in clients that do not support markdown links. https://github.com/rescript-lang/rescript-vscode/pull/619