Skip to content

Commit 50edab4

Browse files
committed
Improvements to extract function
* Ignore variables inside funs() and list comprehensions * Don't suggest to extract function unless it contains more than one poi
1 parent 04a32dd commit 50edab4

6 files changed

+66
-30
lines changed

apps/els_core/src/els_poi.erl

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
| include
4545
| include_lib
4646
| keyword_expr
47+
| list_comp
4748
| macro
4849
| module
4950
| nifs

apps/els_core/src/els_text.erl

+14-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
split_at_line/2,
1313
tokens/1,
1414
tokens/2,
15-
apply_edits/2
15+
apply_edits/2,
16+
is_keyword_expr/1
1617
]).
1718
-export([strip_comments/1]).
1819

@@ -176,6 +177,18 @@ strip_comments(Text) ->
176177
)
177178
).
178179

180+
-spec is_keyword_expr(binary()) -> boolean().
181+
is_keyword_expr(Text) ->
182+
lists:member(Text, [
183+
<<"begin">>,
184+
<<"case">>,
185+
<<"fun">>,
186+
<<"if">>,
187+
<<"maybe">>,
188+
<<"receive">>,
189+
<<"try">>
190+
]).
191+
179192
%%==============================================================================
180193
%% Internal functions
181194
%%==============================================================================

apps/els_lsp/src/els_code_actions.erl

+8-2
Original file line numberDiff line numberDiff line change
@@ -421,11 +421,17 @@ fix_atom_typo(Uri, Range, _Data, [Atom]) ->
421421
-spec extract_function(uri(), range()) -> [map()].
422422
extract_function(Uri, Range) ->
423423
{ok, [Document]} = els_dt_document:lookup(Uri),
424-
#{from := From = {Line, Column}, to := To} = els_range:to_poi_range(Range),
424+
PoiRange = els_range:to_poi_range(Range),
425+
#{from := From = {Line, Column}, to := To} = PoiRange,
425426
%% We only want to extract if selection is large enough
426427
%% and cursor is inside a function
428+
POIsInRange = els_dt_document:pois_in_range(Document, PoiRange),
429+
#{text := Text} = Document,
430+
MarkedText = els_text:range(Text, From, To),
427431
case
428-
large_enough_range(From, To) andalso
432+
(length(POIsInRange) > 1 orelse
433+
els_text:is_keyword_expr(MarkedText)) andalso
434+
large_enough_range(From, To) andalso
429435
not contains_function_clause(Document, Line) andalso
430436
els_dt_document:wrapping_functions(Document, Line, Column) /= []
431437
of

apps/els_lsp/src/els_dt_document.erl

+11
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
new/4,
3131
pois/1,
3232
pois/2,
33+
pois_in_range/2,
3334
pois_in_range/3,
3435
get_element_at_pos/3,
3536
uri/1,
@@ -213,6 +214,16 @@ pois(#{pois := POIs}) ->
213214
pois(Item, Kinds) ->
214215
[POI || #{kind := K} = POI <- pois(Item), lists:member(K, Kinds)].
215216

217+
%% @doc Returns the list of POIs of the given types in the given range
218+
%% for the current document
219+
-spec pois_in_range(item(), els_poi:poi_range()) -> [els_poi:poi()].
220+
pois_in_range(Item, Range) ->
221+
[
222+
POI
223+
|| #{range := R} = POI <- pois(Item),
224+
els_range:in(R, Range)
225+
].
226+
216227
%% @doc Returns the list of POIs of the given types in the given range
217228
%% for the current document
218229
-spec pois_in_range(

apps/els_lsp/src/els_execute_command_provider.erl

+30-26
Original file line numberDiff line numberDiff line change
@@ -346,33 +346,49 @@ end_symbol(ExtractString) ->
346346
non_neg_integer()
347347
) -> [string()].
348348
get_args(PoiRange, Document, FromL, FunBeginLine) ->
349-
%% TODO: Possible improvement. To make this bullet proof we should
350-
%% ignore vars defined inside LCs and funs()
351-
VarPOIs = els_poi:sort(els_dt_document:pois(Document, [variable])),
352349
BeforeRange = #{from => {FunBeginLine, 1}, to => {FromL, 1}},
353-
VarsBefore = ids_in_range(BeforeRange, VarPOIs),
354-
VarsInside = ids_in_range(PoiRange, VarPOIs),
350+
VarPOIsBefore = els_dt_document:pois_in_range(Document, [variable], BeforeRange),
351+
%% Remove all variables inside LCs or keyword expressions
352+
LCPOIs = els_dt_document:pois(Document, [list_comp]),
353+
FunExprPOIs = [
354+
POI
355+
|| #{id := fun_expr} = POI <- els_dt_document:pois(Document, [keyword_expr])
356+
],
357+
%% Only consider fun exprs that doesn't contain the selected range
358+
ExcludePOIs = [
359+
POI
360+
|| #{range := R} = POI <- FunExprPOIs ++ LCPOIs, not els_range:in(PoiRange, R)
361+
],
362+
VarsBefore = [
363+
Id
364+
|| #{range := VarRange, id := Id} <- els_poi:sort(VarPOIsBefore),
365+
not_in_any_range(VarRange, ExcludePOIs)
366+
],
367+
%% Find all variables defined before the current function that are used
368+
%% inside the selected range.
369+
VarPOIsInside = els_dt_document:pois_in_range(Document, [variable], PoiRange),
355370
els_utils:uniq([
356371
atom_to_list(Id)
357-
|| Id <- VarsInside,
372+
|| #{id := Id} <- VarPOIsInside,
358373
lists:member(Id, VarsBefore)
359374
]).
360375

361-
-spec ids_in_range(els_poi:poi_range(), [els_poi:poi()]) -> [atom()].
362-
ids_in_range(PoiRange, VarPOIs) ->
363-
[
364-
Id
365-
|| #{range := R, id := Id} <- VarPOIs,
366-
els_range:in(R, PoiRange)
367-
].
376+
-spec not_in_any_range(els_poi:poi_range(), [els_poi:poi()]) -> boolean().
377+
not_in_any_range(VarRange, POIs) ->
378+
not lists:any(
379+
fun(#{range := Range}) ->
380+
els_range:in(VarRange, Range)
381+
end,
382+
POIs
383+
).
368384

369385
-spec extract_range(els_dt_document:item(), range()) -> els_poi:poi_range().
370386
extract_range(#{text := Text} = Document, Range) ->
371387
PoiRange = els_range:to_poi_range(Range),
372388
#{from := {CurrL, CurrC} = From, to := To} = PoiRange,
373389
POIs = els_dt_document:get_element_at_pos(Document, CurrL, CurrC),
374390
MarkedText = els_text:range(Text, From, To),
375-
case is_keyword_expr(MarkedText) of
391+
case els_text:is_keyword_expr(MarkedText) of
376392
true ->
377393
case sort_by_range_size([P || #{kind := keyword_expr} = P <- POIs]) of
378394
[] ->
@@ -384,18 +400,6 @@ extract_range(#{text := Text} = Document, Range) ->
384400
PoiRange
385401
end.
386402

387-
-spec is_keyword_expr(binary()) -> boolean().
388-
is_keyword_expr(Text) ->
389-
lists:member(Text, [
390-
<<"begin">>,
391-
<<"case">>,
392-
<<"fun">>,
393-
<<"if">>,
394-
<<"maybe">>,
395-
<<"receive">>,
396-
<<"try">>
397-
]).
398-
399403
-spec sort_by_range_size(_) -> _.
400404
sort_by_range_size(POIs) ->
401405
lists:sort([{range_size(P), P} || P <- POIs]).

apps/els_lsp/src/els_parser.erl

+2-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ do_points_of_interest(Tree) ->
276276
Type == implicit_fun;
277277
Type == maybe_expr;
278278
Type == receive_expr;
279-
Type == try_expr
279+
Type == try_expr;
280+
Type == fun_expr
280281
->
281282
keyword_expr(Type, Tree);
282283
_Other ->

0 commit comments

Comments
 (0)