elvis_core
is the core library for the elvis
Erlang style
reviewer. It is also used by rebar3_lint
for easier
integration into your Erlang libraries or applications.
It includes the mechanisms to apply rules to your Erlang code, as well as their implementation.
It implements pre-defined rules, but also supports user-defined ones.
The elvis
command-line tool uses elvis_core
extensively, so
do check that project for a concrete example on how you could use it for your own purposes.
The rebar3_lint
plugin eases integration of the
style reviewer into your application or library. Be sure to check that out for further information.
After adding elvis_core
as a dependency to your project and starting a shell, you will need to
make sure the application is started:
1> {ok, _} = application:ensure_all_started(elvis_core).
{ok,[zipper,katana_code,elvis_core]}
2>
Once this is done you can apply the style rules in the following ways.
1> ElvisConfig = elvis_config:from_file("elvis.config").
<loaded_config>
2> elvis_core:rock(ElvisConfig).
# src/elvis_core.erl [OK]
# src/elvis_result.erl [OK]
# src/elvis_style.erl [OK]
# src/elvis_utils.erl [OK]
ok
3>
This will load the configuration, specified in file elvis.config
, from the
current directory. If no configuration is found {invalid_config, _}
is thrown.
Another option for using elvis_core
from the shell is to explicitly provide the configuration as
an argument to elvis_core:rock/1
:
1> ElvisConfig = [#{dirs => ["src"], filter => "*.erl", rules => []}].
[#{dirs => ["src"],filter => "*.erl",rules => []}]
2> elvis_core:rock(ElvisConfig).
Loading src/elvis_core.erl
# src/elvis_core.erl [OK]
Loading src/elvis_result.erl
# src/elvis_result.erl [OK]
Loading src/elvis_style.erl
# src/elvis_style.erl [OK]
Loading src/elvis_utils.erl
# src/elvis_utils.erl [OK]
ok
3>
We have only presented results where all files were well-behaved (i.e. they respect all the rules), so here's an example of how the output looks when files break some of the rules:
# ../../test/examples/fail_line_length.erl [FAIL]
- line_length
- Line 14 is too long: " io:format(\"This line is 81 characters long and should be detected, yeah!!!\").".
- Line 20 is too long: " io:format(\"This line is 90 characters long and should be detected!!!!!!!!!!!!!!!!!!\").".
# ../../test/examples/fail_no_tabs.erl [FAIL]
- no_tabs
- Line 6 has a tab at column 0.
- Line 15 has a tab at column 0.
# ../../test/examples/small.erl [OK]
An elvis.config
file looks something like this:
[{elvis, [
{config, [
#{ dirs => ["src"]
, filter => "*.erl"
, ruleset => erl_files }
, #{ dirs => ["include"]
, filter => "*.hrl"
, ruleset => hrl_files }
, #{ dirs => ["."]
, filter => "rebar.config"
, ruleset => rebar_config }
, #{ dirs => ["."]
, filter => "elvis.config"
, ruleset => elvis_config }
]}
% output_format (optional): how to format the output.
% Possible values are 'plain', 'colors' or 'parsable' (default='colors').
, {output_format, colors}
% verbose (optional): when 'true' more information will
% be printed (default=false).
, {verbose, true}
% no_output (optional): when 'true' nothing will be printed
% (default=false).
, {no_output, false}
% parallel: determine how many files will be
% analyzed in parallel (default=1).
, {parallel, 1}
]}].
The dirs
key is a list that tells elvis_core
where it should look for the files that match
filter
, which will be run through each of the pre-defined rules in the specified ruleset
.
filter
can contain **
for further matching (it uses
filelib:wildcard/1
under the hood).
If you want to override the pre-defined rules, for a given ruleset, you need
to specify them in a rules
key which is a list of items with the following structure
{Module, Function, RuleConfig}
, or {Module, Function}
- if the rule takes no configuration
values. You can also disable
certain rules if you want to, by specifying them in the rules
key
and passing disable
as a third parameter.
IMPORTANT: disable
will only work if you also provided a ruleset
as shown above.
Let's say you like your files to have a maximum of 90 characters per line and you also like to use
tabs instead of spaces. In that case, you need to override erl_files
's ruleset
pre-defined
rules
as follows:
#{ dirs => ["src"]
, filter => "*.erl"
, rules => [
{elvis_text_style, line_length, #{ limit => 90 }} % change line_length from 100 to 90
, {elvis_text_style, no_tabs, disable} % disable no_tabs
]
, ruleset => erl_files
}.
You can also ignore
modules at a check level or at a ruleset (group of checks) level:
- at a check level, you set the
ignore
option in the rule you want to ignore, e.g.:
{elvis_style, no_debug_call, #{ ignore => [elvis, elvis_utils] }}
(we are telling elvis
to ignore the elvis
and elvis_utils
modules when executing
the no_debug_call
check.
- at a ruleset (group of checks) level, you set the
ignore
option for the group you want to ignore, e.g.:
#{ dirs => ["src"]
, filter => "*.erl"
, ruleset => erl_files
, ignore => [module1, module4]
}.
With this configuration, none of the checks for erl_files
is applied to module1
or module4
.
Option output_format
allows you to configure the output format. Possible values are colors
,
plain
and parsable
. The latter could be used for automated parsing and has a format very close
to the one presented by dialyzer
, like <file>:<line>:<rule>:<message>
:
src/example.erl:1:god_modules:This module has too many functions (56). Consider breaking it into a number of modules.
src/example_a.erl:341:no_debug_call:Remove the debug call to io:format/2 on line 341.
src/example_a.erl:511:used_ignored_variable:Ignored variable is being used on line 511 and column 54.
src/example_a.erl:1252:used_ignored_variable:Ignored variable is being used on line 1252 and column 21.
The default value for the output_format
option is colors
.
It is possible to tell elvis_core
to produce a more verbose output, using the verbose
option.
The value provided is a boolean, either true
or false
.
The default value for the verbose
option is false
.
On the other hand, if no output is desired then the value for the no_output
option should be
true
.
The default value for the no_output
option is false
.
In order to speed up the analysis process, you can use the parallel
option.
Its value indicates how many processes to use at the same time to apply the style rules to all the files gathered. The provided number should be less than or equal to the available cores, since any value higher than that won't report any speedup benefits.
The default value for parallel
is 1
.
You can find examples for configuration files in this project's
config
directory.
Options output_format
, verbose
, no_output
, and parallel
can also be set as application-level
environment variables, i.e. as they would be found by application:get_env/2,3
.
A reference to all pre-defined rules (and some other information) implemented in elvis_core
can be
found in this repository's RULES.md.
The implementation of a new rule is a function that takes 3 arguments in the following order:
t:elvis_config:config()
: the value of optionconfig
as found in the configuration,t:elvis_file:file()
: the file to be analyzed,t:erlang:map()
: a configuration map specific to your user-defined rule.
This means you can define rules of your own (user-defined rules) as long as the functions that implement them respect this interface.
If you find any bugs or have other problems using this library, open an issue in this repository (or even a pull request 😃).
Inspired by HoundCI.