diff --git a/CHANGELOG.md b/CHANGELOG.md index f8991cdac..c55b8bc1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Latest parser for newest syntax features. https://github.com/rescript-lang/rescript-vscode/pull/917 - Handle completion for DOM/element attributes and attribute values properly when using a generic JSX transform. https://github.com/rescript-lang/rescript-vscode/pull/919 - Highlight tagged template literal functions as functions. https://github.com/rescript-lang/rescript-vscode/pull/920 +- Complete for `type t` values when encountering a `type t` in relevant scenarios. https://github.com/rescript-lang/rescript-vscode/pull/924 ## 1.38.0 diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index d5d1a1462..891b41d69 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1257,6 +1257,12 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens match t with | TtypeT {env; path} -> if Debug.verbose () then print_endline "[complete_typed_value]--> TtypeT"; + (* Find all values in the module with type t *) + let valueWithTypeT t = + match t.Types.desc with + | Tconstr (Pident {name = "t"}, [], _) -> true + | _ -> false + in (* Find all functions in the module that returns type t *) let rec fnReturnsTypeT t = match t.Types.desc with @@ -1277,41 +1283,49 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens | _ -> false) | _ -> false in - let functionsReturningTypeT = - Hashtbl.create (Hashtbl.length env.exported.values_) + let getCompletionName exportedValueName = + let fnNname = + TypeUtils.getPathRelativeToEnv ~debug:false + ~env:(QueryEnv.fromFile full.file) + ~envFromItem:env (Utils.expandPath path) + in + match fnNname with + | None -> None + | Some base -> + let base = + TypeUtils.removeOpensFromCompletionPath ~rawOpens + ~package:full.package base + in + Some ((base |> String.concat ".") ^ "." ^ exportedValueName) + in + let getExportedValueCompletion name (declared : Types.type_expr Declared.t) + = + let typeExpr = declared.item in + if valueWithTypeT typeExpr then + getCompletionName name + |> Option.map (fun name -> + createWithSnippet ~name ~insertText:name ~kind:(Value typeExpr) + ~env ()) + else if fnReturnsTypeT typeExpr then + getCompletionName name + |> Option.map (fun name -> + createWithSnippet + ~name:(Printf.sprintf "%s()" name) + ~insertText:(name ^ "($0)") ~kind:(Value typeExpr) ~env ()) + else None in - env.exported.values_ - |> Hashtbl.iter (fun name stamp -> - match Stamps.findValue env.file.stamps stamp with - | None -> () - | Some {item} -> ( - if fnReturnsTypeT item then - let fnNname = - TypeUtils.getPathRelativeToEnv ~debug:false - ~env:(QueryEnv.fromFile full.file) - ~envFromItem:env (Utils.expandPath path) - in - - match fnNname with - | None -> () - | Some base -> - let base = - TypeUtils.removeOpensFromCompletionPath ~rawOpens - ~package:full.package base - in - Hashtbl.add functionsReturningTypeT - ((base |> String.concat ".") ^ "." ^ name) - item)); - let completionItems = Hashtbl.fold - (fun fnName typeExpr all -> - createWithSnippet - ~name:(Printf.sprintf "%s()" fnName) - ~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env () - :: all) - functionsReturningTypeT [] + (fun name stamp all -> + match Stamps.findValue env.file.stamps stamp with + | None -> all + | Some declaredTypeExpr -> ( + match getExportedValueCompletion name declaredTypeExpr with + | None -> all + | Some completion -> completion :: all)) + env.exported.values_ [] in + (* Special casing for things where we want extra things in the completions *) let completionItems = match path with diff --git a/analysis/tests/src/CompletionExpressions.res b/analysis/tests/src/CompletionExpressions.res index 3572caede..c4f14d6ee 100644 --- a/analysis/tests/src/CompletionExpressions.res +++ b/analysis/tests/src/CompletionExpressions.res @@ -321,3 +321,39 @@ let mkStuff = (r: Js.Re.t) => { // mkStuff() // ^com + +module Money: { + type t + + let zero: t + + let nonTType: string + + let make: unit => t + + let fromInt: int => t + + let plus: (t, t) => t +} = { + type t = string + + let zero: t = "0" + + let nonTType = "0" + + let make = (): t => zero + + let fromInt = (int): t => int->Js.Int.toString + + let plus = (m1, _) => m1 +} + +let tArgCompletionTestFn = (tVal: Money.t) => () + +// tArgCompletionTestFn() +// ^com + +let labeledTArgCompletionTestFn = (~tVal: Money.t) => () + +// labeledTArgCompletionTestFn(~tVal=) +// ^com diff --git a/analysis/tests/src/expected/CompletionExpressions.res.txt b/analysis/tests/src/expected/CompletionExpressions.res.txt index d84e5bfe5..a95c7ed00 100644 --- a/analysis/tests/src/expected/CompletionExpressions.res.txt +++ b/analysis/tests/src/expected/CompletionExpressions.res.txt @@ -1314,6 +1314,14 @@ Path mkStuff "documentation": null, "insertText": "%re(\"/$0/g\")", "insertTextFormat": 2 + }, { + "label": "Js.Re.fromString()", + "kind": 12, + "tags": [], + "detail": "string => t", + "documentation": null, + "insertText": "Js.Re.fromString($0)", + "insertTextFormat": 2 }, { "label": "Js.Re.fromStringWithFlags()", "kind": 12, @@ -1322,13 +1330,77 @@ Path mkStuff "documentation": null, "insertText": "Js.Re.fromStringWithFlags($0)", "insertTextFormat": 2 + }] + +Complete src/CompletionExpressions.res 352:24 +posCursor:[352:24] posNoWhite:[352:23] Found expr:[352:3->352:25] +Pexp_apply ...[352:3->352:23] (...[352:24->352:25]) +Completable: Cexpression CArgument Value[tArgCompletionTestFn]($0) +Raw opens: 1 CompletionSupport.place holder +Package opens Pervasives.JsxModules.place holder +Resolved opens 2 pervasives CompletionSupport.res +ContextPath CArgument Value[tArgCompletionTestFn]($0) +ContextPath Value[tArgCompletionTestFn] +Path tArgCompletionTestFn +[{ + "label": "Money.fromInt()", + "kind": 12, + "tags": [], + "detail": "int => t", + "documentation": null, + "insertText": "Money.fromInt($0)", + "insertTextFormat": 2 }, { - "label": "Js.Re.fromString()", + "label": "Money.make()", "kind": 12, "tags": [], - "detail": "string => t", + "detail": "unit => t", "documentation": null, - "insertText": "Js.Re.fromString($0)", + "insertText": "Money.make($0)", + "insertTextFormat": 2 + }, { + "label": "Money.zero", + "kind": 12, + "tags": [], + "detail": "t", + "documentation": null, + "insertText": "Money.zero", + "insertTextFormat": 2 + }] + +Complete src/CompletionExpressions.res 357:37 +posCursor:[357:37] posNoWhite:[357:36] Found expr:[357:3->357:38] +Pexp_apply ...[357:3->357:30] (~tVal357:32->357:36=...__ghost__[0:-1->0:-1]) +Completable: Cexpression CArgument Value[labeledTArgCompletionTestFn](~tVal) +Raw opens: 1 CompletionSupport.place holder +Package opens Pervasives.JsxModules.place holder +Resolved opens 2 pervasives CompletionSupport.res +ContextPath CArgument Value[labeledTArgCompletionTestFn](~tVal) +ContextPath Value[labeledTArgCompletionTestFn] +Path labeledTArgCompletionTestFn +[{ + "label": "Money.fromInt()", + "kind": 12, + "tags": [], + "detail": "int => t", + "documentation": null, + "insertText": "Money.fromInt($0)", + "insertTextFormat": 2 + }, { + "label": "Money.make()", + "kind": 12, + "tags": [], + "detail": "unit => t", + "documentation": null, + "insertText": "Money.make($0)", + "insertTextFormat": 2 + }, { + "label": "Money.zero", + "kind": 12, + "tags": [], + "detail": "t", + "documentation": null, + "insertText": "Money.zero", "insertTextFormat": 2 }]