Skip to content

Commit f8a8f7f

Browse files
authored
feat(grainlsp): Add inlay types (#2005)
1 parent 759ea13 commit f8a8f7f

File tree

4 files changed

+173
-14
lines changed

4 files changed

+173
-14
lines changed

compiler/src/language_server/inlayhint.re

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,100 @@ let send_no_result = (~id: Protocol.message_id) => {
3333
Protocol.response(~id, `Null);
3434
};
3535

36+
let build_hint =
37+
(position: Protocol.position, message: string): ResponseResult.inlay_hint => {
38+
{label: ": " ++ message, position};
39+
};
40+
41+
let rec resolve_typ = (typ: Types.type_expr) => {
42+
switch (typ.desc) {
43+
| TTyLink(type_expr)
44+
| TTySubst(type_expr) => resolve_typ(type_expr)
45+
| _ => typ
46+
};
47+
};
48+
let string_of_typ = (typ: Types.type_expr) => {
49+
Printtyp.string_of_type_scheme(resolve_typ(typ));
50+
};
51+
52+
let is_func_typ = (typ: Types.type_expr) => {
53+
switch (resolve_typ(typ).desc) {
54+
| TTyArrow(_, _, _) => true
55+
| _ => false
56+
};
57+
};
58+
3659
let find_hints = program => {
3760
let hints = ref([]);
3861
open Typedtree;
3962
open Protocol;
4063
module Iterator =
4164
TypedtreeIter.MakeIterator({
4265
include TypedtreeIter.DefaultIteratorArgument;
43-
// Inlay hints for various expressions can be included here.
66+
67+
let enter_expression = ({exp_desc, exp_type}: expression) =>
68+
switch (exp_desc) {
69+
| TExpLambda(bindings, _) =>
70+
List.iter(
71+
({mb_pat, mb_loc}: match_branch) => {
72+
switch (mb_pat.pat_desc) {
73+
| TPatTuple(args) =>
74+
switch (resolve_typ(exp_type).desc) {
75+
| TTyArrow(typ_args, _, _) =>
76+
let argument_typs =
77+
List.map(
78+
((arg, typ: Types.type_expr)) =>
79+
switch (arg) {
80+
| Default(_) => None
81+
| _ => Some(typ)
82+
},
83+
typ_args,
84+
);
85+
if (List.length(argument_typs) == List.length(args)) {
86+
List.iter(
87+
((arg: pattern, typ: option(Types.type_expr))) => {
88+
switch (arg.pat_desc, typ) {
89+
| (TPatVar(_, _), Some(typ)) =>
90+
let bind_end = arg.pat_loc.loc_end;
91+
let p: Protocol.position = {
92+
line: bind_end.pos_lnum - 1,
93+
character: bind_end.pos_cnum - bind_end.pos_bol,
94+
};
95+
let typeSignature = string_of_typ(typ);
96+
hints := [build_hint(p, typeSignature), ...hints^];
97+
| _ => ()
98+
}
99+
},
100+
List.combine(args, argument_typs),
101+
);
102+
};
103+
| _ => ()
104+
}
105+
| _ => ()
106+
}
107+
},
108+
bindings,
109+
)
110+
| _ => ()
111+
};
112+
113+
let enter_binding = ({vb_pat, vb_expr}: value_binding, toplevel: bool) =>
114+
if (!toplevel) {
115+
switch (vb_pat) {
116+
| {pat_extra: [], pat_desc: TPatVar(_, {loc})} =>
117+
let bind_end = loc.loc_end;
118+
let p: Protocol.position = {
119+
line: bind_end.pos_lnum - 1,
120+
character: bind_end.pos_cnum - bind_end.pos_bol,
121+
};
122+
let typ = vb_pat.pat_type;
123+
if (!is_func_typ(typ)) {
124+
let typeSignature = string_of_typ(typ);
125+
hints := [build_hint(p, typeSignature), ...hints^];
126+
};
127+
| _ => ()
128+
};
129+
};
44130
});
45131
Iterator.iter_typed_program(program);
46132
hints^;

compiler/src/typed/typedtreeIter.re

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ module type IteratorArgument = {
3030
let leave_core_type: core_type => unit;
3131
let leave_toplevel_stmt: toplevel_stmt => unit;
3232

33-
let enter_bindings: (rec_flag, mut_flag) => unit;
34-
let enter_binding: value_binding => unit;
33+
let enter_bindings: (rec_flag, mut_flag, bool) => unit;
34+
let enter_binding: (value_binding, bool) => unit;
3535
let leave_binding: value_binding => unit;
3636
let leave_bindings: (rec_flag, mut_flag) => unit;
3737

@@ -74,16 +74,16 @@ module MakeIterator =
7474
Iter.leave_core_type(ct);
7575
}
7676

77-
and iter_binding = ({vb_pat, vb_expr} as vb) => {
78-
Iter.enter_binding(vb);
77+
and iter_binding = (~toplevel=false, {vb_pat, vb_expr} as vb) => {
78+
Iter.enter_binding(vb, toplevel);
7979
iter_pattern(vb_pat);
8080
iter_expression(vb_expr);
8181
Iter.leave_binding(vb);
8282
}
8383

84-
and iter_bindings = (rec_flag, mut_flag, binds) => {
85-
Iter.enter_bindings(rec_flag, mut_flag);
86-
List.iter(iter_binding, binds);
84+
and iter_bindings = (~toplevel=false, rec_flag, mut_flag, binds) => {
85+
Iter.enter_bindings(rec_flag, mut_flag, toplevel);
86+
List.iter(iter_binding(~toplevel), binds);
8787
Iter.leave_bindings(rec_flag, mut_flag);
8888
}
8989

@@ -132,7 +132,7 @@ module MakeIterator =
132132
| TTopModule({tmod_statements}) => iter_toplevel_stmts(tmod_statements)
133133
| TTopExpr(e) => iter_expression(e)
134134
| TTopLet(recflag, mutflag, binds) =>
135-
iter_bindings(recflag, mutflag, binds)
135+
iter_bindings(recflag, mutflag, binds, ~toplevel=true)
136136
};
137137
Iter.leave_toplevel_stmt(stmt);
138138
}
@@ -206,7 +206,7 @@ module MakeIterator =
206206
| TExpIdent(_)
207207
| TExpConstant(_) => ()
208208
| TExpLet(recflag, mutflag, binds) =>
209-
iter_bindings(recflag, mutflag, binds)
209+
iter_bindings(recflag, mutflag, binds, ~toplevel=false)
210210
| TExpLambda(branches, _) => iter_match_branches(branches)
211211
| TExpApp(exp, _, args) =>
212212
iter_expression(exp);
@@ -277,8 +277,8 @@ module DefaultIteratorArgument: IteratorArgument = {
277277
let enter_expression = _ => ();
278278
let enter_core_type = _ => ();
279279
let enter_toplevel_stmt = _ => ();
280-
let enter_bindings = (_, _) => ();
281-
let enter_binding = _ => ();
280+
let enter_bindings = (_, _, _) => ();
281+
let enter_binding = (_, _) => ();
282282
let enter_data_declaration = _ => ();
283283
let enter_data_declarations = () => ();
284284

compiler/src/typed/typedtreeIter.rei

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ module type IteratorArgument = {
3030
let leave_core_type: core_type => unit;
3131
let leave_toplevel_stmt: toplevel_stmt => unit;
3232

33-
let enter_bindings: (rec_flag, mut_flag) => unit;
34-
let enter_binding: value_binding => unit;
33+
let enter_bindings: (rec_flag, mut_flag, bool) => unit;
34+
let enter_binding: (value_binding, bool) => unit;
3535
let leave_binding: value_binding => unit;
3636
let leave_bindings: (rec_flag, mut_flag) => unit;
3737

compiler/test/suites/grainlsp.re

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,4 +910,77 @@ let Some(a) = Some(1)
910910
]),
911911
]),
912912
);
913+
914+
assertLspOutput(
915+
"inlay_hints",
916+
"file:///a.gr",
917+
{|
918+
module Main
919+
920+
// Top Level Values
921+
let a = 1
922+
and b = 2
923+
924+
let c = true
925+
let d = []
926+
// Patterns
927+
enum A {
928+
S(Number),
929+
}
930+
let S(t) = S(1)
931+
// Functions
932+
let e = (a, b) => a + b
933+
let e = (a, b) => {
934+
return (a, b) => a + b
935+
}
936+
let test = (a, b, c: Number) => {
937+
let x = 1
938+
let S(t) = S(1)
939+
return a + b + c
940+
}
941+
// Record
942+
record Nested {
943+
a: Number,
944+
b: String,
945+
}
946+
record Test {
947+
test: String,
948+
test2: Nested,
949+
}
950+
let test = { test: "test", test2: { a: 1, b: "test" } }
951+
{
952+
let test = { test: "test", test2: { a: 1, b: "test" } }
953+
}
954+
|},
955+
lsp_input(
956+
"textDocument/inlayHint",
957+
`Assoc([
958+
("textDocument", `Assoc([("uri", `String("file:///a.gr"))])),
959+
("range", lsp_range((0, 0), (37, 0))),
960+
]),
961+
),
962+
`List(
963+
List.map(
964+
((label, (line, char))) => {
965+
`Assoc([
966+
("label", `String(label)),
967+
("position", lsp_position(line, char)),
968+
])
969+
},
970+
[
971+
// (label, position)
972+
(": Test", (35, 14)),
973+
(": Number", (20, 11)),
974+
(": Number", (19, 20)),
975+
(": Number", (19, 17)),
976+
(": Number", (17, 18)),
977+
(": Number", (17, 15)),
978+
(": a", (16, 17)),
979+
(": a", (16, 14)),
980+
(": Number", (15, 17)),
981+
(": Number", (15, 14)),
982+
],
983+
),
984+
),
985+
);
913986
});

0 commit comments

Comments
 (0)