rubocop 0.26.1 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -0
  3. data/.rubocop_todo.yml +10 -6
  4. data/.travis.yml +2 -0
  5. data/CHANGELOG.md +30 -0
  6. data/README.md +9 -2
  7. data/assets/logo.png +0 -0
  8. data/assets/output.html.erb +68 -65
  9. data/config/default.yml +42 -7
  10. data/config/disabled.yml +5 -0
  11. data/config/enabled.yml +32 -7
  12. data/lib/rubocop.rb +10 -2
  13. data/lib/rubocop/comment_config.rb +11 -17
  14. data/lib/rubocop/config.rb +20 -16
  15. data/lib/rubocop/config_loader.rb +8 -12
  16. data/lib/rubocop/cop/cop.rb +13 -12
  17. data/lib/rubocop/cop/lint/block_alignment.rb +4 -6
  18. data/lib/rubocop/cop/lint/def_end_alignment.rb +2 -2
  19. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  20. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -3
  21. data/lib/rubocop/cop/lint/useless_setter_call.rb +2 -2
  22. data/lib/rubocop/cop/metrics/abc_size.rb +27 -0
  23. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -4
  24. data/lib/rubocop/cop/metrics/class_length.rb +1 -1
  25. data/lib/rubocop/cop/metrics/line_length.rb +2 -5
  26. data/lib/rubocop/cop/metrics/method_length.rb +2 -2
  27. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +24 -15
  28. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +15 -2
  29. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +63 -0
  30. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
  31. data/lib/rubocop/cop/mixin/if_node.rb +3 -1
  32. data/lib/rubocop/cop/mixin/method_complexity.rb +3 -3
  33. data/lib/rubocop/cop/mixin/{on_method.rb → on_method_def.rb} +3 -3
  34. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +2 -2
  35. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  36. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
  37. data/lib/rubocop/cop/mixin/string_literals_help.rb +28 -0
  38. data/lib/rubocop/cop/rails/delegate.rb +2 -2
  39. data/lib/rubocop/cop/style/access_modifier_indentation.rb +2 -2
  40. data/lib/rubocop/cop/style/accessor_method_name.rb +2 -2
  41. data/lib/rubocop/cop/style/align_hash.rb +16 -12
  42. data/lib/rubocop/cop/style/align_parameters.rb +1 -1
  43. data/lib/rubocop/cop/style/and_or.rb +14 -6
  44. data/lib/rubocop/cop/style/array_join.rb +1 -1
  45. data/lib/rubocop/cop/style/block_comments.rb +16 -8
  46. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +6 -30
  47. data/lib/rubocop/cop/style/case_indentation.rb +20 -12
  48. data/lib/rubocop/cop/style/collection_methods.rb +4 -4
  49. data/lib/rubocop/cop/style/colon_method_call.rb +9 -0
  50. data/lib/rubocop/cop/style/comment_annotation.rb +1 -1
  51. data/lib/rubocop/cop/style/comment_indentation.rb +22 -22
  52. data/lib/rubocop/cop/style/def_with_parentheses.rb +2 -2
  53. data/lib/rubocop/cop/style/deprecated_hash_methods.rb +1 -1
  54. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  55. data/lib/rubocop/cop/style/else_alignment.rb +93 -0
  56. data/lib/rubocop/cop/style/empty_line_between_defs.rb +1 -1
  57. data/lib/rubocop/cop/style/empty_lines.rb +1 -1
  58. data/lib/rubocop/cop/style/empty_lines_around_class_body.rb +34 -0
  59. data/lib/rubocop/cop/style/empty_lines_around_method_body.rb +37 -0
  60. data/lib/rubocop/cop/style/empty_lines_around_module_body.rb +30 -0
  61. data/lib/rubocop/cop/style/encoding.rb +1 -1
  62. data/lib/rubocop/cop/style/format_string.rb +4 -4
  63. data/lib/rubocop/cop/style/indent_array.rb +2 -2
  64. data/lib/rubocop/cop/style/indent_hash.rb +17 -12
  65. data/lib/rubocop/cop/style/indentation_width.rb +27 -19
  66. data/lib/rubocop/cop/style/method_call_parentheses.rb +3 -3
  67. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -1
  68. data/lib/rubocop/cop/style/method_def_parentheses.rb +17 -11
  69. data/lib/rubocop/cop/style/method_name.rb +1 -1
  70. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +174 -0
  71. data/lib/rubocop/cop/style/non_nil_check.rb +12 -15
  72. data/lib/rubocop/cop/style/not.rb +1 -1
  73. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +12 -17
  74. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  75. data/lib/rubocop/cop/style/predicate_name.rb +2 -2
  76. data/lib/rubocop/cop/style/redundant_begin.rb +2 -2
  77. data/lib/rubocop/cop/style/redundant_return.rb +3 -3
  78. data/lib/rubocop/cop/style/redundant_self.rb +3 -3
  79. data/lib/rubocop/cop/style/regexp_literal.rb +17 -13
  80. data/lib/rubocop/cop/style/rescue_modifier.rb +2 -2
  81. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  82. data/lib/rubocop/cop/style/space_after_method_name.rb +2 -2
  83. data/lib/rubocop/cop/style/space_around_equals_in_parameter_default.rb +17 -11
  84. data/lib/rubocop/cop/style/space_before_block_braces.rb +1 -1
  85. data/lib/rubocop/cop/style/space_inside_block_braces.rb +17 -14
  86. data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +10 -6
  87. data/lib/rubocop/cop/style/string_literals.rb +13 -16
  88. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +41 -0
  89. data/lib/rubocop/cop/style/trailing_comma.rb +1 -3
  90. data/lib/rubocop/cop/style/trivial_accessors.rb +3 -3
  91. data/lib/rubocop/cop/style/unneeded_capital_w.rb +1 -1
  92. data/lib/rubocop/cop/style/unneeded_percent_q.rb +2 -2
  93. data/lib/rubocop/cop/style/word_array.rb +23 -19
  94. data/lib/rubocop/cop/team.rb +13 -26
  95. data/lib/rubocop/cop/util.rb +5 -0
  96. data/lib/rubocop/cop/variable_force/locatable.rb +7 -13
  97. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  98. data/lib/rubocop/formatter/formatter_set.rb +9 -1
  99. data/lib/rubocop/formatter/html_formatter.rb +83 -55
  100. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -2
  101. data/lib/rubocop/formatter/text_util.rb +25 -0
  102. data/lib/rubocop/options.rb +14 -7
  103. data/lib/rubocop/path_util.rb +11 -7
  104. data/lib/rubocop/runner.rb +7 -2
  105. data/lib/rubocop/version.rb +1 -1
  106. data/relnotes/v0.27.0.md +77 -0
  107. data/rubocop.gemspec +1 -1
  108. data/spec/fixtures/html_formatter/expected.html +495 -0
  109. data/spec/fixtures/html_formatter/project/app/controllers/application_controller.rb +5 -0
  110. data/spec/fixtures/html_formatter/project/app/controllers/books_controller.rb +74 -0
  111. data/spec/fixtures/html_formatter/project/app/models/book.rb +5 -0
  112. data/spec/rubocop/cli_spec.rb +56 -13
  113. data/spec/rubocop/cop/lint/invalid_character_literal_spec.rb +1 -1
  114. data/spec/rubocop/cop/metrics/abc_size_spec.rb +99 -0
  115. data/spec/rubocop/cop/rails/action_filter_spec.rb +1 -0
  116. data/spec/rubocop/cop/style/access_modifier_indentation_spec.rb +23 -1
  117. data/spec/rubocop/cop/style/align_hash_spec.rb +13 -0
  118. data/spec/rubocop/cop/style/align_parameters_spec.rb +44 -33
  119. data/spec/rubocop/cop/style/blocks_spec.rb +8 -0
  120. data/spec/rubocop/cop/style/braces_around_hash_parameters_spec.rb +9 -9
  121. data/spec/rubocop/cop/style/case_indentation_spec.rb +3 -2
  122. data/spec/rubocop/cop/style/colon_method_call_spec.rb +5 -0
  123. data/spec/rubocop/cop/style/comment_indentation_spec.rb +6 -1
  124. data/spec/rubocop/cop/style/else_alignment_spec.rb +437 -0
  125. data/spec/rubocop/cop/style/empty_lines_around_class_body_spec.rb +75 -0
  126. data/spec/rubocop/cop/style/{empty_lines_around_body_spec.rb → empty_lines_around_method_body_spec.rb} +9 -50
  127. data/spec/rubocop/cop/style/empty_lines_around_module_body_spec.rb +79 -0
  128. data/spec/rubocop/cop/style/file_name_spec.rb +1 -1
  129. data/spec/rubocop/cop/style/format_string_spec.rb +12 -0
  130. data/spec/rubocop/cop/style/indent_array_spec.rb +6 -1
  131. data/spec/rubocop/cop/style/indent_hash_spec.rb +2 -1
  132. data/spec/rubocop/cop/style/indentation_width_spec.rb +765 -722
  133. data/spec/rubocop/cop/style/multiline_operation_indentation_spec.rb +414 -0
  134. data/spec/rubocop/cop/style/non_nil_check_spec.rb +86 -55
  135. data/spec/rubocop/cop/style/single_line_methods_spec.rb +5 -1
  136. data/spec/rubocop/cop/style/space_before_block_braces_spec.rb +2 -1
  137. data/spec/rubocop/cop/style/space_inside_block_braces_spec.rb +2 -1
  138. data/spec/rubocop/cop/style/string_literals_in_interpolation_spec.rb +63 -0
  139. data/spec/rubocop/cop/style/string_literals_spec.rb +2 -2
  140. data/spec/rubocop/cop/style/word_array_spec.rb +15 -1
  141. data/spec/rubocop/formatter/base_formatter_spec.rb +1 -1
  142. data/spec/rubocop/formatter/disabled_lines_formatter_spec.rb +0 -1
  143. data/spec/rubocop/formatter/formatter_set_spec.rb +9 -0
  144. data/spec/rubocop/formatter/html_formatter_spec.rb +25 -122
  145. data/spec/rubocop/formatter/offense_count_formatter_spec.rb +0 -1
  146. data/spec/rubocop/runner_spec.rb +1 -1
  147. data/spec/spec_helper.rb +12 -130
  148. data/spec/support/cop_helper.rb +72 -0
  149. data/spec/support/coverage.rb +15 -0
  150. data/spec/support/{offenses_matcher.rb → custom_matchers.rb} +28 -0
  151. data/spec/support/jruby_workaround.rb +15 -0
  152. data/spec/support/{isolated_environment.rb → shared_contexts.rb} +19 -0
  153. metadata +49 -14
  154. data/lib/rubocop/cop/style/empty_lines_around_body.rb +0 -75
  155. data/spec/support/shared_context.rb +0 -20
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks if uses of quotes match the configured preference.
7
+ class StringLiteralsInInterpolation < Cop
8
+ include ConfigurableEnforcedStyle
9
+ include StringLiteralsHelp
10
+
11
+ private
12
+
13
+ def message(*)
14
+ # single_quotes -> single-quoted
15
+ kind = style.to_s.sub(/_(.*)s/, '-\1d')
16
+
17
+ "Prefer #{kind} strings inside interpolations."
18
+ end
19
+
20
+ def offense?(node)
21
+ # If it's not a string within a dynamic string, i.e. part of an
22
+ # expression in an interpolation, then it's not an offense for this
23
+ # cop.
24
+ return false unless node.each_ancestor.find do |a|
25
+ a.type == :dstr && within_node?(node, a)
26
+ end
27
+
28
+ wrong_quotes?(node, style)
29
+ end
30
+
31
+ def autocorrect(node)
32
+ @corrections << lambda do |corrector|
33
+ replacement = node.loc.begin.is?('"') ? "'" : '"'
34
+ corrector.replace(node.loc.begin, replacement)
35
+ corrector.replace(node.loc.end, replacement)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -78,8 +78,6 @@ module RuboCop
78
78
  unless should_have_comma
79
79
  extra_info = if style == :comma
80
80
  ', unless each item is on its own line'
81
- else
82
- ''
83
81
  end
84
82
  avoid_comma(kind, after_last_item.begin_pos + comma_offset, sb,
85
83
  extra_info)
@@ -123,7 +121,7 @@ module RuboCop
123
121
  article = kind =~ /array/ ? 'an' : 'a'
124
122
  add_offense(range, range,
125
123
  format(MSG, 'Avoid', format(kind, article)) +
126
- extra_info + '.')
124
+ "#{extra_info}.")
127
125
  end
128
126
 
129
127
  def put_comma(items, kind, sb)
@@ -6,13 +6,13 @@ module RuboCop
6
6
  # This cop looks for trivial reader/writer methods, that could
7
7
  # have been created with the attr_* family of functions automatically.
8
8
  class TrivialAccessors < Cop
9
- include OnMethod
9
+ include OnMethodDef
10
10
 
11
11
  MSG = 'Use `attr_%s` to define trivial %s methods.'
12
12
 
13
13
  private
14
14
 
15
- def on_method(node, method_name, args, body)
15
+ def on_method_def(node, method_name, args, body)
16
16
  kind = if trivial_reader?(method_name, args, body)
17
17
  'reader'
18
18
  elsif trivial_writer?(method_name, args, body)
@@ -91,7 +91,7 @@ module RuboCop
91
91
 
92
92
  def trivial_accessor_kind(method_name, args, body)
93
93
  if trivial_writer?(method_name, args, body) &&
94
- !dsl_writer?(method_name)
94
+ !dsl_writer?(method_name)
95
95
  'writer'
96
96
  elsif trivial_reader?(method_name, args, body)
97
97
  'reader'
@@ -19,7 +19,7 @@ module RuboCop
19
19
  def on_percent_literal(node)
20
20
  node.children.each do |string|
21
21
  if string.type == :dstr ||
22
- string.loc.expression.source =~ StringHelp::ESCAPED_CHAR_REGEXP
22
+ string.loc.expression.source =~ StringHelp::ESCAPED_CHAR_REGEXP
23
23
  return
24
24
  end
25
25
  end
@@ -33,11 +33,11 @@ module RuboCop
33
33
  end
34
34
  return if ignored_node?(node) || part_of_ignored_node?(node)
35
35
  src = node.loc.expression.source
36
- return unless src =~ /^%q/i
36
+ return unless src.start_with?('%q') || src.start_with?('%Q')
37
37
  return if src =~ /'/ && src =~ /"/
38
38
  return if src =~ StringHelp::ESCAPED_CHAR_REGEXP
39
39
 
40
- extra = if src =~ /^%Q/
40
+ extra = if src.start_with?('%Q')
41
41
  ', or for dynamic strings that contain double quotes'
42
42
  else
43
43
  ''
@@ -17,8 +17,8 @@ module RuboCop
17
17
  def on_array(node)
18
18
  array_elems = node.children
19
19
  return unless array_of?(:str, node) &&
20
- !complex_content?(array_elems) &&
21
- array_elems.size > min_size && !comments_in_array?(node)
20
+ !complex_content?(array_elems) &&
21
+ array_elems.size > min_size && !comments_in_array?(node)
22
22
 
23
23
  add_offense(node, :expression) { self.max = array_elems.size }
24
24
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
  next if source.start_with?('?') # %W(\r \n) can replace [?\r, ?\n]
46
46
 
47
47
  str_content = Util.strip_quotes(source)
48
- return true unless str_content =~ /\A[\p{Word}]+\z/
48
+ return true unless str_content =~ word_regex
49
49
  end
50
50
 
51
51
  false
@@ -55,29 +55,33 @@ module RuboCop
55
55
  cop_config['MinSize']
56
56
  end
57
57
 
58
+ def word_regex
59
+ cop_config['WordRegex']
60
+ end
61
+
58
62
  def autocorrect(node)
59
- sb = node.loc.expression.source_buffer
60
- interpolated = false
61
-
62
- contents = node.children.map do |n|
63
- if character_literal?(n)
64
- interpolated = true
65
- begin_pos = n.loc.expression.begin_pos + '?'.length
66
- end_pos = n.loc.expression.end_pos
67
- else
68
- begin_pos = n.loc.begin.end_pos
69
- end_pos = n.loc.end.begin_pos
70
- end
71
- Parser::Source::Range.new(sb, begin_pos, end_pos).source
72
- end.join(' ')
73
-
74
- char = interpolated ? 'W' : 'w'
63
+ @interpolated = false
64
+ contents = node.children.map { |n| source_for(n) }.join(' ')
65
+ char = @interpolated ? 'W' : 'w'
75
66
 
76
67
  @corrections << lambda do |corrector|
77
68
  corrector.replace(node.loc.expression, "%#{char}(#{contents})")
78
69
  end
79
70
  end
80
71
 
72
+ def source_for(str_node)
73
+ if character_literal?(str_node)
74
+ @interpolated = true
75
+ begin_pos = str_node.loc.expression.begin_pos + '?'.length
76
+ end_pos = str_node.loc.expression.end_pos
77
+ else
78
+ begin_pos = str_node.loc.begin.end_pos
79
+ end_pos = str_node.loc.end.begin_pos
80
+ end
81
+ Parser::Source::Range.new(str_node.loc.expression.source_buffer,
82
+ begin_pos, end_pos).source
83
+ end
84
+
81
85
  def character_literal?(node)
82
86
  node.loc.end.nil?
83
87
  end
@@ -62,26 +62,7 @@ module RuboCop
62
62
  @updated_source_file = false
63
63
  return unless autocorrect?
64
64
 
65
- corrections = cops.each_with_object([]) do |cop, array|
66
- array.concat(cop.corrections) if cop.relevant_file?(buffer.name)
67
- end
68
-
69
- corrector = Corrector.new(buffer, corrections)
70
- new_source = begin
71
- # Corrector#rewrite can raise RangeError or RuntimeError
72
- # for various error conditions including clobbering.
73
- s = corrector.rewrite
74
- # We raise RuntimeError ourselves if the rewritten code
75
- # is not parsable ruby. We don't want to write that code
76
- # to file.
77
- fail unless ProcessedSource.new(s).valid_syntax?
78
- s
79
- rescue RangeError, RuntimeError
80
- # Handle all errors by limiting the changes to one
81
- # cop. The caller will parse the source again when the
82
- # file has been updated.
83
- autocorrect_one_cop(buffer, cops)
84
- end
65
+ new_source = autocorrect_one_cop(buffer, cops)
85
66
 
86
67
  return if new_source == buffer.source
87
68
 
@@ -90,13 +71,19 @@ module RuboCop
90
71
  @updated_source_file = true
91
72
  end
92
73
 
93
- # Does a slower but safer auto-correction run by correcting for just one
94
- # cop. The re-running of auto-corrections will make sure that the full
95
- # set of auto-corrections is tried again after this method has finished.
74
+ # Does an auto-correction run by correcting for just one cop. The
75
+ # re-running of auto-corrections will make sure that the full set of
76
+ # auto-corrections is tried again after this method has finished.
96
77
  def autocorrect_one_cop(buffer, cops)
97
- cop_with_corrections = cops.find { |cop| cop.corrections.any? }
98
- corrector = Corrector.new(buffer, cop_with_corrections.corrections)
99
- corrector.rewrite
78
+ cop_with_corrections = cops.find do |cop|
79
+ cop.relevant_file?(buffer.name) && cop.corrections.any?
80
+ end
81
+ if cop_with_corrections
82
+ corrector = Corrector.new(buffer, cop_with_corrections.corrections)
83
+ corrector.rewrite
84
+ else
85
+ buffer.source
86
+ end
100
87
  end
101
88
 
102
89
  def process_commissioner_errors(file, file_errors)
@@ -158,6 +158,11 @@ module RuboCop
158
158
  source_before_end =~ /\n\s*\Z/
159
159
  end
160
160
 
161
+ def within_node?(inner, outer)
162
+ o, i = outer.loc.expression, inner.loc.expression
163
+ i.begin_pos >= o.begin_pos && i.end_pos <= o.end_pos
164
+ end
165
+
161
166
  # Returns, for example, a bare `if` node if the given node is an `if`
162
167
  # with calls chained to the end of it.
163
168
  def first_part_of_call_chain(node)
@@ -39,7 +39,7 @@ module RuboCop
39
39
  # # resbody
40
40
  # end
41
41
  if branch_point_node.type == :rescue &&
42
- (branch_body_name == 'main' || other.branch_body_name == 'main')
42
+ (branch_body_name == 'main' || other.branch_body_name == 'main')
43
43
  return false
44
44
  end
45
45
 
@@ -78,18 +78,12 @@ module RuboCop
78
78
 
79
79
  def branch_body_name
80
80
  case branch_point_node.type
81
- when :if
82
- if_body_name
83
- when :case
84
- case_body_name
85
- when *LOGICAL_OPERATOR_TYPES
86
- logical_operator_body_name
87
- when RESCUE_TYPE
88
- rescue_body_name
89
- when ENSURE_TYPE
90
- ensure_body_name
91
- else
92
- fail InvalidBranchBodyError
81
+ when :if then if_body_name
82
+ when :case then case_body_name
83
+ when *LOGICAL_OPERATOR_TYPES then logical_operator_body_name
84
+ when RESCUE_TYPE then rescue_body_name
85
+ when ENSURE_TYPE then ensure_body_name
86
+ else fail InvalidBranchBodyError
93
87
  end
94
88
  rescue InvalidBranchBodyError
95
89
  raise InvalidBranchBodyError,
@@ -56,7 +56,7 @@ module RuboCop
56
56
  return unless default_cfg
57
57
 
58
58
  params = default_cfg.keys - %w(Description StyleGuide Enabled) -
59
- cfg.keys
59
+ cfg.keys
60
60
  return if params.empty?
61
61
 
62
62
  output.puts "# Configuration parameters: #{params.join(', ')}."
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'fileutils'
4
+
3
5
  module RuboCop
4
6
  module Formatter
5
7
  # This is a collection of formatters. A FormatterSet can hold multiple
@@ -37,7 +39,13 @@ module RuboCop
37
39
  builtin_formatter_class(formatter_type)
38
40
  end
39
41
 
40
- output = output_path ? File.open(output_path, 'w') : $stdout
42
+ if output_path
43
+ dir_path = File.dirname(output_path)
44
+ FileUtils.mkdir_p(dir_path) unless File.exist?(dir_path)
45
+ output = File.open(output_path, 'w')
46
+ else
47
+ output = $stdout
48
+ end
41
49
 
42
50
  self << formatter_class.new(output)
43
51
  end
@@ -1,89 +1,117 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'erb'
4
- require 'pathname'
4
+ require 'ostruct'
5
+ require 'base64'
6
+ require 'rubocop/formatter/text_util'
5
7
 
6
8
  module RuboCop
7
9
  module Formatter
8
10
  # This formatter saves the output as a html file.
9
11
  class HTMLFormatter < BaseFormatter
10
- include PathUtil
12
+ TEMPLATE_PATH =
13
+ File.expand_path('../../../../assets/output.html.erb', __FILE__)
11
14
 
12
- attr_reader :output_hash
15
+ attr_reader :files, :summary
13
16
 
14
17
  def initialize(output)
15
18
  super
16
- @output_hash = {
17
- metadata: metadata_hash,
18
- files: [],
19
- summary: { offense_count: 0 }
20
- }
19
+ @files = []
20
+ @summary = OpenStruct.new(offense_count: 0)
21
21
  end
22
22
 
23
23
  def started(target_files)
24
- output_hash[:summary][:target_file_count] = target_files.count
24
+ summary.target_files = target_files
25
25
  end
26
26
 
27
27
  def file_finished(file, offenses)
28
- output_hash[:files] << hash_for_file(file, offenses)
29
- output_hash[:summary][:offense_count] += offenses.count
28
+ files << OpenStruct.new(path: file, offenses: offenses)
29
+ summary.offense_count += offenses.count
30
30
  end
31
31
 
32
32
  def finished(inspected_files)
33
- output_hash[:summary][:inspected_file_count] = inspected_files.count
34
- template = File.read(File
35
- .expand_path('../../../../assets/output.html.erb', __FILE__))
36
- erb = ERB.new(template)
37
- html_content = erb.result(binding)
38
- output.write html_content
39
- end
33
+ summary.inspected_files = inspected_files
40
34
 
41
- def metadata_hash
42
- {
43
- rubocop_version: RuboCop::Version::STRING,
44
- ruby_engine: RUBY_ENGINE,
45
- ruby_version: RUBY_VERSION,
46
- ruby_patchlevel: RUBY_PATCHLEVEL.to_s,
47
- ruby_platform: RUBY_PLATFORM
48
- }
35
+ render_html
49
36
  end
50
37
 
51
- def hash_for_file(file, offenses)
52
- {
53
- path: relative_path(file),
54
- offenses: offenses.map { |o| hash_for_offense(o) }
55
- }
38
+ def render_html
39
+ context = ERBContext.new(files, summary)
40
+
41
+ template = File.read(TEMPLATE_PATH)
42
+ erb = ERB.new(template, nil, '-')
43
+ html = erb.result(context.binding)
44
+
45
+ output.write html
56
46
  end
57
47
 
58
- def hash_for_offense(offense)
59
- {
60
- severity: offense.severity.name,
61
- message: offense.message,
62
- cop_name: offense.cop_name,
63
- corrected: offense.corrected?,
64
- location: hash_for_location(offense)
65
- }
48
+ Color = Struct.new(:red, :green, :blue, :alpha) do
49
+ def to_s
50
+ "rgba(#{values.join(', ')})"
51
+ end
52
+
53
+ def fade_out(amount)
54
+ dup.tap do |color|
55
+ color.alpha -= amount
56
+ end
57
+ end
66
58
  end
67
59
 
68
- # TODO: Consider better solution for Offense#real_column.
69
- def hash_for_location(offense)
70
- {
71
- line: offense.line,
72
- column: offense.real_column,
73
- length: offense.location.length,
74
- source_line: offense.location.source_line,
75
- highlight: highlight_line(offense.location)
60
+ # This class privides helper methods used in the ERB template.
61
+ class ERBContext
62
+ include PathUtil, TextUtil
63
+
64
+ SEVERITY_COLORS = {
65
+ refactor: Color.new(0xED, 0x9C, 0x28, 1.0),
66
+ convention: Color.new(0xED, 0x9C, 0x28, 1.0),
67
+ warning: Color.new(0x96, 0x28, 0xEF, 1.0),
68
+ error: Color.new(0xD2, 0x32, 0x2D, 1.0),
69
+ fatal: Color.new(0xD2, 0x32, 0x2D, 1.0)
76
70
  }
77
- end
78
71
 
79
- def highlight_line(location)
80
- column_length = if location.begin.line == location.end.line
81
- location.column_range.count
82
- else
83
- location.source_line.length - location.column
84
- end
72
+ LOGO_IMAGE_PATH =
73
+ File.expand_path('../../../../assets/logo.png', __FILE__)
74
+
75
+ attr_reader :files, :summary
76
+
77
+ def initialize(files, summary)
78
+ @files = files.sort_by(&:path)
79
+ @summary = summary
80
+ end
81
+
82
+ # Make Kernel#binding public.
83
+ def binding
84
+ super
85
+ end
86
+
87
+ def decorated_message(offense)
88
+ offense.message.gsub(/`(.+?)`/) do
89
+ "<code>#{Regexp.last_match(1)}</code>"
90
+ end
91
+ end
92
+
93
+ def highlighted_source_line(offense)
94
+ location = offense.location
95
+
96
+ column_range = if location.begin.line == location.end.line
97
+ location.column_range
98
+ else
99
+ location.column...location.source_line.length
100
+ end
101
+
102
+ source_line = location.source_line
103
+
104
+ source_line[0...column_range.begin] +
105
+ "<span class=\"highlight #{offense.severity}\">" +
106
+ source_line[column_range] +
107
+ '</span>' +
108
+ source_line[column_range.end..-1]
109
+ end
85
110
 
86
- ' ' * location.column + '^' * column_length
111
+ def base64_encoded_logo_image
112
+ image = File.read(LOGO_IMAGE_PATH, binmode: true)
113
+ Base64.encode64(image)
114
+ end
87
115
  end
88
116
  end
89
117
  end