Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add completion for type t values #924

Merged
merged 5 commits into from
Feb 27, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

76 changes: 45 additions & 31 deletions analysis/src/CompletionBackEnd.ml
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions analysis/tests/src/CompletionExpressions.res
Original file line number Diff line number Diff line change
@@ -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
78 changes: 75 additions & 3 deletions analysis/tests/src/expected/CompletionExpressions.res.txt
Original file line number Diff line number Diff line change
@@ -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
}]