diff --git a/rebar.config b/rebar.config index d9010c44e..fe96827b8 100644 --- a/rebar.config +++ b/rebar.config @@ -34,6 +34,7 @@ %% upstream version: %% https://github.com/massemanet/redbug/pull/10 , [ {redbug, {git, "https://github.com/robertoaloi/redbug.git", {branch, "relax-beam-lib-error-matching"}}} + , {eflame, {git, "https://github.com/jfacorro/eflame.git", {branch, "various.improvements"}}} ] } , { escript_emu_args diff --git a/src/els_code_navigation.erl b/src/els_code_navigation.erl index 128332d0d..9f6e7439a 100644 --- a/src/els_code_navigation.erl +++ b/src/els_code_navigation.erl @@ -79,7 +79,7 @@ find([Uri|Uris0], Kind, Data) -> [] -> find(lists:usort(include_uris(Document) ++ Uris0), Kind, Data); Definitions -> - {ok, Uri, lists:last(Definitions)} + {ok, Uri, hd(lists:sort(Definitions))} end; {error, not_found} -> find(Uris0, Kind, Data) diff --git a/src/els_dodger.erl b/src/els_dodger.erl index 71e23a2ea..ec0fd637a 100644 --- a/src/els_dodger.erl +++ b/src/els_dodger.erl @@ -79,9 +79,9 @@ -export([parse_file/1, quick_parse_file/1, parse_file/2, quick_parse_file/2, parse/1, quick_parse/1, parse/2, - quick_parse/2, parse/3, quick_parse/3, parse_form/2, + quick_parse/2, parse/3, parse/4, quick_parse/3, parse_form/2, parse_form/3, quick_parse_form/2, quick_parse_form/3, - format_error/1, tokens_to_string/1]). + format_error/1, tokens_to_string/1, normal_parser/2]). %% The following should be: 1) pseudo-uniquely identifiable, and 2) diff --git a/src/els_parser.erl b/src/els_parser.erl index a66aa4b1c..f4663eb3b 100644 --- a/src/els_parser.erl +++ b/src/els_parser.erl @@ -20,43 +20,47 @@ %%============================================================================== -spec parse(binary()) -> {ok, [poi()]}. parse(Text) -> - Pid = els_io_string:new(Text), - parse_file(Pid). + IoDevice = els_io_string:new(Text), + parse_file(IoDevice). -spec parse_file(file:io_device()) -> {ok, [poi()]}. parse_file(IoDevice) -> - {ok, Forms} = els_dodger:parse(IoDevice, {1, 1}), - Tree = erl_syntax:form_list(Forms), - %% Reset file pointer position. - {ok, 0} = file:position(IoDevice, 0), - {ok, Extra} = parse_attribute_pois(IoDevice, [], {1, 1}), + {ok, NestedPOIs} = els_dodger:parse(IoDevice, {1, 1}, fun parse_form/3, []), ok = file:close(IoDevice), - POIs = points_of_interest(Tree), - {ok, Extra ++ POIs}. + {ok, lists:flatten(NestedPOIs)}. %%============================================================================== %% Internal Functions %%============================================================================== --spec parse_attribute_pois(io:device(), [poi()], erl_anno:location()) -> - {ok, [poi()]} | {error, any()}. -parse_attribute_pois(IoDevice, Acc, StartLoc) -> - case io:scan_erl_form(IoDevice, "", StartLoc) of - {ok, Tokens, EndLoc} -> - case erl_parse:parse_form(Tokens) of - {ok, Form} -> - POIs = find_attribute_pois(Form, Tokens), - parse_attribute_pois(IoDevice, POIs ++ Acc, EndLoc); - {error, _Error} -> - parse_attribute_pois(IoDevice, Acc, EndLoc) +%% Adapted from els_dodger +-spec parse_form(file:io_device(), any(), [any()]) -> + {'ok', erl_syntax:forms() + | none, integer()} + | {'eof', integer()} + | {'error', any(), integer()}. +parse_form(IoDevice, Location, Options) -> + parse_form(IoDevice, Location, fun els_dodger:normal_parser/2, Options). + +%% Adapted from els_dodger +-spec parse_form(file:io_device(), any(), function(), [any()]) -> + {'ok', erl_syntax:forms() | none, integer()} + | {'eof', integer()} + | {'error', any(), integer()}. +parse_form(IoDevice, Location0, Parser, _Options) -> + case io:scan_erl_form(IoDevice, "", Location0) of + {ok, Tokens, Location1} -> + try {ok, Parser(Tokens, undefined)} of + {ok, F} -> + POIs = [find_attribute_pois(F, Tokens), points_of_interest(F)], + {ok, POIs, Location1} + catch + _:_ -> + {ok, [], Location1} end; - {eof, _} -> - {ok, Acc}; - {error, ErrorInfo, EndLoc} -> - lager:warning( "Could not parse extra information [end_loc=p] ~p" - , [EndLoc, ErrorInfo] - ), - {ok, Acc} + {error, _IoErr, _Location1} = Err -> Err; + {error, _Reason} -> {eof, Location0}; + {eof, _Location1} = Eof -> Eof end. -spec find_attribute_pois(erl_parse:abstract_form(), [erl_scan:token()]) -> @@ -64,7 +68,7 @@ parse_attribute_pois(IoDevice, Acc, StartLoc) -> find_attribute_pois(Form, Tokens) -> case erl_syntax:type(Form) of attribute -> - case erl_syntax_lib:analyze_attribute(Form) of + try erl_syntax_lib:analyze_attribute(Form) of {export, Exports} -> %% The first atom is the attribute name, so we skip it. [_|Atoms] = [T|| {atom, _, _} = T <- Tokens], @@ -78,8 +82,9 @@ find_attribute_pois(Form, Tokens) -> || {{F, A}, {atom, Pos, _}} <- lists:zip(Imports, Atoms)]; {spec, {spec, {_, FTs}}} -> lists:flatten([find_spec_points_of_interest(FT) || FT <- FTs]); - _ -> - [] + _ -> [] + catch + error:syntax_error -> [] end; _ -> [] @@ -114,11 +119,8 @@ do_find_spec_points_of_interest(Tree, Acc) -> -spec points_of_interest(tree()) -> [poi()]. points_of_interest(Tree) -> - lists:flatten( - erl_syntax_lib:fold( - fun(T, Acc) -> - [do_points_of_interest(T)|Acc] - end, [], Tree)). + FoldFun = fun(T, Acc) -> [do_points_of_interest(T), Acc] end, + erl_syntax_lib:fold(FoldFun, [], Tree). %% @edoc Return the list of points of interest for a given `Tree`. -spec do_points_of_interest(tree()) -> [poi()]. diff --git a/test/els_document_symbol_SUITE.erl b/test/els_document_symbol_SUITE.erl index e760daa84..e2b476e4b 100644 --- a/test/els_document_symbol_SUITE.erl +++ b/test/els_document_symbol_SUITE.erl @@ -78,7 +78,7 @@ functions(Config) -> } || {Name, {FromL, FromC}, {ToL, ToC}} <- lists:append([functions()])], ?assertEqual(length(Expected), length(Symbols)), - Pairs = lists:zip(Expected, Symbols), + Pairs = lists:zip(lists:sort(Expected), lists:sort(Symbols)), [?assertEqual(E, S) || {E, S} <- Pairs], ok.