Skip to content

Commit 0543c32

Browse files
authored
Add support for inlay hints (#1504)
Argument names in function calls will now show up as inlay hints. Inlay hints are disabled by default, enable by adding this to config: inlay_hints_enabled: true Additional changes: * Add functional API to submit job results to els_server * Improve completions by extracting argument names from spec. * Improve completions by extracting argument names from all function clauses. * Gracefully handle parsing of missing header file
1 parent a0bf4fe commit 0543c32

23 files changed

+659
-110
lines changed

apps/els_core/include/els_core.hrl

+29
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,35 @@
602602
command => els_command:command()
603603
}.
604604

605+
%%------------------------------------------------------------------------------
606+
%% Inlay hint
607+
%%------------------------------------------------------------------------------
608+
-define(INLAY_HINT_KIND_TYPE, 1).
609+
-define(INLAY_HINT_KIND_PARAMETER, 2).
610+
611+
-type inlay_hint_kind() ::
612+
?INLAY_HINT_KIND_TYPE
613+
| ?INLAY_HINT_KIND_PARAMETER.
614+
615+
-type inlay_hint_label_part() ::
616+
#{
617+
value := binary(),
618+
tooltip => binary() | markup_content(),
619+
location => location(),
620+
command => els_command:command()
621+
}.
622+
623+
-type inlay_hint() :: #{
624+
position := position(),
625+
label := binary() | [inlay_hint_label_part()],
626+
kind => inlay_hint_kind(),
627+
textEdits => [text_edit()],
628+
tooltip => binary() | markup_content(),
629+
paddingLeft => boolean(),
630+
paddingRight => boolean(),
631+
data => map()
632+
}.
633+
605634
%%------------------------------------------------------------------------------
606635
%% Workspace
607636
%%------------------------------------------------------------------------------

apps/els_core/src/els_client.erl

+12-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
preparecallhierarchy/3,
5858
callhierarchy_incomingcalls/1,
5959
callhierarchy_outgoingcalls/1,
60-
get_notifications/0
60+
get_notifications/0,
61+
inlay_hint/2
6162
]).
6263

6364
-export([handle_responses/1]).
@@ -276,6 +277,10 @@ get_notifications() ->
276277
handle_responses(Responses) ->
277278
gen_server:cast(?SERVER, {handle_responses, Responses}).
278279

280+
-spec inlay_hint(uri(), range()) -> ok.
281+
inlay_hint(Uri, Range) ->
282+
gen_server:call(?SERVER, {inlay_hint, {Uri, Range}}).
283+
279284
%%==============================================================================
280285
%% gen_server Callback Functions
281286
%%==============================================================================
@@ -462,6 +467,7 @@ method_lookup(implementation) -> <<"textDocument/implementation">>;
462467
method_lookup(folding_range) -> <<"textDocument/foldingRange">>;
463468
method_lookup(semantic_tokens) -> <<"textDocument/semanticTokens/full">>;
464469
method_lookup(preparecallhierarchy) -> <<"textDocument/prepareCallHierarchy">>;
470+
method_lookup(inlay_hint) -> <<"textDocument/inlayHint">>;
465471
method_lookup(callhierarchy_incomingcalls) -> <<"callHierarchy/incomingCalls">>;
466472
method_lookup(callhierarchy_outgoingcalls) -> <<"callHierarchy/outgoingCalls">>;
467473
method_lookup(workspace_symbol) -> <<"workspace/symbol">>;
@@ -476,6 +482,11 @@ request_params({document_symbol, {Uri}}) ->
476482
#{textDocument => TextDocument};
477483
request_params({workspace_symbol, {Query}}) ->
478484
#{query => Query};
485+
request_params({inlay_hint, {Uri, Range}}) ->
486+
#{
487+
textDocument => #{uri => Uri},
488+
range => Range
489+
};
479490
request_params({workspace_executecommand, {Command, Args}}) ->
480491
#{
481492
command => Command,

apps/els_core/src/els_config.erl

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) ->
172172
RefactorErl = maps:get("refactorerl", Config, notconfigured),
173173
Providers = maps:get("providers", Config, #{}),
174174
EdocParseEnabled = maps:get("edoc_parse_enabled", Config, true),
175+
InlayHintsEnabled = maps:get("inlay_hints_enabled", Config, false),
175176
Formatting = maps:get("formatting", Config, #{}),
176177
DocsMemo = maps:get("docs_memo", Config, false),
177178

@@ -248,6 +249,7 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) ->
248249
ok = set(edoc_custom_tags, EDocCustomTags),
249250
ok = set(edoc_parse_enabled, EdocParseEnabled),
250251
ok = set(incremental_sync, IncrementalSync),
252+
ok = set(inlay_hints_enabled, InlayHintsEnabled),
251253
ok = set(
252254
indexing,
253255
maps:merge(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-module(inlay_hint).
2+
-export([test/0]).
3+
4+
-record(foo, {}).
5+
6+
test() ->
7+
a(1, 2),
8+
b(1, 2),
9+
c(1),
10+
d(1, 2),
11+
lists:append([], []).
12+
13+
a(Hej, Hoj) ->
14+
Hej + Hoj.
15+
16+
b(x, y) ->
17+
0;
18+
b(A, _B) ->
19+
A.
20+
21+
c(#foo{}) ->
22+
ok.
23+
24+
d([1,2,3] = Foo,
25+
Bar = #{hej := 123}) ->
26+
ok.

apps/els_lsp/src/els_arg.erl

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-module(els_arg).
2+
-export([name/1]).
3+
-export([name/2]).
4+
-export([index/1]).
5+
-export_type([arg/0]).
6+
7+
-type arg() :: #{
8+
index := pos_integer(),
9+
name := string() | undefined,
10+
range => els_poi:poi_range()
11+
}.
12+
13+
-spec name(arg()) -> string().
14+
name(Arg) ->
15+
name("Arg", Arg).
16+
17+
-spec name(string(), arg()) -> string().
18+
name(Prefix, #{index := N, name := undefined}) ->
19+
Prefix ++ integer_to_list(N);
20+
name(_Prefix, #{name := Name}) ->
21+
Name.
22+
23+
-spec index(arg()) -> string().
24+
index(#{index := Index}) ->
25+
integer_to_list(Index).

apps/els_lsp/src/els_background_job.erl

+23
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
-export([
1010
new/1,
1111
list/0,
12+
list_titles/0,
1213
stop/1,
1314
stop_all/0
1415
]).
@@ -82,6 +83,22 @@ list() ->
8283
supervisor:which_children(els_background_job_sup)
8384
].
8485

86+
%% @doc Return the list of running background jobs
87+
-spec list_titles() -> [any()].
88+
list_titles() ->
89+
Children = supervisor:which_children(els_background_job_sup),
90+
lists:flatmap(
91+
fun({_Id, Pid, _Type, _Modules}) ->
92+
case catch gen_server:call(Pid, get_title) of
93+
{ok, Title} ->
94+
[Title];
95+
_ ->
96+
[]
97+
end
98+
end,
99+
Children
100+
).
101+
85102
%% @doc Terminate a background job
86103
-spec stop(pid()) -> ok.
87104
stop(Pid) ->
@@ -145,6 +162,12 @@ init(#{entries := Entries, title := Title} = Config) ->
145162

146163
-spec handle_call(any(), {pid(), any()}, state()) ->
147164
{noreply, state()}.
165+
handle_call(
166+
get_title,
167+
_From,
168+
#{config := #{title := Title}} = State
169+
) ->
170+
{reply, {ok, Title}, State};
148171
handle_call(_Request, _From, State) ->
149172
{noreply, State}.
150173

apps/els_lsp/src/els_code_actions.erl

+1-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ format_args(Document, Arity, Range) ->
264264
],
265265
case Matches of
266266
[#{data := #{args := Args0}} | _] ->
267-
string:join([A || {_N, A} <- Args0], ", ");
267+
string:join([els_arg:name(A) || A <- Args0], ", ");
268268
[] ->
269269
string:join(lists:duplicate(Arity, "_"), ", ")
270270
end.

apps/els_lsp/src/els_code_lens_provider.erl

+1-5
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@ run_lenses_job(Uri) ->
4242
end,
4343
entries => [Document],
4444
title => <<"Lenses">>,
45-
on_complete =>
46-
fun(Lenses) ->
47-
els_server ! {result, Lenses, self()},
48-
ok
49-
end
45+
on_complete => fun els_server:register_result/1
5046
},
5147
{ok, Pid} = els_background_job:new(Config),
5248
Pid.

apps/els_lsp/src/els_compiler_diagnostics.erl

+17-7
Original file line numberDiff line numberDiff line change
@@ -117,20 +117,30 @@ parse(Uri) ->
117117
_ ->
118118
undefined
119119
end,
120-
{ok, Epp} = epp:open([
120+
Options = [
121121
{name, FileName},
122122
{includes, els_config:get(include_paths)},
123123
{macros, [
124124
{'MODULE', dummy_module, redefine},
125125
{'MODULE_STRING', "dummy_module", redefine}
126126
]}
127-
]),
128-
Res = [
129-
epp_diagnostic(Document, Anno, Module, Desc)
130-
|| {error, {Anno, Module, Desc}} <- epp:parse_file(Epp)
131127
],
132-
epp:close(Epp),
133-
Res.
128+
case epp:open(Options) of
129+
{ok, Epp} ->
130+
Res = [
131+
epp_diagnostic(Document, Anno, Module, Desc)
132+
|| {error, {Anno, Module, Desc}} <- epp:parse_file(Epp)
133+
],
134+
epp:close(Epp),
135+
Res;
136+
{error, Reason} ->
137+
?LOG_ERROR(
138+
"Failed to open: ~s\n"
139+
"Reason: ~p",
140+
[FileName, Reason]
141+
),
142+
[]
143+
end.
134144

135145
%% Possible cases to handle
136146
%% ,{error,{19,erl_parse,["syntax error before: ","'-'"]}}

0 commit comments

Comments
 (0)