Skip to content

Commit 3342c36

Browse files
authored
Add support for bump variables code action (#1553)
1 parent 0a15a75 commit 3342c36

File tree

3 files changed

+110
-3
lines changed

3 files changed

+110
-3
lines changed

apps/els_lsp/src/els_code_action_provider.erl

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ code_actions(Uri, Range, #{<<"diagnostics">> := Diagnostics}) ->
3333
lists:usort(
3434
lists:flatten([make_code_actions(Uri, D) || D <- Diagnostics]) ++
3535
wrangler_handler:get_code_actions(Uri, Range) ++
36-
els_code_actions:extract_function(Uri, Range)
36+
els_code_actions:extract_function(Uri, Range) ++
37+
els_code_actions:bump_variables(Uri, Range)
3738
).
3839

3940
-spec make_code_actions(uri(), map()) -> [map()].

apps/els_lsp/src/els_code_actions.erl

+40-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
add_include_lib_record/4,
1717
suggest_macro/4,
1818
suggest_record/4,
19-
suggest_record_field/4
19+
suggest_record_field/4,
20+
bump_variables/2
2021
]).
2122

2223
-include("els_lsp.hrl").
@@ -440,6 +441,36 @@ extract_function(Uri, Range) ->
440441
[]
441442
end.
442443

444+
-spec bump_variables(uri(), range()) -> [map()].
445+
bump_variables(Uri, Range) ->
446+
{ok, Document} = els_utils:lookup_document(Uri),
447+
#{from := {Line, Column}} = els_range:to_poi_range(Range),
448+
POIs = els_dt_document:get_element_at_pos(Document, Line, Column),
449+
case [POI || #{kind := variable} = POI <- POIs] of
450+
[] ->
451+
[];
452+
[#{id := Id, range := PoiRange} = _POI | _] ->
453+
Name = atom_to_binary(Id),
454+
case ends_with_digit(Name) of
455+
false ->
456+
[];
457+
true ->
458+
VarRange = els_protocol:range(PoiRange),
459+
[
460+
#{
461+
title => <<"Bump variables: ", Name/binary>>,
462+
kind => ?CODE_ACTION_KIND_QUICKFIX,
463+
command => make_bump_variables_command(VarRange, Uri, Name)
464+
}
465+
]
466+
end
467+
end.
468+
469+
-spec ends_with_digit(binary()) -> boolean().
470+
ends_with_digit(Bin) ->
471+
N = binary:last(Bin),
472+
$0 =< N andalso N =< $9.
473+
443474
-spec make_extract_function_command(range(), uri()) -> map().
444475
make_extract_function_command(Range, Uri) ->
445476
els_command:make_command(
@@ -448,6 +479,14 @@ make_extract_function_command(Range, Uri) ->
448479
[#{uri => Uri, range => Range}]
449480
).
450481

482+
-spec make_bump_variables_command(range(), uri(), binary()) -> map().
483+
make_bump_variables_command(Range, Uri, Name) ->
484+
els_command:make_command(
485+
<<"Bump variables">>,
486+
<<"bump-variables">>,
487+
[#{uri => Uri, range => Range, name => Name}]
488+
).
489+
451490
-spec contains_function_clause(
452491
els_dt_document:item(),
453492
non_neg_integer()

apps/els_lsp/src/els_execute_command_provider.erl

+68-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ options() ->
2525
<<"suggest-spec">>,
2626
<<"function-references">>,
2727
<<"refactor.extract">>,
28-
<<"add-behaviour-callbacks">>
28+
<<"add-behaviour-callbacks">>,
29+
<<"bump-variables">>
2930
],
3031
#{
3132
commands => [
@@ -115,6 +116,15 @@ execute_command(<<"refactor.extract">>, [
115116
]) ->
116117
ok = extract_function(Uri, Range),
117118
[];
119+
execute_command(<<"bump-variables">>, [
120+
#{
121+
<<"uri">> := Uri,
122+
<<"range">> := Range,
123+
<<"name">> := Name
124+
}
125+
]) ->
126+
ok = bump_variables(Uri, Range, Name),
127+
[];
118128
execute_command(<<"add-behaviour-callbacks">>, [
119129
#{
120130
<<"uri">> := Uri,
@@ -206,6 +216,63 @@ execute_command(Command, Arguments) ->
206216
end,
207217
[].
208218

219+
-spec bump_variables(uri(), range(), binary()) -> ok.
220+
bump_variables(Uri, Range, VarName) ->
221+
{Name, Number} = split_variable(VarName),
222+
{ok, Document} = els_utils:lookup_document(Uri),
223+
VarPOIs = els_poi:sort(els_dt_document:pois(Document, [variable])),
224+
VarRange = els_range:to_poi_range(Range),
225+
ScopeRange = els_scope:variable_scope_range(VarRange, Document),
226+
Changes =
227+
[
228+
bump_variable_change(POI)
229+
|| POI <- pois_in(VarPOIs, ScopeRange),
230+
should_bump_variable(POI, Name, Number)
231+
],
232+
Method = <<"workspace/applyEdit">>,
233+
Params = #{edit => #{changes => #{Uri => Changes}}},
234+
els_server:send_request(Method, Params).
235+
236+
-spec should_bump_variable(els_poi:poi(), binary(), binary()) -> boolean().
237+
should_bump_variable(#{id := Id}, Name, Number) ->
238+
case split_variable(Id) of
239+
{PName, PNumber} when PName == Name ->
240+
binary_to_integer(PNumber) >= binary_to_integer(Number);
241+
_ ->
242+
false
243+
end.
244+
245+
-spec bump_variable_change(els_poi:poi()) -> map().
246+
bump_variable_change(#{id := Id, range := PoiRange}) ->
247+
{Name, Number} = split_variable(Id),
248+
NewNumber = integer_to_binary(binary_to_integer(Number) + 1),
249+
NewId = binary_to_atom(<<Name/binary, NewNumber/binary>>, utf8),
250+
#{
251+
newText => NewId,
252+
range => els_protocol:range(PoiRange)
253+
}.
254+
255+
-spec pois_in([els_poi:poi()], els_poi:poi_range()) ->
256+
[els_poi:poi()].
257+
pois_in(POIs, Range) ->
258+
[POI || #{range := R} = POI <- POIs, els_range:in(R, Range)].
259+
260+
-spec split_variable(atom() | binary() | list()) -> {binary(), binary()} | error.
261+
split_variable(Name) when is_atom(Name) ->
262+
split_variable(atom_to_list(Name));
263+
split_variable(Name) when is_binary(Name) ->
264+
split_variable(unicode:characters_to_list(Name));
265+
split_variable(Name) when is_list(Name) ->
266+
split_variable(lists:reverse(Name), []).
267+
268+
-spec split_variable(string(), string()) -> {binary(), binary()} | error.
269+
split_variable([H | T], Acc) when $0 =< H, H =< $9 ->
270+
split_variable(T, [H | Acc]);
271+
split_variable(_Name, []) ->
272+
error;
273+
split_variable(Name, Acc) ->
274+
{list_to_binary(lists:reverse(Name)), list_to_binary(Acc)}.
275+
209276
-spec extract_function(uri(), range()) -> ok.
210277
extract_function(Uri, Range) ->
211278
{ok, [#{text := Text} = Document]} = els_dt_document:lookup(Uri),

0 commit comments

Comments
 (0)