Skip to content

Commit 316ef5b

Browse files
committed
Sheldon diagnostics
1 parent 210b871 commit 316ef5b

8 files changed

+189
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ doc
2929
# Emacs temporary files
3030
.#*
3131
*#
32+
rebar3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
%% Trigger some "somestrange" warnings
2+
3+
%% Trigger some "sheldon" warnings
4+
-module(diagnostics_sheldon).

apps/els_lsp/src/els_diagnostics.erl

+13-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ available_diagnostics() ->
6363
, <<"dialyzer">>
6464
, <<"gradualizer">>
6565
, <<"elvis">>
66+
, <<"sheldon">>
6667
, <<"unused_includes">>
6768
, <<"unused_macros">>
6869
, <<"unused_record_fields">>
@@ -78,7 +79,9 @@ enabled_diagnostics() ->
7879
Default = default_diagnostics(),
7980
Enabled = maps:get("enabled", Config, []),
8081
Disabled = maps:get("disabled", Config, []),
81-
lists:usort((Default ++ valid(Enabled)) -- valid(Disabled)).
82+
Diagnostics = lists:usort((Default ++ valid(Enabled)) -- valid(Disabled)),
83+
ok = extra(Diagnostics),
84+
Diagnostics.
8285

8386
-spec make_diagnostic(range(), binary(), severity(), binary()) -> diagnostic().
8487
make_diagnostic(Range, Message, Severity, Source) ->
@@ -146,3 +149,12 @@ valid(Ids0) ->
146149
})
147150
end,
148151
Valid.
152+
153+
-spec extra(list()) -> ok.
154+
extra([]) ->
155+
ok;
156+
extra([<<"sheldon">>|_]) ->
157+
{ok, _} = application:ensure_all_started(rebar3_sheldon),
158+
ok;
159+
extra([_|T]) ->
160+
extra(T).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
%%==============================================================================
2+
%% Compiler diagnostics
3+
%%==============================================================================
4+
5+
-module(els_sheldon_diagnostics).
6+
7+
%%==============================================================================
8+
%% Behaviours
9+
%%==============================================================================
10+
11+
-behaviour(els_diagnostics).
12+
13+
%%==============================================================================
14+
%% Exports
15+
%%==============================================================================
16+
17+
-export([ is_default/0
18+
, run/1
19+
, source/0
20+
]).
21+
22+
%%==============================================================================
23+
%% Includes
24+
%%==============================================================================
25+
26+
-include("els_lsp.hrl").
27+
-include_lib("kernel/include/logger.hrl").
28+
29+
%%==============================================================================
30+
%% Callback Functions
31+
%%==============================================================================
32+
33+
-spec is_default() -> boolean().
34+
is_default() ->
35+
false.
36+
37+
-spec run(uri()) -> [els_diagnostics:diagnostic()].
38+
run(Uri) ->
39+
case els_utils:project_relative(Uri) of
40+
{error, not_relative} -> [];
41+
RelFile ->
42+
try
43+
RegEx = "[_@./#&+-=*]",
44+
Mod = rebar3_sheldon_ast,
45+
Mod:spellcheck([RelFile], RegEx)
46+
of
47+
[] -> [];
48+
Problems -> format_diagnostics(Problems)
49+
catch _:Err ->
50+
?LOG_WARNING("Sheldon error.[Err=~p] ", [Err]),
51+
[]
52+
end
53+
end.
54+
55+
-spec source() -> binary().
56+
source() ->
57+
<<"Sheldon">>.
58+
59+
%%==============================================================================
60+
%% Internal Functions
61+
%%==============================================================================
62+
63+
-spec format_diagnostics(any()) -> [map()].
64+
format_diagnostics(Data) ->
65+
R = format_rules(Data),
66+
lists:flatten(R).
67+
68+
-spec format_rules([any()]) -> [[map()]].
69+
format_rules([]) ->
70+
[];
71+
format_rules([#{reason := #{misspelled_words := Miss}} = Data | EItems]) ->
72+
ItemDiags = format_item(Miss, Data),
73+
[lists:flatten(ItemDiags) | format_rules(EItems)].
74+
75+
-spec format_item([any()], map()) -> [[map()]].
76+
format_item([#{candidates := [], word := Word} | Items], Data) ->
77+
#{line := Line, type := Type} = Data,
78+
Msg = format_text("The word ~p in ~p is unknown.", [Word, Type]),
79+
Diagnostic = diagnostic(Msg, Line, ?DIAGNOSTIC_WARNING),
80+
[Diagnostic | format_item(Items, Data)];
81+
format_item([#{candidates := Candidates, word := Word} | Items], Data) ->
82+
#{line := Line, type := Type} = Data,
83+
FormatCandidates = format_sheldon_candidates(Candidates, []),
84+
Text = "The word ~p in ~p is unknown. Maybe you wanted to use ~ts?",
85+
Msg = format_text(Text, [Word, Type, FormatCandidates]),
86+
Diagnostic = diagnostic(Msg, Line, ?DIAGNOSTIC_WARNING),
87+
[Diagnostic | format_item(Items, Data)];
88+
format_item([], _) ->
89+
[].
90+
91+
-spec diagnostic( any(), integer(), els_diagnostics:severity()) -> [map()].
92+
diagnostic(Msg, Ln, Severity) ->
93+
Range = els_protocol:range(#{from => {Ln, 1}, to => {Ln + 1, 1}}),
94+
Message = els_utils:to_binary(Msg),
95+
[#{ range => Range
96+
, severity => Severity
97+
, code => spellcheck
98+
, source => source()
99+
, message => Message
100+
, relatedInformation => []
101+
}].
102+
103+
-spec format_sheldon_candidates([any()], [[[any()] | char()]]) -> list().
104+
format_sheldon_candidates([], Acc) ->
105+
Acc;
106+
format_sheldon_candidates([Candidate], Acc) ->
107+
[Acc, format_text("~p", [Candidate])];
108+
format_sheldon_candidates([Candidate | T], Acc) ->
109+
format_sheldon_candidates(T, [Acc, format_text("~p or ", [Candidate])]).
110+
111+
-spec format_text(string(), list()) -> string().
112+
format_text(Text, Args) ->
113+
Formatted = io_lib:format(Text, Args),
114+
unicode:characters_to_list(Formatted).

apps/els_lsp/test/els_diagnostics_SUITE.erl

+46
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
, unused_macros/1
4242
, unused_record_fields/1
4343
, gradualizer/1
44+
, sheldon/1
4445
]).
4546

4647
%%==============================================================================
@@ -123,6 +124,11 @@ init_per_testcase(TestCase, Config) when TestCase =:= gradualizer ->
123124
meck:expect(els_gradualizer_diagnostics, is_default, 0, true),
124125
els_mock_diagnostics:setup(),
125126
els_test_utils:init_per_testcase(TestCase, Config);
127+
init_per_testcase(TestCase, Config) when TestCase =:= sheldon ->
128+
meck:new(els_sheldon_diagnostics, [passthrough, no_link]),
129+
meck:expect(els_sheldon_diagnostics, is_default, 0, true),
130+
els_mock_diagnostics:setup(),
131+
els_test_utils:init_per_testcase(TestCase, Config);
126132
init_per_testcase(TestCase, Config) ->
127133
els_mock_diagnostics:setup(),
128134
els_test_utils:init_per_testcase(TestCase, Config).
@@ -164,6 +170,11 @@ end_per_testcase(TestCase, Config) when TestCase =:= gradualizer ->
164170
els_test_utils:end_per_testcase(TestCase, Config),
165171
els_mock_diagnostics:teardown(),
166172
ok;
173+
end_per_testcase(TestCase, Config) when TestCase =:= sheldon ->
174+
meck:unload(els_sheldon_diagnostics),
175+
els_test_utils:end_per_testcase(TestCase, Config),
176+
els_mock_diagnostics:teardown(),
177+
ok;
167178
end_per_testcase(TestCase, Config) ->
168179
els_test_utils:end_per_testcase(TestCase, Config),
169180
els_mock_diagnostics:teardown(),
@@ -654,6 +665,41 @@ gradualizer(_Config) ->
654665
Hints = [],
655666
els_test:run_diagnostics_test(Path, Source, Errors, Warnings, Hints).
656667

668+
-spec sheldon(config()) -> ok.
669+
sheldon(_Config) ->
670+
case list_to_integer(erlang:system_info(otp_release)) >= 23 of
671+
true ->
672+
{ok, Cwd} = file:get_cwd(),
673+
RootPath = els_test_utils:root_path(),
674+
try
675+
file:set_cwd(RootPath),
676+
Path = src_path("diagnostics_sheldon.erl"),
677+
Source = <<"Sheldon">>,
678+
Errors = [],
679+
Warnings = [ #{ code => spellcheck
680+
, message => <<"The word \"sheldon\" in "
681+
"comment is unknown. "
682+
"Maybe you wanted to use \"Sheldon\"?">>
683+
, range => {{2, 0}, {3, 0}}
684+
, relatedInformation => []
685+
}
686+
, #{ code => spellcheck
687+
, message => <<"The word \"somestrange\" in comment is "
688+
"unknown.">>
689+
, range => {{0, 0}, {1, 0}}
690+
, relatedInformation => []
691+
}
692+
],
693+
Hints = [],
694+
els_test:run_diagnostics_test(Path, Source, Errors, Warnings, Hints)
695+
catch _Err ->
696+
file:set_cwd(Cwd)
697+
end,
698+
ok;
699+
false ->
700+
{skipped, "Sheldon diagnostics should run on OTP23+"}
701+
end.
702+
657703
%%==============================================================================
658704
%% Internal Functions
659705
%%==============================================================================

elvis.config

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
, els_tcp
3535
, els_dap_test_utils
3636
, els_test_utils
37+
, els_sheldon_diagnostics
3738
]}}
3839
, {elvis_text_style, line_length, #{limit => 80, skip_comments => false}}
3940
, {elvis_style, operator_spaces, #{ rules => [ {right, ","}

rebar.config

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
, {tdiff, "0.1.2"}
1818
, {uuid, "2.0.1", {pkg, uuid_erl}}
1919
, {gradualizer, {git, "https://github.com/josefs/Gradualizer.git", {ref, "e93db1c"}}}
20+
, {rebar3_sheldon, "0.4.1"}
2021
]
2122
}.
2223

rebar.lock

+9
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
{<<"providers">>,{pkg,<<"providers">>,<<"1.8.1">>},1},
1515
{<<"quickrand">>,{pkg,<<"quickrand">>,<<"2.0.1">>},1},
1616
{<<"rebar3_format">>,{pkg,<<"rebar3_format">>,<<"0.8.2">>},0},
17+
{<<"rebar3_sheldon">>,{pkg,<<"rebar3_sheldon">>,<<"0.4.1">>},0},
1718
{<<"redbug">>,{pkg,<<"redbug">>,<<"2.0.6">>},0},
19+
{<<"sheldon">>,{pkg,<<"sheldon">>,<<"0.4.1">>},1},
1820
{<<"tdiff">>,{pkg,<<"tdiff">>,<<"0.1.2">>},0},
1921
{<<"uuid">>,{pkg,<<"uuid_erl">>,<<"2.0.1">>},0},
22+
{<<"worker_pool">>,{pkg,<<"worker_pool">>,<<"5.1.0">>},2},
2023
{<<"yamerl">>,{pkg,<<"yamerl">>,<<"0.8.1">>},0},
2124
{<<"zipper">>,{pkg,<<"zipper">>,<<"1.0.1">>},1}]}.
2225
[
@@ -32,9 +35,12 @@
3235
{<<"providers">>, <<"70B4197869514344A8A60E2B2A4EF41CA03DEF43CFB1712ECF076A0F3C62F083">>},
3336
{<<"quickrand">>, <<"6D861FA11E6EB51BB2343A2616EFF704C2681A9997F41ABC78E58FA76DA33981">>},
3437
{<<"rebar3_format">>, <<"2D64DA61E0B87FCA6C4512ADA6D9CBC2B27ADC9AE6844178561147E7121761BD">>},
38+
{<<"rebar3_sheldon">>, <<"E2F4535929A25A7F8CFCCFC23E3CF93839BCC7F83C0BFFBBDB3446210D8014D4">>},
3539
{<<"redbug">>, <<"A764690B012B67C404562F9C6E1BA47A73892EE17DF5C15F670B1A5BF9D2F25A">>},
40+
{<<"sheldon">>, <<"1413143F9D96D30C6A18DD6746B5183F048B23AC345FF3BB92B399A5A69EBE93">>},
3641
{<<"tdiff">>, <<"4E1B30321F1B3D600DF65CD60858EDE1235FE4E5EE042110AB5AD90CD6464AC5">>},
3742
{<<"uuid">>, <<"1FD9079C544D521063897887A1C5B3302DCA98F9BB06AADCDC6FB0663F256797">>},
43+
{<<"worker_pool">>, <<"61BA970F856AF8B2D85232ADC8E4B990FA1719F396FCDAD954702946395ADD80">>},
3844
{<<"yamerl">>, <<"07DA13FFA1D8E13948943789665C62CCD679DFA7B324A4A2ED3149DF17F453A4">>},
3945
{<<"zipper">>, <<"3CCB4F14B97C06B2749B93D8B6C204A1ECB6FAFC6050CACC3B93B9870C05952A">>}]},
4046
{pkg_hash_ext,[
@@ -49,9 +55,12 @@
4955
{<<"providers">>, <<"E45745ADE9C476A9A469EA0840E418AB19360DC44F01A233304E118A44486BA0">>},
5056
{<<"quickrand">>, <<"14DB67D4AEF6B8815810EC9F3CCEF5E324B73B56CAE3687F99D752B85BDD4C96">>},
5157
{<<"rebar3_format">>, <<"CA8FF27638C2169593D1449DACBE8895634193ED3334E906B54FC97F081F5213">>},
58+
{<<"rebar3_sheldon">>, <<"79DAA99B7B58F590B9DBD2641B0C0E0F116D79E587779E861D4246B1D7CD5737">>},
5259
{<<"redbug">>, <<"AAD9498671F4AB91EACA5099FE85A61618158A636E6286892C4F7CF4AF171D04">>},
60+
{<<"sheldon">>, <<"15E24EDDCDB42FE07AA7913DA770DBCE4EE052A37AA76E467748D4C977F463C9">>},
5361
{<<"tdiff">>, <<"E0C2E168F99252A5889768D5C8F1E6510A184592D4CFA06B22778A18D33D7875">>},
5462
{<<"uuid">>, <<"AB57CACCD51F170011E5F444CE865F84B41605E483A9EFCC468C1AFAEC87553B">>},
63+
{<<"worker_pool">>, <<"59A975728E5C2B69A23D1C5EBC3B63251EC4ACBF4E9D25F1C619E3AD4B550813">>},
5564
{<<"yamerl">>, <<"96CB30F9D64344FED0EF8A92E9F16F207DE6C04DFFF4F366752CA79F5BCEB23F">>},
5665
{<<"zipper">>, <<"6A1FD3E1F0CC1D1DF5642C9A0CE2178036411B0A5C9642851D1DA276BD737C2D">>}]}
5766
].

0 commit comments

Comments
 (0)