Skip to content

Commit 4db8783

Browse files
committed
Go to definition can now handle lines where parsing fails
Use tokens to generate POIs on lines where parsing fails. Currently handling call(), module:call(), ?MACRO, #record, atom.
1 parent eeec8ef commit 4db8783

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

apps/els_core/src/els_text.erl

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
range/3,
1212
split_at_line/2,
1313
tokens/1,
14+
tokens/2,
1415
apply_edits/2
1516
]).
1617
-export([strip_comments/1]).
@@ -71,6 +72,13 @@ tokens(Text) ->
7172
{error, _, _} -> []
7273
end.
7374

75+
-spec tokens(text(), {integer(), integer()}) -> [any()].
76+
tokens(Text, Pos) ->
77+
case erl_scan:string(els_utils:to_list(Text), Pos) of
78+
{ok, Tokens, _} -> Tokens;
79+
{error, _, _} -> []
80+
end.
81+
7482
%% @doc Extract the last token from the given text.
7583
-spec last_token(text()) -> token() | {error, empty}.
7684
last_token(Text) ->

apps/els_lsp/src/els_code_navigation.erl

+3-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@ goto_definition(Uri, #{kind := callback, id := Id}) ->
144144
goto_definition(_Filename, _) ->
145145
{error, not_found}.
146146

147-
-spec is_imported_bif(uri(), atom(), non_neg_integer()) -> boolean().
147+
-spec is_imported_bif(uri(), atom(), non_neg_integer() | any_arity) -> boolean().
148+
is_imported_bif(_Uri, _F, any_arity) ->
149+
false;
148150
is_imported_bif(_Uri, F, A) ->
149151
OldBif = erl_internal:old_bif(F, A),
150152
Bif = erl_internal:bif(F, A),

apps/els_lsp/src/els_definition_provider.erl

+80-2
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,87 @@ goto_definition(Uri, [POI | Rest]) ->
7373
end.
7474

7575
-spec match_incomplete(binary(), pos()) -> [els_poi:poi()].
76-
match_incomplete(Text, Pos) ->
76+
match_incomplete(Text, {Line, Col} = Pos) ->
7777
%% Try parsing subsets of text to find a matching POI at Pos
78-
match_after(Text, Pos) ++ match_line(Text, Pos).
78+
case match_after(Text, Pos) ++ match_line(Text, Pos) of
79+
[] ->
80+
%% Still found nothing, let's analyze the tokens to kludge a POI
81+
LineText = els_text:line(Text, Line),
82+
Tokens = els_text:tokens(LineText, {Line, 1}),
83+
kludge_match(Tokens, {Line, Col + 1});
84+
POIs ->
85+
POIs
86+
end.
87+
88+
-spec kludge_match(_, _) -> _.
89+
kludge_match([], _Pos) ->
90+
[];
91+
kludge_match(
92+
[
93+
{atom, {FromL, FromC}, Module},
94+
{':', _},
95+
{atom, _, Function},
96+
{'(', {ToL, ToC}}
97+
| _
98+
],
99+
{_, C}
100+
) when
101+
FromC =< C, C < ToC
102+
->
103+
%% Match mod:fun(
104+
Range = #{from => {FromL, FromC}, to => {ToL, ToC}},
105+
POI = els_poi:new(Range, application, {Module, Function, any_arity}),
106+
[POI];
107+
kludge_match([{atom, {FromL, FromC}, Function}, {'(', {ToL, ToC}} | _], {_, C}) when
108+
FromC =< C, C < ToC
109+
->
110+
%% Match fun(
111+
Range = #{from => {FromL, FromC}, to => {ToL, ToC}},
112+
POI = els_poi:new(Range, application, {Function, any_arity}),
113+
[POI];
114+
kludge_match([{'#', _}, {atom, {FromL, FromC}, Record} | T], {_, C} = Pos) when
115+
FromC =< C
116+
->
117+
%% Match #record
118+
ToC = FromC + length(atom_to_list(Record)),
119+
case C =< ToC of
120+
true ->
121+
Range = #{from => {FromL, FromC}, to => {FromL, ToC}},
122+
POI = els_poi:new(Range, record_expr, Record),
123+
[POI];
124+
false ->
125+
kludge_match(T, Pos)
126+
end;
127+
kludge_match([{'?', _}, {VarOrAtom, {FromL, FromC}, Macro} | T], {_, C} = Pos) when
128+
FromC =< C, (VarOrAtom == var orelse VarOrAtom == atom)
129+
->
130+
%% Match ?MACRO
131+
ToC = FromC + length(atom_to_list(Macro)),
132+
case C =< ToC of
133+
true ->
134+
%% Match fun(
135+
Range = #{from => {FromL, FromC}, to => {FromL, ToC}},
136+
POI = els_poi:new(Range, macro, Macro),
137+
[POI];
138+
false ->
139+
kludge_match(T, Pos)
140+
end;
141+
kludge_match([{atom, {FromL, FromC}, Atom} | T], {_, C} = Pos) when
142+
FromC =< C
143+
->
144+
%% Match atom
145+
ToC = FromC + length(atom_to_list(Atom)),
146+
case C =< ToC of
147+
true ->
148+
Range = #{from => {FromL, FromC}, to => {FromL, ToC}},
149+
POI = els_poi:new(Range, atom, Atom),
150+
[POI];
151+
false ->
152+
kludge_match(T, Pos)
153+
end;
154+
kludge_match([_ | T], Pos) ->
155+
%% TODO: Add more kludges here
156+
kludge_match(T, Pos).
79157

80158
-spec match_after(binary(), pos()) -> [els_poi:poi()].
81159
match_after(Text, {Line, Character}) ->

0 commit comments

Comments
 (0)