Skip to content

Commit

Permalink
[#22] Update navigation to function definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
robertoaloi committed Jul 26, 2019
1 parent 377a9b5 commit aaef692
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 136 deletions.
26 changes: 9 additions & 17 deletions src/erlang_ls_buffer.erl
Expand Up @@ -37,7 +37,7 @@
%%==============================================================================
%% Type Definitions
%%==============================================================================
-type state() :: #state{}.
-type state() :: #state{}.

%%%===================================================================
%%% API
Expand Down Expand Up @@ -140,22 +140,14 @@ do_get_mfa(Text, Line, _Character) ->
{M, F, A}.

-spec do_get_element_at_pos(binary(), non_neg_integer(), non_neg_integer()) ->
any().
do_get_element_at_pos(Text, Line, Character) ->
Ast = get_ast(Text),
erlang_ls_navigation:find_by_pos({Line, Character}, Ast).

-spec get_ast(binary()) -> [any()].
get_ast(Text) ->
%% TODO: Do not write to file
File = "/tmp/x.erl",
ok = file:write_file(File, Text),
{ok, F} = file:open(File, [read]),
{ok, E} = epp:open(File, F, {1, 1}, [], []),
Epp = epp:parse_file(E),
ok = epp:close(E),
ok = file:close(F),
Epp.
erlang_ls_parser:poi().
do_get_element_at_pos(Text, Line, Column) ->
%% TODO: Cache tree
{ok, Tree} = erlang_ls_parser:parse(Text),
AnnotatedTree = erlang_ls_parser:annotate(Tree),
%% TODO: Refine API to handle multiple, overlapping, POIs
[POI] = erlang_ls_parser:find_poi_by_pos(AnnotatedTree, {Line, Column}),
POI.

-spec get_line_text(binary(), integer()) -> binary().
get_line_text(Text, Line) ->
Expand Down
4 changes: 3 additions & 1 deletion src/erlang_ls_compiler_diagnostics.erl
Expand Up @@ -53,7 +53,9 @@ diagnostics(List, Severity) ->

-spec diagnostic(integer(), module(), string(), integer()) -> diagnostic().
diagnostic(Line, Module, Desc, Severity) ->
Range = erlang_ls_protocol:range(Line - 1),
Range = erlang_ls_protocol:range(#{ from => {Line - 1, 0}
, to => {Line - 1, 0}
}),
Message = list_to_binary(lists:flatten(Module:format_error(Desc))),
#{ range => Range
, message => Message
Expand Down
4 changes: 3 additions & 1 deletion src/erlang_ls_dialyzer_diagnostics.erl
Expand Up @@ -35,7 +35,9 @@ diagnostics(Uri) ->
%%==============================================================================
-spec diagnostic({any(), {any(), integer()}, any()}) -> diagnostic().
diagnostic({_, {_, Line}, _} = Warning) ->
Range = erlang_ls_protocol:range(Line - 1),
Range = erlang_ls_protocol:range(#{ from => {Line - 1, 0}
, to => {Line - 1, 0}
}),
Message = list_to_binary(lists:flatten(dialyzer:format_warning(Warning))),
#{ range => Range
, message => Message
Expand Down
9 changes: 2 additions & 7 deletions src/erlang_ls_indexer.erl
Expand Up @@ -91,14 +91,9 @@ handle_cast(_Msg, State) -> {noreply, State}.
%%==============================================================================
-spec do_index(uri(), binary()) -> ok.
do_index(Uri, Text) ->
%% TODO: Avoid writing
Path = "/tmp/erlang_ls_tmp",
ok = file:write_file(Path, Text),
{ok, IoDevice} = file:open(Path, [read]),
{ok, Forms} = epp_dodger:parse(IoDevice, {1, 1}),
{ok, Tree} = erlang_ls_parser:parse(Text),
F = fun(Form) -> index_form(Form, Uri) end,
erl_syntax_lib:map(F, erl_syntax:form_list(Forms)),
ok = file:close(IoDevice),
erl_syntax_lib:map(F, Tree),
ok.

-spec index_form(erl_syntax:syntaxTree(), uri()) -> erl_syntax:syntaxTree().
Expand Down
67 changes: 0 additions & 67 deletions src/erlang_ls_navigation.erl

This file was deleted.

14 changes: 14 additions & 0 deletions src/erlang_ls_parser.erl
Expand Up @@ -3,6 +3,7 @@

-export([ annotate/1
, annotate_node/1
, find_poi_by_info/2
, find_poi_by_pos/2
, list_poi/1
, parse/1
Expand All @@ -18,6 +19,8 @@
%% Point of Interest
-type poi() :: #{ type := atom(), info => any(), range := range()}.

-export_type([ poi/0 ]).

%% TODO: Generate random filename
%% TODO: Ideally avoid writing to file at all (require epp changes)
-define(TMP_PATH, "/tmp/erlang_ls_tmp").
Expand Down Expand Up @@ -83,6 +86,10 @@ get_range(_Tree, {_Line, _Column}, {exports_entry, {_F, _A}}) ->
%% TODO: The location information for the arity qualifiers are lost during
%% parsing in `epp_dodger`. This requires fixing.
#{ from => {0, 0}, to => {0, 0} };
get_range(_Tree, {Line, Column}, {function, {F, _A}}) ->
From = {Line - 1, Column - 1},
To = {Line - 1, Column + length(atom_to_list(F)) - 1},
#{ from => From, to => To };
get_range(_Tree, {Line, Column}, {include, Include}) ->
From = {Line, Column - 1},
To = {Line, Column + length("include") + length(Include)},
Expand All @@ -104,6 +111,10 @@ get_range(_Tree, {_Line, _Column}, {spec, _Spec}) ->
%% parsing in `epp_dodger`. This requires fixing.
#{ from => {0, 0}, to => {0, 0} }.

-spec find_poi_by_info(syntax_tree(), any()) -> poi().
find_poi_by_info(Tree, Info0) ->
[POI || #{info := Info} = POI <- list_poi(Tree), Info0 =:= Info].

-spec find_poi_by_pos(syntax_tree(), pos()) -> [poi()].
find_poi_by_pos(Tree, Pos) ->
[POI || #{range := Range} = POI <- list_poi(Tree), matches_pos(Pos, Range)].
Expand Down Expand Up @@ -192,6 +203,9 @@ analyze(Tree, attribute) ->
_ ->
[]
end;
analyze(Tree, function) ->
{F, A} = erl_syntax_lib:analyze_function(Tree),
[poi(Tree, {function, {F, A}})];
analyze(Tree, macro) ->
Macro = erl_syntax:variable_name(erl_syntax:macro_name(Tree)),
[poi(Tree, {macro, Macro})];
Expand Down
16 changes: 5 additions & 11 deletions src/erlang_ls_protocol.erl
Expand Up @@ -13,9 +13,7 @@
]).

%% Data Structures
-export([ range/1
, range/2
]).
-export([ range/1 ]).

%%==============================================================================
%% Includes
Expand Down Expand Up @@ -54,14 +52,10 @@ response(RequestId, Result) ->
%%==============================================================================
%% Data Structures
%%==============================================================================
-spec range(integer()) -> range().
range(FromLine) ->
range(FromLine, FromLine).

-spec range(integer(), integer()) -> range().
range(FromLine, ToLine) ->
#{ start => #{line => FromLine, character => 0}
, 'end' => #{line => ToLine, character => 0}
-spec range(erlang_ls_parser:range()) -> range().
range(#{ from := {FromL, FromC}, to := {ToL, ToC} }) ->
#{ start => #{line => FromL, character => FromC}
, 'end' => #{line => ToL, character => ToC}
}.

%%==============================================================================
Expand Down
55 changes: 24 additions & 31 deletions src/erlang_ls_server.erl
Expand Up @@ -36,6 +36,8 @@
%%==============================================================================
-record(state, {socket, buffer}).

-define(OTP_INCLUDE_PATH, "/usr/local/Cellar/erlang/21.2.4/lib/erlang/lib").

%%==============================================================================
%% Type Definitions
%%==============================================================================
Expand Down Expand Up @@ -175,8 +177,8 @@ handle_method(<<"textDocument/definition">>, Params) ->
TextDocument = maps:get(<<"textDocument">>, Params),
Uri = maps:get(<<"uri">> , TextDocument),
{ok, Buffer} = erlang_ls_buffer_server:get_buffer(Uri),
Element = erlang_ls_buffer:get_element_at_pos(Buffer, Line + 1, Character + 1),
Result = definition(Element),
POI = erlang_ls_buffer:get_element_at_pos(Buffer, Line + 1, Character + 1),
Result = definition(Uri, POI),
{response, Result};
handle_method(Method, _Params) ->
lager:warning("[Method not implemented] [method=~s]", [Method]),
Expand All @@ -193,36 +195,27 @@ send_notification(Socket, Method, Params) ->
lager:debug("[SERVER] Sending notification [notification=~p]", [Notification]),
gen_tcp:send(Socket, Notification).

-spec definition(any()) -> null | [map()].
definition(Element) ->
case erl_syntax:type(Element) of
application ->
Op = erl_syntax:application_operator(Element),
A = length(erl_syntax:application_arguments(Element)),
case erl_syntax:type(Op) of
module_qualifier ->
definition_from_module_qualifier(Op, A);
_ ->
-spec definition(uri(), erlang_ls_parser:poi()) -> null | map().
definition(_Uri, #{ info := {application, {M, F, A}} }) ->
%% TODO: Check whether URI is already cached
Path = filelib:wildcard(filename:join([?OTP_INCLUDE_PATH, "*/src"])),
%% TODO: Cache syntax tree here?
case file:path_open(Path, atom_to_list(M) ++ ".erl", [read]) of
{ok, IoDevice, FullName} ->
%% TODO: Avoid opening file twice
file:close(IoDevice),
{ok, Tree} = erlang_ls_parser:parse_file(FullName),
AnnotatedTree = erlang_ls_parser:annotate(Tree),
Info = {function, {F, A}},
case erlang_ls_parser:find_poi_by_info(AnnotatedTree, Info) of
[#{ range := Range }] ->
%% TODO: Use API to create types
#{ uri => erlang_ls_uri:uri(list_to_binary(FullName))
, range => erlang_ls_protocol:range(Range)
};
[] ->
null
end;
_ ->
null
end.

-spec definition_from_module_qualifier(any(), non_neg_integer()) -> null | map().
definition_from_module_qualifier(Op, A) ->
M = list_to_atom(erl_syntax:atom_name(erl_syntax:module_qualifier_argument(Op))),
F = list_to_atom(erl_syntax:atom_name(erl_syntax:module_qualifier_body(Op))),
Which = code:which(M),
Source = list_to_binary(proplists:get_value( source
, M:module_info(compile))),
DefUri = <<"file://", Source/binary>>,
{ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Which, [abstract_code]),
case [ AL || {function, AL, AF, AA, _} <- AC, F =:= AF, A =:= AA] of
[DefLine] ->
#{ uri => DefUri
, range => erlang_ls_protocol:range(erl_anno:line(DefLine) - 1)
};
[] ->
{error, _Error} ->
null
end.
8 changes: 7 additions & 1 deletion src/erlang_ls_uri.erl
Expand Up @@ -8,7 +8,9 @@
%%==============================================================================
%% Exports
%%==============================================================================
-export([ path/1 ]).
-export([ path/1
, uri/1
]).

%%==============================================================================
%% Includes
Expand All @@ -18,3 +20,7 @@
-spec path(uri()) -> uri_path().
path(<<"file://", Path/binary>>) ->
Path.

-spec uri(uri_path()) -> uri().
uri(Path) ->
<<"file://", Path/binary>>.

0 comments on commit aaef692

Please sign in to comment.