diff --git a/apps/els_core/src/els_text.erl b/apps/els_core/src/els_text.erl index 041fd557..d6cec3a0 100644 --- a/apps/els_core/src/els_text.erl +++ b/apps/els_core/src/els_text.erl @@ -11,6 +11,7 @@ range/3, split_at_line/2, tokens/1, + tokens/2, apply_edits/2 ]). -export([strip_comments/1]). @@ -71,6 +72,27 @@ tokens(Text) -> {error, _, _} -> [] end. +-spec tokens(text(), {integer(), integer()}) -> [any()]. +tokens(Text, Pos) -> + case erl_scan:string(els_utils:to_list(Text), Pos) of + {ok, Tokens, _} -> + [unpack_anno(T) || T <- Tokens]; + {error, _, _} -> + [] + end. + +-spec unpack_anno(erl_scan:token()) -> + {Category :: atom(), Pos :: {integer(), integer()}, Symbol :: any()} + | {Category :: atom(), Pos :: {integer(), integer()}}. +unpack_anno({Category, Anno, Symbol}) -> + Line = erl_anno:line(Anno), + Column = erl_anno:column(Anno), + {Category, {Line, Column}, Symbol}; +unpack_anno({Category, Anno}) -> + Line = erl_anno:line(Anno), + Column = erl_anno:column(Anno), + {Category, {Line, Column}}. + %% @doc Extract the last token from the given text. -spec last_token(text()) -> token() | {error, empty}. last_token(Text) -> diff --git a/apps/els_lsp/src/els_code_navigation.erl b/apps/els_lsp/src/els_code_navigation.erl index b383e6dd..e037a3f1 100644 --- a/apps/els_lsp/src/els_code_navigation.erl +++ b/apps/els_lsp/src/els_code_navigation.erl @@ -144,7 +144,9 @@ goto_definition(Uri, #{kind := callback, id := Id}) -> goto_definition(_Filename, _) -> {error, not_found}. --spec is_imported_bif(uri(), atom(), non_neg_integer()) -> boolean(). +-spec is_imported_bif(uri(), atom(), non_neg_integer() | any_arity) -> boolean(). +is_imported_bif(_Uri, _F, any_arity) -> + false; is_imported_bif(_Uri, F, A) -> OldBif = erl_internal:old_bif(F, A), Bif = erl_internal:bif(F, A), diff --git a/apps/els_lsp/src/els_definition_provider.erl b/apps/els_lsp/src/els_definition_provider.erl index 060cc582..0a27c48c 100644 --- a/apps/els_lsp/src/els_definition_provider.erl +++ b/apps/els_lsp/src/els_definition_provider.erl @@ -73,9 +73,87 @@ goto_definition(Uri, [POI | Rest]) -> end. -spec match_incomplete(binary(), pos()) -> [els_poi:poi()]. -match_incomplete(Text, Pos) -> +match_incomplete(Text, {Line, Col} = Pos) -> %% Try parsing subsets of text to find a matching POI at Pos - match_after(Text, Pos) ++ match_line(Text, Pos). + case match_after(Text, Pos) ++ match_line(Text, Pos) of + [] -> + %% Still found nothing, let's analyze the tokens to kludge a POI + LineText = els_text:line(Text, Line), + Tokens = els_text:tokens(LineText, {Line, 1}), + kludge_match(Tokens, {Line, Col + 1}); + POIs -> + POIs + end. + +-spec kludge_match([any()], pos()) -> [els_poi:poi()]. +kludge_match([], _Pos) -> + []; +kludge_match( + [ + {atom, {FromL, FromC}, Module}, + {':', _}, + {atom, _, Function}, + {'(', {ToL, ToC}} + | _ + ], + {_, C} +) when + FromC =< C, C < ToC +-> + %% Match mod:fun( + Range = #{from => {FromL, FromC}, to => {ToL, ToC}}, + POI = els_poi:new(Range, application, {Module, Function, any_arity}), + [POI]; +kludge_match([{atom, {FromL, FromC}, Function}, {'(', {ToL, ToC}} | _], {_, C}) when + FromC =< C, C < ToC +-> + %% Match fun( + Range = #{from => {FromL, FromC}, to => {ToL, ToC}}, + POI = els_poi:new(Range, application, {Function, any_arity}), + [POI]; +kludge_match([{'#', _}, {atom, {FromL, FromC}, Record} | T], {_, C} = Pos) when + FromC =< C +-> + %% Match #record + ToC = FromC + length(atom_to_list(Record)), + case C =< ToC of + true -> + Range = #{from => {FromL, FromC}, to => {FromL, ToC}}, + POI = els_poi:new(Range, record_expr, Record), + [POI]; + false -> + kludge_match(T, Pos) + end; +kludge_match([{'?', _}, {VarOrAtom, {FromL, FromC}, Macro} | T], {_, C} = Pos) when + FromC =< C, (VarOrAtom == var orelse VarOrAtom == atom) +-> + %% Match ?MACRO + ToC = FromC + length(atom_to_list(Macro)), + case C =< ToC of + true -> + %% Match fun( + Range = #{from => {FromL, FromC}, to => {FromL, ToC}}, + POI = els_poi:new(Range, macro, Macro), + [POI]; + false -> + kludge_match(T, Pos) + end; +kludge_match([{atom, {FromL, FromC}, Atom} | T], {_, C} = Pos) when + FromC =< C +-> + %% Match atom + ToC = FromC + length(atom_to_list(Atom)), + case C =< ToC of + true -> + Range = #{from => {FromL, FromC}, to => {FromL, ToC}}, + POI = els_poi:new(Range, atom, Atom), + [POI]; + false -> + kludge_match(T, Pos) + end; +kludge_match([_ | T], Pos) -> + %% TODO: Add more kludges here + kludge_match(T, Pos). -spec match_after(binary(), pos()) -> [els_poi:poi()]. match_after(Text, {Line, Character}) ->