Skip to content

Commit 21a4839

Browse files
committed
refactor so that signature help tries to use typed information first for function calls. This will give better results since typed info will have type variables instantiated
1 parent ab1bdd2 commit 21a4839

File tree

3 files changed

+135
-90
lines changed

3 files changed

+135
-90
lines changed

Diff for: analysis/src/SignatureHelp.ml

+120-82
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,120 @@
11
open SharedTypes
22
type cursorAtArg = Unlabelled of int | Labelled of string
33

4-
let findFunctionType ~currentFile ~debug ~path ~pos =
5-
let completables =
6-
let textOpt = Files.readFile currentFile in
7-
match textOpt with
8-
| None | Some "" -> None
9-
| Some text -> (
10-
(* Leverage the completion functionality to pull out the type of the identifier doing the function application.
11-
This lets us leverage all of the smart work done in completions to find the correct type in many cases even
12-
for files not saved yet. *)
13-
match
14-
CompletionFrontEnd.completionWithParser ~debug ~path ~posCursor:pos
15-
~currentFile ~text
16-
with
17-
| None -> None
18-
| Some (completable, scope) -> (
19-
match Cmt.loadFullCmtFromPath ~path with
20-
| None -> None
21-
| Some full ->
22-
let {file; package} = full in
23-
let env = QueryEnv.fromFile file in
4+
let shouldPrintMainTypeStr typ ~env ~package =
5+
match typ |> Shared.digConstructor with
6+
| Some path -> (
7+
match References.digConstructor ~env ~package path with
8+
| Some (_, {item = {kind = Record _}}) -> false
9+
| _ -> true)
10+
| _ -> false
11+
12+
(* Produces the doc string shown below the signature help for each parameter. *)
13+
let docsForLabel typeExpr ~file ~package ~supportsMarkdownLinks =
14+
let env = QueryEnv.fromFile file in
15+
let types = Hover.findRelevantTypesFromType ~file ~package typeExpr in
16+
let typeString =
17+
if shouldPrintMainTypeStr typeExpr ~env ~package then
18+
Markdown.codeBlock (typeExpr |> Shared.typeToString)
19+
else ""
20+
in
21+
let typeNames = types |> List.map (fun {Hover.name} -> name) in
22+
let typeDefinitions =
23+
types
24+
|> List.map (fun {Hover.decl; name; env; loc; path} ->
25+
let linkToTypeDefinitionStr =
26+
if supportsMarkdownLinks then
27+
Markdown.goToDefinitionText ~env ~pos:loc.Warnings.loc_start
28+
else ""
29+
in
30+
(* Since printing the whole name via its path can get quite long, and
31+
we're short on space for the signature help, we'll only print the
32+
fully "qualified" type name if we must (ie if several types we're
33+
displaying have the same name). *)
34+
let multipleTypesHaveThisName =
35+
typeNames
36+
|> List.filter (fun typeName -> typeName = name)
37+
|> List.length > 1
38+
in
39+
let typeName =
40+
if multipleTypesHaveThisName then
41+
path |> SharedTypes.pathIdentToString
42+
else name
43+
in
44+
Markdown.codeBlock
45+
(Shared.declToString ~printNameAsIs:true typeName decl)
46+
^ linkToTypeDefinitionStr)
47+
in
48+
typeString :: typeDefinitions |> String.concat "\n"
49+
50+
let findFunctionType ~currentFile ~debug ~path ~pos ~supportsMarkdownLinks =
51+
(* Start by looking at the typed info at the loc of the fn *)
52+
match Cmt.loadFullCmtFromPath ~path with
53+
| None -> None
54+
| Some full -> (
55+
let {file; package} = full in
56+
let env = QueryEnv.fromFile file in
57+
let fnFromLocItem =
58+
match References.getLocItem ~full ~pos ~debug:false with
59+
| Some {locType = Typed (_, typeExpr, _)} -> (
60+
if Debug.verbose () then
61+
Printf.printf "[sig_help_fn] Found loc item: %s.\n"
62+
(Shared.typeToString typeExpr);
63+
match
64+
TypeUtils.extractFunctionType2 ~env ~package:full.package typeExpr
65+
with
66+
| args, _tRet, _ when args <> [] ->
2467
Some
25-
( completable
26-
|> CompletionBackEnd.processCompletable ~debug ~full ~pos ~scope
27-
~env ~forHover:true,
28-
env,
68+
( args,
69+
[docsForLabel ~file ~package ~supportsMarkdownLinks typeExpr],
70+
typeExpr,
2971
package,
30-
file )))
31-
in
32-
match completables with
33-
| Some ({kind = Value type_expr; docstring} :: _, env, package, file) ->
34-
let args, _ = TypeUtils.extractFunctionType type_expr ~env ~package in
35-
Some (args, docstring, type_expr, package, env, file)
36-
| _ -> None
72+
env,
73+
file )
74+
| _ -> None)
75+
| None ->
76+
if Debug.verbose () then
77+
Printf.printf "[sig_help_fn] Found no loc item.\n";
78+
None
79+
| Some _ ->
80+
if Debug.verbose () then
81+
Printf.printf
82+
"[sig_help_fn] Found loc item, but not what was expected.\n";
83+
None
84+
in
85+
match fnFromLocItem with
86+
| Some fnFromLocItem -> Some fnFromLocItem
87+
| None -> (
88+
(* If nothing was found there, try using the unsaved completion engine *)
89+
let completables =
90+
let textOpt = Files.readFile currentFile in
91+
match textOpt with
92+
| None | Some "" -> None
93+
| Some text -> (
94+
(* Leverage the completion functionality to pull out the type of the identifier doing the function application.
95+
This lets us leverage all of the smart work done in completions to find the correct type in many cases even
96+
for files not saved yet. *)
97+
match
98+
CompletionFrontEnd.completionWithParser ~debug ~path ~posCursor:pos
99+
~currentFile ~text
100+
with
101+
| None -> None
102+
| Some (completable, scope) ->
103+
Some
104+
( completable
105+
|> CompletionBackEnd.processCompletable ~debug ~full ~pos ~scope
106+
~env ~forHover:true,
107+
env,
108+
package,
109+
file ))
110+
in
111+
match completables with
112+
| Some ({kind = Value type_expr; docstring} :: _, env, package, file) ->
113+
let args, _, _ =
114+
TypeUtils.extractFunctionType2 type_expr ~env ~package
115+
in
116+
Some (args, docstring, type_expr, package, env, file)
117+
| _ -> None))
37118

38119
(* Extracts all parameters from a parsed function signature *)
39120
let extractParameters ~signature ~typeStrForParser ~labelPrefixLen =
@@ -126,52 +207,6 @@ let findActiveParameter ~argAtCursor ~args =
126207
index := !index + 1;
127208
None)
128209

129-
let shouldPrintMainTypeStr typ ~env ~package =
130-
match typ |> Shared.digConstructor with
131-
| Some path -> (
132-
match References.digConstructor ~env ~package path with
133-
| Some (_, {item = {kind = Record _}}) -> false
134-
| _ -> true)
135-
| _ -> false
136-
137-
(* Produces the doc string shown below the signature help for each parameter. *)
138-
let docsForLabel typeExpr ~file ~package ~supportsMarkdownLinks =
139-
let env = QueryEnv.fromFile file in
140-
let types = Hover.findRelevantTypesFromType ~file ~package typeExpr in
141-
let typeString =
142-
if shouldPrintMainTypeStr typeExpr ~env ~package then
143-
Markdown.codeBlock (typeExpr |> Shared.typeToString)
144-
else ""
145-
in
146-
let typeNames = types |> List.map (fun {Hover.name} -> name) in
147-
let typeDefinitions =
148-
types
149-
|> List.map (fun {Hover.decl; name; env; loc; path} ->
150-
let linkToTypeDefinitionStr =
151-
if supportsMarkdownLinks then
152-
Markdown.goToDefinitionText ~env ~pos:loc.Warnings.loc_start
153-
else ""
154-
in
155-
(* Since printing the whole name via its path can get quite long, and
156-
we're short on space for the signature help, we'll only print the
157-
fully "qualified" type name if we must (ie if several types we're
158-
displaying have the same name). *)
159-
let multipleTypesHaveThisName =
160-
typeNames
161-
|> List.filter (fun typeName -> typeName = name)
162-
|> List.length > 1
163-
in
164-
let typeName =
165-
if multipleTypesHaveThisName then
166-
path |> SharedTypes.pathIdentToString
167-
else name
168-
in
169-
Markdown.codeBlock
170-
(Shared.declToString ~printNameAsIs:true typeName decl)
171-
^ linkToTypeDefinitionStr)
172-
in
173-
typeString :: typeDefinitions |> String.concat "\n"
174-
175210
let findConstructorArgs ~full ~env ~constructorName loc =
176211
match
177212
References.getLocItem ~debug:false ~full
@@ -232,10 +267,11 @@ let signatureHelp ~path ~pos ~currentFile ~debug ~allowForConstructorPayloads =
232267
of %s\n"
233268
(printThing thing) (printThing currentThing)
234269
| Some (_, currentThing) ->
235-
Printf.printf
236-
"[sig_help_result] Doing nothing because loc of %s < then \
237-
existing of %s\n"
238-
(printThing thing) (printThing currentThing))
270+
if Debug.verbose () then
271+
Printf.printf
272+
"[sig_help_result] Doing nothing because loc of %s < then \
273+
existing of %s\n"
274+
(printThing thing) (printThing currentThing))
239275
in
240276
let searchForArgWithCursor ~isPipeExpr ~args =
241277
let extractedArgs = extractExpApplyArgs ~args in
@@ -358,7 +394,9 @@ let signatureHelp ~path ~pos ~currentFile ~debug ~allowForConstructorPayloads =
358394
| Some (_, `FunctionCall (argAtCursor, exp, _extractedArgs)) -> (
359395
(* Not looking for the cursor position after this, but rather the target function expression's loc. *)
360396
let pos = exp.pexp_loc |> Loc.end_ in
361-
match findFunctionType ~currentFile ~debug ~path ~pos with
397+
match
398+
findFunctionType ~currentFile ~debug ~path ~pos ~supportsMarkdownLinks
399+
with
362400
| Some (args, docstring, type_expr, package, _env, file) ->
363401
if debug then
364402
Printf.printf "argAtCursor: %s\n"

Diff for: analysis/tests/src/SignatureHelp.res

+2-4
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,5 @@ let _deepestTakesPrecedence = [12]->Js.Array2.map(v =>
110110
}
111111
)
112112

113-
// ^dv+
114-
// let _usesCorrectTypeInfo = [12]->Js.Array2.map(v => v)
115-
// she
116-
// ^dv-
113+
let _usesCorrectTypeInfo = [12]->Belt.Array.map(v => v)
114+
// ^she

Diff for: analysis/tests/src/expected/SignatureHelp.res.txt

+13-4
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ extracted params:
247247
}
248248

249249
Signature help src/SignatureHelp.res 53:31
250-
[sig_help_result] Doing nothing because loc of FunctionCall < then existing of FunctionCall
251250
posCursor:[53:29] posNoWhite:[53:28] Found expr:[53:11->53:31]
252251
posCursor:[53:29] posNoWhite:[53:28] Found expr:[53:20->53:31]
253252
Pexp_apply ...[53:20->53:29] (...[53:30->53:31])
@@ -454,8 +453,6 @@ Signature help src/SignatureHelp.res 100:24
454453
}
455454

456455
Signature help src/SignatureHelp.res 105:9
457-
[sig_help_result] Doing nothing because loc of FunctionCall < then existing of FunctionCall
458-
[sig_help_result] Doing nothing because loc of Constructor < then existing of FunctionCall
459456
{
460457
"signatures": [{
461458
"label": "One({miss?: bool, hit?: bool, stuff?: string})",
@@ -466,5 +463,17 @@ Signature help src/SignatureHelp.res 105:9
466463
"activeParameter": -1
467464
}
468465

469-
466+
Signature help src/SignatureHelp.res 112:53
467+
argAtCursor: unlabelled<1>
468+
extracted params:
469+
[Belt.Array.t<int>, int => int]
470+
{
471+
"signatures": [{
472+
"label": "(Belt.Array.t<int>, int => int) => array<int>",
473+
"parameters": [{"label": [1, 18], "documentation": {"kind": "markdown", "value": "```rescript\nBelt.Array.t<int>\n```\n```rescript\ntype t<'a> = array<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_Array.resi%22%2C27%2C0%5D)"}}, {"label": [20, 30], "documentation": {"kind": "markdown", "value": "```rescript\nBelt.Array.t<int>\n```\n```rescript\ntype t<'a> = array<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_Array.resi%22%2C27%2C0%5D)"}}],
474+
"documentation": {"kind": "markdown", "value": "\n```rescript\ntype t<'a> = array<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_Array.resi%22%2C27%2C0%5D)"}
475+
}],
476+
"activeSignature": 0,
477+
"activeParameter": 1
478+
}
470479

0 commit comments

Comments
 (0)