Skip to content

Commit 84fa4dd

Browse files
committed
feat(inlayHint): basic support
1 parent 9bfa95e commit 84fa4dd

14 files changed

+1003
-685
lines changed

analysis/src/Cli.ml

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ let main () =
8484
Commands.hover ~path
8585
~pos:(int_of_string line, int_of_string col)
8686
~currentFile ~debug:false
87+
| [_; "inlayHint"; path; line_start; line_end] ->
88+
Commands.inlayhint ~path
89+
~pos:(int_of_string line_start, int_of_string line_end)
90+
~debug:false
8791
| [_; "codeAction"; path; line; col; currentFile] ->
8892
Commands.codeAction ~path
8993
~pos:(int_of_string line, int_of_string col)

analysis/src/Commands.ml

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ let completion ~debug ~path ~pos ~currentFile =
2828
|> List.map Protocol.stringifyCompletionItem
2929
|> Protocol.array)
3030

31+
let inlayhint ~path ~pos ~debug =
32+
let result = match Hint.inlay ~path ~debug with
33+
| [] -> Protocol.null
34+
| hints -> "[\n" ^ String.concat ",\n" hints ^ "\n]" in
35+
print_endline result
36+
3137
let hover ~path ~pos ~currentFile ~debug =
3238
let result =
3339
match Cmt.fullFromPath ~path with

analysis/src/Hint.ml

+290
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
open SharedTypes
2+
3+
type kind =
4+
| Module
5+
| Property
6+
| Constructor
7+
| Function
8+
| Variable
9+
| Constant
10+
| String
11+
| Number
12+
| EnumMember
13+
| TypeParameter
14+
15+
let hintPrefix label = Printf.sprintf ": %s" label
16+
let hintkindNumber = function TypeParameter | EnumMember -> 2 | _ -> 1
17+
18+
let locItemToTypeHint ~full:{file; package} locItem =
19+
match locItem.locType with
20+
(* | TypeDefinition (name, decl, _stamp) -> *)
21+
(* let typeDef = Shared.declToString name decl in *)
22+
(* Some (hintPrefix typeDef) *)
23+
(* | LModule (Definition (stamp, _tip)) | LModule (LocalReference (stamp, _tip)) *)
24+
(* -> ( *)
25+
(* match Stamps.findModule file.stamps stamp with *)
26+
(* | None -> None *)
27+
(* | Some md -> ( *)
28+
(* match References.resolveModuleReference ~file ~package md with *)
29+
(* | None -> None *)
30+
(* | Some (file, declared) -> *)
31+
(* let name, docstring = *)
32+
(* match declared with *)
33+
(* | Some d -> (d.name.txt, d.docstring) *)
34+
(* | None -> (file.moduleName, file.structure.docstring) *)
35+
(* in *)
36+
(* Hover.showModule ~docstring ~name ~file declared)) *)
37+
(* | LModule (GlobalReference (moduleName, path, tip)) -> ( *)
38+
(* match ProcessCmt.fileForModule ~package moduleName with *)
39+
(* | None -> None *)
40+
(* | Some file -> ( *)
41+
(* let env = QueryEnv.fromFile file in *)
42+
(* match ResolvePath.resolvePath ~env ~path ~package with *)
43+
(* | None -> None *)
44+
(* | Some (env, name) -> ( *)
45+
(* match References.exportedForTip ~env name tip with *)
46+
(* | None -> None *)
47+
(* | Some stamp -> ( *)
48+
(* match Stamps.findModule file.stamps stamp with *)
49+
(* | None -> None *)
50+
(* | Some md -> ( *)
51+
(* match References.resolveModuleReference ~file ~package md with *)
52+
(* | None -> None *)
53+
(* | Some (file, declared) -> *)
54+
(* let name, docstring = *)
55+
(* match declared with *)
56+
(* | Some d -> (d.name.txt, d.docstring) *)
57+
(* | None -> (file.moduleName, file.structure.docstring) *)
58+
(* in *)
59+
(* Hover.showModule ~docstring ~name ~file declared))))) *)
60+
| LModule NotFound -> None
61+
(* | TopLevelModule name -> ( *)
62+
(* match ProcessCmt.fileForModule ~package name with *)
63+
(* | None -> None *)
64+
(* | Some file -> *)
65+
(* Hover.showModule ~docstring:file.structure.docstring ~name:file.moduleName *)
66+
(* ~file None) *)
67+
| Typed (_, _, Definition (_, (Field _ | Constructor _))) -> None
68+
| Constant t ->
69+
Some
70+
(hintPrefix
71+
(match t with
72+
| Const_int _ -> "int"
73+
| Const_char _ -> "char"
74+
| Const_string _ -> "string"
75+
| Const_float _ -> "float"
76+
| Const_int32 _ -> "int32"
77+
| Const_int64 _ -> "int64"
78+
| Const_nativeint _ -> "int"))
79+
| Typed (_, t, locKind) ->
80+
(* let fromType ~docstring typ = *)
81+
(* let typeString = Hover.codeBlock (typ |> Shared.typeToString) in *)
82+
(* let extraTypeInfo = *)
83+
(* let env = QueryEnv.fromFile file in *)
84+
(* match typ |> Shared.digConstructor with *)
85+
(* | None -> None *)
86+
(* | Some path -> ( *)
87+
(* match References.digConstructor ~env ~package path with *)
88+
(* | None -> None *)
89+
(* | Some (_env, {docstring; name = {txt}; item = {decl}}) -> *)
90+
(* if Utils.isUncurriedInternal path then None *)
91+
(* else Some (decl |> Shared.declToString txt, docstring)) *)
92+
(* in *)
93+
(* let typeString, docstring = *)
94+
(* match extraTypeInfo with *)
95+
(* | None -> (typeString, docstring) *)
96+
(* | Some (extra, extraDocstring) -> *)
97+
(* (typeString ^ "\n\n" ^ Hover.codeBlock extra, extraDocstring) *)
98+
(* in *)
99+
(* (typeString, docstring) *)
100+
(* in *)
101+
let parts =
102+
match References.definedForLoc ~file ~package locKind with
103+
| None ->
104+
let typeString = Shared.typeToString t in
105+
typeString
106+
| Some (docstring, res) -> (
107+
match res with
108+
| `Declared ->
109+
let typeString = Shared.typeToString t in
110+
typeString
111+
| `Constructor {cname = {txt}; args} ->
112+
let typeString = Shared.typeToString t in
113+
typeString
114+
(* let typeString, docstring = t |> fromType ~docstring in *)
115+
(* let argsString = *)
116+
(* match args with *)
117+
(* | [] -> "" *)
118+
(* | _ -> *)
119+
(* args *)
120+
(* |> List.map (fun (t, _) -> (Shared.typeToString t) ^ "hre") *)
121+
(* |> String.concat ", " |> Printf.sprintf "(%s)" *)
122+
(* in *)
123+
(* typeString *)
124+
| `Field ->
125+
let typeString = Shared.typeToString t in
126+
typeString
127+
(* let typeString, docstring = t |> fromType ~docstring in *)
128+
(* typeString) *)
129+
)
130+
in
131+
Some parts
132+
133+
let inlay ~path ~debug =
134+
let symbols = ref [] in
135+
let rec exprKind (exp : Parsetree.expression) =
136+
match exp.pexp_desc with
137+
| Pexp_fun _ -> Function
138+
| Pexp_function _ -> Function
139+
| Pexp_constraint (e, _) -> exprKind e
140+
| Pexp_constant (Pconst_string _) -> String
141+
| Pexp_constant (Pconst_float _ | Pconst_integer _) -> Number
142+
| Pexp_constant _ -> Constant
143+
| _ -> Variable
144+
in
145+
(* let processTypeKind (tk : Parsetree.type_kind) = *)
146+
(* match tk with *)
147+
(* | Ptype_variant constrDecls -> *)
148+
(* constrDecls *)
149+
(* |> List.iter (fun (cd : Parsetree.constructor_declaration) -> *)
150+
(* symbols := (cd.pcd_name.txt, cd.pcd_loc, EnumMember) :: !symbols) *)
151+
(* | Ptype_record labelDecls -> *)
152+
(* labelDecls *)
153+
(* |> List.iter (fun (ld : Parsetree.label_declaration) -> *)
154+
(* symbols := (ld.pld_name.txt, ld.pld_loc, Property) :: !symbols) *)
155+
(* | _ -> () *)
156+
(* in *)
157+
(* let processTypeDeclaration (td : Parsetree.type_declaration) = *)
158+
(* symbols := (td.ptype_name.txt, td.ptype_loc, TypeParameter) :: !symbols; *)
159+
(* processTypeKind td.ptype_kind *)
160+
(* in *)
161+
let processValueDescription (vd : Parsetree.value_description) =
162+
symbols := (vd.pval_name.txt, vd.pval_loc, Variable) :: !symbols
163+
in
164+
(* let processModuleBinding (mb : Parsetree.module_binding) = *)
165+
(* symbols := (mb.pmb_name.txt, mb.pmb_loc, Module) :: !symbols *)
166+
(* in *)
167+
(* let processModuleDeclaration (md : Parsetree.module_declaration) = *)
168+
(* symbols := (md.pmd_name.txt, md.pmd_loc, Module) :: !symbols *)
169+
(* in *)
170+
(* let processExtensionConstructor (et : Parsetree.extension_constructor) = *)
171+
(* symbols := (et.pext_name.txt, et.pext_loc, Constructor) :: !symbols *)
172+
(* in *)
173+
let value_binding (iterator : Ast_iterator.iterator)
174+
(vb : Parsetree.value_binding) =
175+
(match vb.pvb_pat.ppat_desc with
176+
| Ppat_var {txt} | Ppat_constraint ({ppat_desc = Ppat_var {txt}}, _) ->
177+
symbols := (txt, vb.pvb_loc, exprKind vb.pvb_expr) :: !symbols
178+
| _ -> ());
179+
Ast_iterator.default_iterator.value_binding iterator vb
180+
in
181+
(* let expr (iterator : Ast_iterator.iterator) (e : Parsetree.expression) = *)
182+
(* (match e.pexp_desc with *)
183+
(* | Pexp_letmodule ({txt}, modExpr, _) -> *)
184+
(* symbols := *)
185+
(* (txt, {e.pexp_loc with loc_end = modExpr.pmod_loc.loc_end}, Module) *)
186+
(* :: !symbols *)
187+
(* (* | Pexp_letexception (ec, _) -> processExtensionConstructor ec *) *)
188+
(* | _ -> ()); *)
189+
(* Ast_iterator.default_iterator.expr iterator e *)
190+
(* in *)
191+
let structure_item (iterator : Ast_iterator.iterator)
192+
(item : Parsetree.structure_item) =
193+
(match item.pstr_desc with
194+
| Pstr_value _ -> ()
195+
| Pstr_primitive vd -> processValueDescription vd
196+
(* | Pstr_type (_, typDecls) -> typDecls |> List.iter processTypeDeclaration *)
197+
(* | Pstr_module mb -> processModuleBinding mb *)
198+
(* | Pstr_recmodule mbs -> mbs |> List.iter processModuleBinding *)
199+
(* | Pstr_exception ec -> processExtensionConstructor ec *)
200+
| _ -> ());
201+
Ast_iterator.default_iterator.structure_item iterator item
202+
in
203+
let signature_item (iterator : Ast_iterator.iterator)
204+
(item : Parsetree.signature_item) =
205+
(match item.psig_desc with
206+
| Psig_value vd -> processValueDescription vd
207+
(* | Psig_type (_, typDecls) -> typDecls |> List.iter processTypeDeclaration *)
208+
(* | Psig_module md -> processModuleDeclaration md *)
209+
(* | Psig_recmodule mds -> mds |> List.iter processModuleDeclaration *)
210+
(* | Psig_exception ec -> processExtensionConstructor ec *)
211+
| _ -> ());
212+
Ast_iterator.default_iterator.signature_item iterator item
213+
in
214+
(* let module_expr (iterator : Ast_iterator.iterator) *)
215+
(* (me : Parsetree.module_expr) = *)
216+
(* match me.pmod_desc with *)
217+
(* | Pmod_constraint (modExpr, _modTyp) -> *)
218+
(* (* Don't double-list items in implementation and interface *) *)
219+
(* Ast_iterator.default_iterator.module_expr iterator modExpr *)
220+
(* | _ -> Ast_iterator.default_iterator.module_expr iterator me *)
221+
(* in *)
222+
let iterator =
223+
{
224+
Ast_iterator.default_iterator with
225+
(* expr; *)
226+
(* module_expr; *)
227+
(* signature_item; *)
228+
(* structure_item; *)
229+
value_binding;
230+
}
231+
in
232+
233+
(if Filename.check_suffix path ".res" then
234+
let parser =
235+
Res_driver.parsingEngine.parseImplementation ~forPrinter:false
236+
in
237+
let {Res_driver.parsetree = structure} = parser ~filename:path in
238+
iterator.structure iterator structure |> ignore
239+
else
240+
let parser = Res_driver.parsingEngine.parseInterface ~forPrinter:false in
241+
let {Res_driver.parsetree = signature} = parser ~filename:path in
242+
iterator.signature iterator signature |> ignore);
243+
!symbols
244+
|> List.rev_map (fun (name, loc, kind) ->
245+
let range = Utils.cmtLocToRange loc in
246+
let character_end_pos =
247+
4 + range.start.character + String.length name
248+
in
249+
let label =
250+
match Cmt.fullFromPath ~path with
251+
| None -> Protocol.null
252+
| Some full -> (
253+
match
254+
References.getLocItem ~full
255+
~pos:(range.start.line, character_end_pos)
256+
~debug:true
257+
with
258+
| None -> "refereces not found"
259+
| Some s -> (
260+
match locItemToTypeHint ~full s with
261+
| Some hint -> hint
262+
| None -> "TypeHint not found"))
263+
in
264+
Protocol.stringifyHint
265+
{
266+
kind = hintkindNumber kind;
267+
(* label is type *)
268+
(* label = name; *)
269+
tooltip = {
270+
kind = "markdown";
271+
value = Hover.codeBlock label;
272+
};
273+
paddingLeft = false;
274+
paddingRight = false;
275+
label = ": " ^ label;
276+
277+
position =
278+
{
279+
line = range.start.line;
280+
(* From col 0 to last character
281+
let name = "lol"
282+
^
283+
*)
284+
character =
285+
character_end_pos
286+
(* character = pos.start.character; *)
287+
(* line = 0; *)
288+
(* character = 0; *);
289+
};
290+
})

analysis/src/Protocol.ml

+14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
type position = {line : int; character : int}
22
type range = {start : position; end_ : position}
33
type markupContent = {kind : string; value : string}
4+
type inlayHint = {position : position; label : string; kind : int; tooltip: markupContent; paddingLeft: bool; paddingRight: bool}
45

56
type completionItem = {
67
label : string;
@@ -123,3 +124,16 @@ let stringifyCodeAction ca =
123124
Printf.sprintf {|{"title": "%s", "kind": "%s", "edit": %s}|} ca.title
124125
(codeActionKindToString ca.codeActionKind)
125126
(ca.edit |> stringifyCodeActionEdit)
127+
128+
let stringifyHint hint =
129+
Printf.sprintf
130+
{|{
131+
"position": %s,
132+
"label": "%s",
133+
"tooltip": %s,
134+
"kind": %i,
135+
"paddingLeft": %b,
136+
"paddingRight": %b
137+
}|}
138+
(stringifyPosition hint.position)
139+
hint.label (stringifyMarkupContent hint.tooltip) hint.kind hint.paddingLeft hint.paddingRight

analysis/tests/src/InlayHint.res

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
let string = "ReScript"
2+
let number = 1
3+
let float = 1.1
4+
let char = 'c'
5+
6+
let add = (x, y) => x + y
7+
8+
let my_sum = 3
9+
-> add(1)
10+
-> add(1)
11+
-> add(1)
12+
-> add(8)
13+
14+
let withAs = (~xx as yyy) => yyy + 1
15+
16+
let arity0a = (. ()) => {
17+
//^hov
18+
let f = () => 3
19+
f
20+
}
21+
22+
let arity0b = (. (), . ()) => 3
23+
24+
type t = (int, float)
25+
26+
@ocaml.doc("This module is commented")
27+
module Dep: {
28+
@ocaml.doc("Some doc comment")
29+
let customDouble: int => int
30+
} = {
31+
let customDouble = foo => foo * 2
32+
}
33+
34+
module D = Dep
35+
36+
let functionWithTypeAnnotation: unit => int = () => 1
37+
38+
@react.component
39+
let make = (~name) => React.string(name)
40+

0 commit comments

Comments
 (0)