Skip to content

Commit

Permalink
Go to definition can now handle lines where parsing fails
Browse files Browse the repository at this point in the history
Use tokens to generate POIs on lines where parsing fails.
Currently handling call(), module:call(), ?MACRO, #record, atom.
  • Loading branch information
plux committed Sep 26, 2024
1 parent eeec8ef commit 4db8783
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 3 deletions.
8 changes: 8 additions & 0 deletions apps/els_core/src/els_text.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
range/3,
split_at_line/2,
tokens/1,
tokens/2,
apply_edits/2
]).
-export([strip_comments/1]).
Expand Down Expand Up @@ -71,6 +72,13 @@ 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, _} -> Tokens;
{error, _, _} -> []
end.

%% @doc Extract the last token from the given text.
-spec last_token(text()) -> token() | {error, empty}.
last_token(Text) ->
Expand Down
4 changes: 3 additions & 1 deletion apps/els_lsp/src/els_code_navigation.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
82 changes: 80 additions & 2 deletions apps/els_lsp/src/els_definition_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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(_, _) -> _.
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}) ->
Expand Down

0 comments on commit 4db8783

Please sign in to comment.