Packages
credo
0.3.8
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.7-rc.0
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.2-rc.4
1.7.2-rc.3
1.7.2-rc.2
1.7.2-rc.1
1.7.2-rc.0
1.7.1
1.7.0
1.7.0-rc.2
1.7.0-rc.1
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.4.1
1.4.0
1.4.0-rc.2
1.4.0-rc.1
1.3.2
1.3.1
1.3.0
1.3.0-rc3
1.3.0-rc2
1.3.0-rc1
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc4
1.2.0-rc3
1.2.0-rc2
1.2.0-rc1
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.1.0-rc3
1.1.0-rc2
1.1.0-rc1
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.1-rc1
1.0.0
1.0.0-rc1
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.9.0-rc8
0.9.0-rc7
0.9.0-rc6
0.9.0-rc5
0.9.0-rc4
0.9.0-rc3
0.9.0-rc2
0.9.0-rc1
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.8.0-rc7
0.8.0-rc6
0.8.0-rc5
0.8.0-rc4
0.8.0-rc3
0.8.0-rc2
0.8.0-rc1
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.6.0-rc2
0.6.0-rc1
0.5.3
0.5.2
0.5.1
0.5.0
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.10-dev
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.4.0-beta5
0.4.0-beta4
0.4.0-beta3
0.4.0-beta2
0.4.0-beta1
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.3.0-dev2
0.3.0-dev
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.0.1-dev
A static code analysis tool with a focus on code consistency and teaching.
Current section
Files
Jump to
Current section
Files
lib/credo/config.ex
defmodule Credo.Config do
@doc """
Every run of Credo is configured via a `Config` object, which is created and
manipulated via the `Credo.Config` module.
"""
defstruct files: nil,
checks: nil,
min_priority: 0,
help: false,
version: false,
verbose: false,
all: false,
format: nil,
match_checks: nil,
ignore_checks: nil,
crash_on_error: true,
read_from_stdin: false,
lint_attribute_map: %{} # maps filenames to @lint attributes
@config_filename ".credo.exs"
@default_config_name "default"
@default_config_file File.read!(@config_filename)
@default_glob "**/*.{ex,exs}"
@default_files_included [@default_glob]
@default_files_excluded []
@doc """
Returns the checks that should be run for a given `config` object.
Takes all checks from the `checks:` field of the config, matches those against
any patterns to include or exclude certain checks given via the command line.
"""
def checks(%__MODULE__{checks: checks, match_checks: match_checks, ignore_checks: ignore_checks}) do
match_regexes = match_checks |> List.wrap |> to_match_regexes
ignore_regexes = ignore_checks |> List.wrap |> to_match_regexes
checks
|> Enum.filter(&match_regex(&1, match_regexes, true))
|> Enum.reject(&match_regex(&1, ignore_regexes, false))
end
defp match_regex(_tuple, [], default_for_empty), do: default_for_empty
defp match_regex(tuple, regexes, _default_for_empty) do
check_name =
tuple
|> Tuple.to_list
|> List.first
|> to_string
regexes
|> Enum.any?(&Regex.run(&1, check_name))
end
defp to_match_regexes(list) do
list
|> Enum.map(fn(match_check) ->
{:ok, match_pattern} = Regex.compile(match_check, "i")
match_pattern
end)
end
def read_or_default(dir, config_name \\ nil) do
dir
|> relevant_config_files
|> Enum.filter(&File.exists?/1)
|> Enum.map(&File.read!/1)
|> List.insert_at(0, @default_config_file)
|> Enum.map(&from_exs(dir, config_name || @default_config_name, &1))
|> merge
|> add_given_directory_to_files(dir)
end
defp relevant_config_files(dir) do
dir
|> relevant_directories
|> add_config_files
end
def relevant_directories(dir) do
dir
|> Path.expand
|> Path.split
|> Enum.reverse
|> get_dir_paths
|> add_config_dirs
end
defp get_dir_paths(dirs), do: do_get_dir_paths(dirs, [])
defp do_get_dir_paths(dirs, acc) when length(dirs) < 2, do: acc
defp do_get_dir_paths([dir | tail], acc) do
expanded_path = tail |> Enum.reverse |> Path.join |> Path.join(dir)
do_get_dir_paths(tail, [expanded_path | acc])
end
defp add_config_dirs(paths) do
Enum.flat_map(paths, fn(path) -> [path, Path.join(path, "config")] end)
end
defp add_config_files(paths) do
for path <- paths, do: Path.join(path, @config_filename)
end
defp from_exs(dir, config_name, exs_string) do
exs_string
|> Credo.ExsLoader.parse
|> from_data(dir, config_name)
end
defp from_data(data, dir, config_name) do
data =
data[:configs]
|> List.wrap
|> Enum.find(&(&1[:name] == config_name))
%__MODULE__{
files: files_from_data(data, dir),
checks: checks_from_data(data)
}
end
defp files_from_data(data, dir) do
files = data[:files] || %{}
included_dir =
(files[:included] || dir)
|> List.wrap
|> Enum.map(&join_default_files_if_directory/1)
%{
included: included_dir,
excluded: files[:excluded] || @default_files_excluded,
}
end
defp checks_from_data(data) do
case data[:checks] do
checks when is_list(checks) -> checks
_ -> []
end
end
@doc """
Merges the given Config objects from left to right, meaning that later entries
overwrites earlier ones.
merge(base, other)
Any options in `other` will overwrite those in `base`.
The `files:` field is merged, meaning that you can define `included` and/or
`excluded` and only override the given one.
The `checks:` field is overwritten in full, meaning that there is no
"blending" of checks from one Config to another. If you are including this
field in a Config, it basically means "I want to run these checks and
these checks only".
"""
def merge(list) when is_list(list) do
base = list |> List.first
tail = list |> List.delete_at(0)
merge(tail, base)
end
def merge([], config), do: config
def merge([other|tail], base) do
new_base = merge(base, other)
merge(tail, new_base)
end
def merge(base, other) do
%__MODULE__{
files: merge_files(base, other),
checks: merge_checks(base, other),
}
end
def merge_checks(%__MODULE__{checks: checks_base}, %__MODULE__{checks: checks_other}) do
checks_other || checks_base
end
def merge_files(%__MODULE__{files: files_base}, %__MODULE__{files: files_other}) do
%{
included: files_other[:included] || files_base[:included],
excluded: files_other[:excluded] || files_base[:excluded],
}
end
defp join_default_files_if_directory(dir) do
if File.dir?(dir) do
Path.join(dir, @default_files_included)
else
dir
end
end
defp add_given_directory_to_files(%__MODULE__{files: files} = config, dir) do
files = %{
included:
files[:included]
|> Enum.map(&add_directory_to_file(&1, dir))
|> Enum.uniq,
excluded: files[:excluded]
|> Enum.map(&add_directory_to_file(&1, dir))
|> Enum.uniq
}
%__MODULE__{config | files: files}
end
defp add_directory_to_file(file_or_glob, dir) do
if File.dir?(dir) do
Path.join(dir, file_or_glob)
else
dir
end
end
end