Skip to content

Commit d38dc66

Browse files
authored
Add suggest code actions for undefined record and record fields (#1539)
1 parent acd7f80 commit d38dc66

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-module(undefined_record_suggest).
2+
3+
-record(foobar, {foobar}).
4+
5+
function_a(R) ->
6+
#foo_bar{} = R,
7+
R#foobar.foobaz.

apps/els_lsp/src/els_code_action_provider.erl

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ make_code_actions(
5252
{"undefined macro '(.*)'", fun els_code_actions:suggest_macro/4},
5353
{"record (.*) undefined", fun els_code_actions:add_include_lib_record/4},
5454
{"record (.*) undefined", fun els_code_actions:define_record/4},
55+
{"record (.*) undefined", fun els_code_actions:suggest_record/4},
56+
{"field (.*) undefined in record (.*)", fun els_code_actions:suggest_record_field/4},
5557
{"Module name '(.*)' does not match file name '(.*)'",
5658
fun els_code_actions:fix_module_name/4},
5759
{"Unused macro: (.*)", fun els_code_actions:remove_macro/4},

apps/els_lsp/src/els_code_actions.erl

+51-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
define_record/4,
1515
add_include_lib_macro/4,
1616
add_include_lib_record/4,
17-
suggest_macro/4
17+
suggest_macro/4,
18+
suggest_record/4,
19+
suggest_record_field/4
1820
]).
1921

2022
-include("els_lsp.hrl").
@@ -294,6 +296,54 @@ suggest_macro(Uri, Range, _Data, [Macro]) ->
294296
Distance > 0.8
295297
].
296298

299+
-spec suggest_record(uri(), range(), binary(), [binary()]) -> [map()].
300+
suggest_record(Uri, Range, _Data, [Record]) ->
301+
%% Supply a quickfix to replace an unrecognized record with the most similar
302+
%% record in scope.
303+
{ok, Document} = els_utils:lookup_document(Uri),
304+
POIs = els_scope:local_and_included_pois(Document, [record]),
305+
RecordsInScope = [atom_to_binary(Id) || #{id := Id} <- POIs, is_atom(Id)],
306+
Distances =
307+
[{els_utils:jaro_distance(Rec, Record), Rec} || Rec <- RecordsInScope, Rec =/= Record],
308+
[
309+
make_edit_action(
310+
Uri,
311+
<<"Did you mean #", Rec/binary, "{}?">>,
312+
?CODE_ACTION_KIND_QUICKFIX,
313+
<<"#", Rec/binary>>,
314+
Range
315+
)
316+
|| {Distance, Rec} <- lists:reverse(lists:usort(Distances)),
317+
Distance > 0.8
318+
].
319+
320+
-spec suggest_record_field(uri(), range(), binary(), [binary()]) -> [map()].
321+
suggest_record_field(Uri, Range, _Data, [Field, Record]) ->
322+
%% Supply a quickfix to replace an unrecognized record field with the most
323+
%% similar record field in Record.
324+
{ok, Document} = els_utils:lookup_document(Uri),
325+
POIs = els_scope:local_and_included_pois(Document, [record]),
326+
RecordId = binary_to_atom(Record, utf8),
327+
Fields = [
328+
atom_to_binary(F)
329+
|| #{id := Id, data := #{field_list := Fs}} <- POIs,
330+
F <- Fs,
331+
Id =:= RecordId
332+
],
333+
Distances =
334+
[{els_utils:jaro_distance(F, Field), F} || F <- Fields, F =/= Field],
335+
[
336+
make_edit_action(
337+
Uri,
338+
<<"Did you mean #", Record/binary, ".", F/binary, "?">>,
339+
?CODE_ACTION_KIND_QUICKFIX,
340+
<<F/binary>>,
341+
Range
342+
)
343+
|| {Distance, F} <- lists:reverse(lists:usort(Distances)),
344+
Distance > 0.8
345+
].
346+
297347
-spec fix_module_name(uri(), range(), binary(), [binary()]) -> [map()].
298348
fix_module_name(Uri, Range0, _Data, [ModName, FileName]) ->
299349
{ok, Document} = els_utils:lookup_document(Uri),

apps/els_lsp/test/els_code_action_SUITE.erl

+57-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
define_macro/1,
2828
define_macro_with_args/1,
2929
suggest_macro/1,
30-
undefined_record/1
30+
undefined_record/1,
31+
undefined_record_suggest/1
3132
]).
3233

3334
%%==============================================================================
@@ -736,3 +737,58 @@ undefined_record(Config) ->
736737
],
737738
?assertEqual(Expected, Result),
738739
ok.
740+
741+
-spec undefined_record_suggest(config()) -> ok.
742+
undefined_record_suggest(Config) ->
743+
Uri = ?config(undefined_record_suggest_uri, Config),
744+
%% Don't care
745+
Range = els_protocol:range(#{
746+
from => {5, 4},
747+
to => {5, 11}
748+
}),
749+
DestRange = els_protocol:range(#{
750+
from => {4, 1},
751+
to => {4, 1}
752+
}),
753+
Diag = #{
754+
message => <<"record foo_bar undefined">>,
755+
range => Range,
756+
severity => 2,
757+
source => <<"Compiler">>
758+
},
759+
#{result := Result} =
760+
els_client:document_codeaction(Uri, Range, [Diag]),
761+
Changes1 =
762+
#{
763+
binary_to_atom(Uri, utf8) =>
764+
[
765+
#{
766+
range => Range,
767+
newText => <<"#foobar">>
768+
}
769+
]
770+
},
771+
Changes2 =
772+
#{
773+
binary_to_atom(Uri, utf8) =>
774+
[
775+
#{
776+
range => DestRange,
777+
newText => <<"-record(foo_bar, {}).\n">>
778+
}
779+
]
780+
},
781+
Expected = [
782+
#{
783+
edit => #{changes => Changes1},
784+
kind => <<"quickfix">>,
785+
title => <<"Did you mean #foobar{}?">>
786+
},
787+
#{
788+
edit => #{changes => Changes2},
789+
kind => <<"quickfix">>,
790+
title => <<"Define record foo_bar">>
791+
}
792+
],
793+
?assertEqual(Expected, Result),
794+
ok.

0 commit comments

Comments
 (0)