-
Notifications
You must be signed in to change notification settings - Fork 414
/
max_line_length.ex
142 lines (120 loc) · 4.06 KB
/
max_line_length.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
defmodule Credo.Check.Readability.MaxLineLength do
use Credo.Check,
base_priority: :low,
tags: [:formatter],
param_defaults: [
max_length: 120,
ignore_definitions: true,
ignore_heredocs: true,
ignore_specs: false,
ignore_strings: true,
ignore_urls: true
],
explanations: [
check: """
Checks for the length of lines.
Ignores function definitions and (multi-)line strings by default.
""",
params: [
max_length: "The maximum number of characters a line may consist of.",
ignore_definitions: "Set to `true` to ignore lines including function definitions.",
ignore_specs: "Set to `true` to ignore lines including `@spec`s.",
ignore_strings: "Set to `true` to ignore lines that are strings or in heredocs.",
ignore_urls: "Set to `true` to ignore lines that contain urls."
]
]
alias Credo.Code.Heredocs
alias Credo.Code.Strings
@def_ops [:def, :defp, :defmacro]
@url_regex ~r/[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/
@doc false
@impl true
def run(%SourceFile{} = source_file, params) do
issue_meta = IssueMeta.for(source_file, params)
max_length = Params.get(params, :max_length, __MODULE__)
ignore_definitions = Params.get(params, :ignore_definitions, __MODULE__)
ignore_specs = Params.get(params, :ignore_specs, __MODULE__)
ignore_strings = Params.get(params, :ignore_strings, __MODULE__)
ignore_heredocs = Params.get(params, :ignore_heredocs, __MODULE__)
ignore_urls = Params.get(params, :ignore_urls, __MODULE__)
definitions = Credo.Code.prewalk(source_file, &find_definitions/2)
specs = Credo.Code.prewalk(source_file, &find_specs/2)
source =
if ignore_heredocs do
Heredocs.replace_with_spaces(source_file, "")
else
SourceFile.source(source_file)
end
lines = Credo.Code.to_lines(source)
lines_for_comparison =
if ignore_strings do
source
|> Strings.replace_with_spaces("", " ", source_file.filename)
|> Credo.Code.to_lines()
else
lines
end
lines_for_comparison =
if ignore_urls do
Enum.reject(lines_for_comparison, fn {_, line} -> line =~ @url_regex end)
else
lines_for_comparison
end
Enum.reduce(lines_for_comparison, [], fn {line_no, line_for_comparison}, issues ->
if String.length(line_for_comparison) > max_length do
if refute_issue?(line_no, definitions, ignore_definitions, specs, ignore_specs) do
issues
else
{_, line} = Enum.at(lines, line_no - 1)
[issue_for(line_no, max_length, line, issue_meta) | issues]
end
else
issues
end
end)
end
# TODO: consider for experimental check front-loader (ast)
for op <- @def_ops do
defp find_definitions({unquote(op), meta, arguments} = ast, definitions)
when is_list(arguments) do
{ast, [meta[:line] | definitions]}
end
end
defp find_definitions(ast, definitions) do
{ast, definitions}
end
# TODO: consider for experimental check front-loader (ast)
defp find_specs({:spec, meta, arguments} = ast, specs) when is_list(arguments) do
{ast, [meta[:line] | specs]}
end
defp find_specs(ast, specs) do
{ast, specs}
end
defp refute_issue?(line_no, definitions, ignore_definitions, specs, ignore_specs) do
ignore_definitions? =
if ignore_definitions do
Enum.member?(definitions, line_no)
else
false
end
ignore_specs? =
if ignore_specs do
Enum.member?(specs, line_no)
else
false
end
ignore_definitions? || ignore_specs?
end
defp issue_for(line_no, max_length, line, issue_meta) do
line_length = String.length(line)
column = max_length + 1
trigger = String.slice(line, max_length, line_length - max_length)
format_issue(
issue_meta,
message: "Line is too long (max is #{max_length}, was #{line_length}).",
line_no: line_no,
column: column,
trigger: trigger
)
end
end