Skip to content

Commit

Permalink
Use info from -spec for inlay hints if available.
Browse files Browse the repository at this point in the history
  • Loading branch information
plux committed May 6, 2024
1 parent 46b80b5 commit c802f78
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 87 deletions.
27 changes: 21 additions & 6 deletions apps/els_lsp/priv/code_navigation/src/inlay_hint.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,34 @@ test() ->
b(1, 2),
c(1),
d(1, 2),
e(1, 2),
f(1, 2),
g(1, 2),
lists:append([], []).

a(Hej, Hoj) ->
Hej + Hoj.
a(A1, A2) ->
A1 + A2.

b(x, y) ->
0;
b(A, _B) ->
A.
b(B1, _B2) ->
B1.

c(#foo{}) ->
ok.

d([1,2,3] = Foo,
Bar = #{hej := 123}) ->
d([1,2,3] = D1,
D2 = #{hej := 123}) ->
ok.

-spec e(E1 :: any(), E2 :: any()) -> ok.
e(_, _) ->
ok.

-spec f(F1, F2) -> ok when F1 :: any(), F2 :: any().
f(_, _) ->
ok.

-spec g(G1, any()) -> ok when G1 :: any().
g(_, G2) ->
ok.
31 changes: 30 additions & 1 deletion apps/els_lsp/src/els_arg.erl
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
-module(els_arg).
-export([new/2]).
-export([name/1]).
-export([name/2]).
-export([index/1]).
-export([merge_args/2]).

-export_type([arg/0]).
-export_type([args/0]).

-type args() :: [arg()].
-type arg() :: #{
index := pos_integer(),
name := string() | undefined,
name := string() | undefined | {type, string()},
range => els_poi:poi_range()
}.

-spec new(pos_integer(), string()) -> arg().
new(Index, Name) ->
#{index => Index, name => Name}.

-spec name(arg()) -> string().
name(Arg) ->
name("Arg", Arg).

-spec name(string(), arg()) -> string().
name(Prefix, #{index := N, name := undefined}) ->
Prefix ++ integer_to_list(N);
name(_Prefix, #{name := {type, Name}}) ->
Name;
name(_Prefix, #{name := Name}) ->
Name.

-spec index(arg()) -> string().
index(#{index := Index}) ->
integer_to_list(Index).

-spec merge_args(args(), args()) -> args().
merge_args([], []) ->
[];
merge_args([#{name := undefined} | T1], [Arg | T2]) ->
[Arg | merge_args(T1, T2)];
merge_args(
[#{name := {type, Name}} = Arg | T1],
[#{name := undefined} | T2]
) ->
[Arg#{name := Name} | merge_args(T1, T2)];
merge_args(
[#{name := {type, _}} | T1],
[Arg | T2]
) ->
[Arg | merge_args(T1, T2)];
merge_args([Arg | T1], [_ | T2]) ->
[Arg | merge_args(T1, T2)].
22 changes: 7 additions & 15 deletions apps/els_lsp/src/els_completion_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1039,10 +1039,10 @@ bif_pois(define) ->
|| {Id, Args} <- Macros
].

-spec generate_arguments(string(), integer()) -> els_parser:args().
-spec generate_arguments(string(), integer()) -> els_arg:args().
generate_arguments(Prefix, Arity) ->
[
#{index => N, name => Prefix ++ integer_to_list(N)}
els_arg:new(N, Prefix ++ integer_to_list(N))
|| N <- lists:seq(1, Arity)
].

Expand Down Expand Up @@ -1134,7 +1134,7 @@ completion_item(#{kind := Kind = define, id := Name, data := Info}, Data, _, _Ur
data => Data
}.

-spec args(els_poi:poi(), uri()) -> els_parser:args().
-spec args(els_poi:poi(), uri()) -> els_arg:args().
args(#{kind := type_definition, data := POIData}, _Uri) ->
maps:get(args, POIData);
args(#{kind := _Kind, data := POIData}, _Uri = undefined) ->
Expand All @@ -1145,19 +1145,11 @@ args(#{kind := function, data := POIData, id := Id}, Uri) ->
POIs = els_dt_document:pois(Document, [spec]),
case [P || #{id := SpecId} = P <- POIs, SpecId == Id] of
[#{data := #{args := SpecArgs}} | _] when SpecArgs /= [] ->
merge_args(SpecArgs, maps:get(args, POIData));
els_arg:merge_args(SpecArgs, maps:get(args, POIData));
_ ->
maps:get(args, POIData)
end.

-spec merge_args(els_parser:args(), els_parser:args()) -> els_parser:args().
merge_args([], []) ->
[];
merge_args([#{name := undefined} | T1], [Arg | T2]) ->
[Arg | merge_args(T1, T2)];
merge_args([Arg | T1], [_ | T2]) ->
[Arg | merge_args(T1, T2)].

-spec features() -> items().
features() ->
%% Hardcoded for now. Could use erl_features:all() in the future.
Expand All @@ -1179,13 +1171,13 @@ macro_label({Name, Arity}) ->
macro_label(Name) ->
atom_to_binary(Name, utf8).

-spec format_function(atom(), els_parser:args(), boolean(), els_poi:poi_kind()) -> binary().
-spec format_function(atom(), els_arg:args(), boolean(), els_poi:poi_kind()) -> binary().
format_function(Name, Args, SnippetSupport, Kind) ->
format_args(atom_to_label(Name), Args, SnippetSupport, Kind).

-spec format_macro(
atom() | {atom(), non_neg_integer()},
els_parser:args(),
els_arg:args(),
boolean()
) -> binary().
format_macro({Name0, _Arity}, Args, SnippetSupport) ->
Expand All @@ -1196,7 +1188,7 @@ format_macro(Name, none, _SnippetSupport) ->

-spec format_args(
binary(),
els_parser:args(),
els_arg:args(),
boolean(),
els_poi:poi_kind()
) -> binary().
Expand Down
2 changes: 1 addition & 1 deletion apps/els_lsp/src/els_docs.erl
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ format_edoc(Desc) when is_map(Desc) ->
FormattedDoc = els_utils:to_list(docsh_edoc:format_edoc(Doc, #{})),
[{text, FormattedDoc}].

-spec macro_signature(els_poi:poi_id(), els_parser:args()) -> unicode:charlist().
-spec macro_signature(els_poi:poi_id(), els_arg:args()) -> unicode:charlist().
macro_signature({Name, _Arity}, Args) ->
[atom_to_list(Name), "(", lists:join(", ", [els_arg:name(A) || A <- Args]), ")"];
macro_signature(Name, none) ->
Expand Down
68 changes: 53 additions & 15 deletions apps/els_lsp/src/els_inlay_hint_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,33 @@ run_inlay_hints_job(Uri, Range) ->
},
els_background_job:new(Config).

%% Dialyzer doesn't like this for some reason:
%% {DefUri, DefPOI} <- [definition(Uri, POI)]
-dialyzer({nowarn_function, get_inlay_hints/2}).

-spec get_inlay_hints({uri(), els_poi:poi_range()}, any()) ->
[inlay_hint()].
get_inlay_hints({Uri, Range}, _) ->
%% Wait for indexing job to finish, so that we have the updated document
wait_for_indexing_job(Uri),
%% Read the document to get the latest version
TS = erlang:timestamp(),
{ok, [Document]} = els_dt_document:lookup(Uri),
%% Fetch all application POIs that are in the given range
AppPOIs = els_dt_document:pois_in_range(Document, [application], Range),
[
arg_hint(ArgRange, ArgName)
Res = [
arg_hint(ArgRange, DefArgName)
|| #{data := #{args := CallArgs}} = POI <- AppPOIs,
#{index := N, name := Name, range := ArgRange} <- CallArgs,
#{data := #{args := DefArgs}} <- [definition(Uri, POI)],
ArgName <- [arg_name(N, DefArgs)],
should_show_arg_hint(Name, ArgName)
].
#{index := N, range := ArgRange} = Arg <- CallArgs,
{DefUri, DefPOI} <- [definition(Uri, POI)],
DefArgName <- [arg_name(N, get_args(DefUri, DefPOI))],
should_show_arg_hint(els_arg:name(Arg), DefArgName)
],
?LOG_DEBUG(
"Inlay hints took ~p ms",
[timer:now_diff(erlang:timestamp(), TS) div 1000]
),
Res.

-spec arg_hint(els_poi:poi_range(), string()) -> inlay_hint().
arg_hint(#{from := {FromL, FromC}}, ArgName) ->
Expand All @@ -81,12 +91,18 @@ should_show_arg_hint(Name, Name) ->
false;
should_show_arg_hint(_Name, undefined) ->
false;
should_show_arg_hint(_Name, _DefArgName) ->
true.
should_show_arg_hint(undefined, _Name) ->
true;
should_show_arg_hint(Name, DefArgName) ->
strip_trailing_digits(Name) /= strip_trailing_digits(DefArgName).

-spec strip_trailing_digits(string()) -> string().
strip_trailing_digits(String) ->
string:trim(String, trailing, "0123456789").

-spec wait_for_indexing_job(uri()) -> ok.
wait_for_indexing_job(Uri) ->
%% Add delay to allowing indexing job to finish
%% Add delay to allowing indexing job to start
timer:sleep(10),
JobTitles = els_background_job:list_titles(),
case lists:member(<<"Indexing ", Uri/binary>>, JobTitles) of
Expand All @@ -98,10 +114,12 @@ wait_for_indexing_job(Uri) ->
wait_for_indexing_job(Uri)
end.

-spec arg_name(non_neg_integer(), els_parser:args()) -> string() | undefined.
-spec arg_name(non_neg_integer(), els_arg:args()) -> string() | undefined.
arg_name(_N, []) ->
undefined;
arg_name(N, Args) ->
#{name := Name0} = lists:nth(N, Args),
case Name0 of
Arg = lists:nth(N, Args),
case els_arg:name(Arg) of
"_" ++ Name ->
Name;
Name ->
Expand All @@ -111,9 +129,29 @@ arg_name(N, Args) ->
-spec definition(uri(), els_poi:poi()) -> els_poi:poi() | error.
definition(Uri, POI) ->
case els_code_navigation:goto_definition(Uri, POI) of
{ok, [{_Uri, DefPOI} | _]} ->
DefPOI;
{ok, [Match | _]} ->
Match;
Err ->
?LOG_INFO("Error: ~p ~p", [Err, POI]),
error
end.

-spec get_args(uri(), els_poi:poi()) -> els_arg:args().
get_args(Uri, #{
id := {F, A},
data := #{args := Args}
}) ->
{ok, Document} = els_utils:lookup_document(Uri),
SpecPOIs = els_dt_document:pois(Document, [spec]),
SpecMatches = [
SpecArgs
|| #{id := Id, data := #{args := SpecArgs}} <- SpecPOIs,
Id == {F, A},
SpecArgs /= []
],
case SpecMatches of
[] ->
Args;
[SpecArgs | _] ->
els_arg:merge_args(SpecArgs, Args)
end.
Loading

0 comments on commit c802f78

Please sign in to comment.